if (document.location.protocol == "http:"){prependURLProtocol='http://';}else{prependURLProtocol='https://';}
URL=top.location.host;
server_name=URL;
GB_ROOT_DIR = prependURLProtocol+server_name+"/jss/libs/GB/";

if (server_name=="dev.madcad.com"){
		secure_server_name		=	'dev.madcad.com';
		nonsecure_server_name	=	'dev.madcad.com';
}else if(server_name=="dev2.madcad.com"){
		secure_server_name		='dev2.madcad.com';
		nonsecure_server_name	='dev2.madcad.com';
}else if(server_name=="launch.madcad.com"){
		secure_server_name		='launch.madcad.com';
		nonsecure_server_name	='secure.madcad.com';
}else{
		secure_server_name		='secure.madcad.com';
		nonsecure_server_name	='www.madcad.com';
}

getUniqueId = function ()
{
     var dateObject = new Date();
     var uniqueId =
	 	  'cid.' +
	 	  server_name + '.' +
          dateObject.getFullYear() + '.' + 
          dateObject.getMonth() + '.' + 
          dateObject.getDate() + '.' + 
          dateObject.getTime();

     return uniqueId;
};
/*  Prototype JavaScript framework, version 1.6.0.2
 *  (c) 2005-2008 Sam Stephenson
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://www.prototypejs.org/
 *
 *--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.6.0.2',

  Browser: {
    IE:     !!(window.attachEvent && !window.opera),
    Opera:  !!window.opera,
    WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
    Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
    MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
  },

  BrowserFeatures: {
    XPath: !!document.evaluate,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div').__proto__ &&
      document.createElement('div').__proto__ !==
        document.createElement('form').__proto__
  },

  ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
  JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,

  emptyFunction: function() { },
  K: function(x) { return x }
};

if (Prototype.Browser.MobileSafari)
  Prototype.BrowserFeatures.SpecificElementExtensions = false;


/* Based on Alex Arnell's inheritance implementation. */
var Class = {
  create: function() {
    var parent = null, properties = $A(arguments);
    if (Object.isFunction(properties[0]))
      parent = properties.shift();

    function klass() {
      this.initialize.apply(this, arguments);
    }

    Object.extend(klass, Class.Methods);
    klass.superclass = parent;
    klass.subclasses = [];

    if (parent) {
      var subclass = function() { };
      subclass.prototype = parent.prototype;
      klass.prototype = new subclass;
      parent.subclasses.push(klass);
    }

    for (var i = 0; i < properties.length; i++)
      klass.addMethods(properties[i]);

    if (!klass.prototype.initialize)
      klass.prototype.initialize = Prototype.emptyFunction;

    klass.prototype.constructor = klass;

    return klass;
  }
};

Class.Methods = {
  addMethods: function(source) {
    var ancestor   = this.superclass && this.superclass.prototype;
    var properties = Object.keys(source);

    if (!Object.keys({ toString: true }).length)
      properties.push("toString", "valueOf");

    for (var i = 0, length = properties.length; i < length; i++) {
      var property = properties[i], value = source[property];
      if (ancestor && Object.isFunction(value) &&
          value.argumentNames().first() == "$super") {
        var method = value, value = Object.extend((function(m) {
          return function() { return ancestor[m].apply(this, arguments) };
        })(property).wrap(method), {
          valueOf:  function() { return method },
          toString: function() { return method.toString() }
        });
      }
      this.prototype[property] = value;
    }

    return this;
  }
};

var Abstract = { };

Object.extend = function(destination, source) {
  for (var property in source)
    destination[property] = source[property];
  return destination;
};

Object.extend(Object, {
  inspect: function(object) {
    try {
      if (Object.isUndefined(object)) return 'undefined';
      if (object === null) return 'null';
      return object.inspect ? object.inspect() : String(object);
    } catch (e) {
      if (e instanceof RangeError) return '...';
      throw e;
    }
  },

  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (Object.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = Object.toJSON(object[property]);
      if (!Object.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : String.interpret(object);
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return Object.extend({ }, object);
  },

  isElement: function(object) {
    return object && object.nodeType == 1;
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isHash: function(object) {
    return object instanceof Hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
});

Object.extend(Function.prototype, {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
    var __method = this, args = $A(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat($A(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = $A(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = $A(arguments);
    return function() {
      return __method.apply(this, args.concat($A(arguments)));
    }
  },

  delay: function() {
    var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    return this._methodized = function() {
      return __method.apply(null, [this].concat($A(arguments)));
    };
  }
});

Function.prototype.defer = Function.prototype.delay.curry(0.01);

Date.prototype.toJSON = function() {
  return '"' + this.getUTCFullYear() + '-' +
    (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
    this.getUTCDate().toPaddedString(2) + 'T' +
    this.getUTCHours().toPaddedString(2) + ':' +
    this.getUTCMinutes().toPaddedString(2) + ':' +
    this.getUTCSeconds().toPaddedString(2) + 'Z"';
};

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0, length = arguments.length; i < length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) { }
    }

    return returnValue;
  }
};

RegExp.prototype.match = RegExp.prototype.test;

RegExp.escape = function(str) {
  return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create({
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  execute: function() {
    this.callback(this);
  },

  stop: function() {
    if (!this.timer) return;
    clearInterval(this.timer);
    this.timer = null;
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.execute();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
});
Object.extend(String, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

Object.extend(String.prototype, {
  gsub: function(pattern, replacement) {
    var result = '', source = this, match;
    replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
      if (match = source.match(pattern)) {
        result += source.slice(0, match.index);
        result += String.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source, source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    replacement = this.gsub.prepareReplacement(replacement);
    count = Object.isUndefined(count) ? 1 : count;

    return this.gsub(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.gsub(pattern, iterator);
    return String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = Object.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },

  escapeHTML: function() {
    var self = arguments.callee;
    self.text.data = this;
    return self.div.innerHTML;
  },

  unescapeHTML: function() {
    var div = new Element('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? (div.childNodes.length > 1 ?
      $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
      div.childNodes[0].nodeValue) : '';
  },

  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var value = pair.length > 1 ? pair.join('=') : pair[0];
        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.gsub(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || Prototype.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
});

if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  }
});

String.prototype.gsub.prepareReplacement = function(replacement) {
  if (Object.isFunction(replacement)) return replacement;
  var template = new Template(replacement);
  return function(match) { return template.evaluate(match) };
};

String.prototype.parseQuery = String.prototype.toQueryParams;

Object.extend(String.prototype.escapeHTML, {
  div:  document.createElement('div'),
  text: document.createTextNode('')
});

with (String.prototype.escapeHTML) div.appendChild(text);

var Template = Class.create({
  initialize: function(template, pattern) {
    this.template = template.toString();
    this.pattern = pattern || Template.Pattern;
  },

  evaluate: function(object) {
    if (Object.isFunction(object.toTemplateReplacements))
      object = object.toTemplateReplacements();

    return this.template.gsub(this.pattern, function(match) {
      if (object == null) return '';

      var before = match[1] || '';
      if (before == '\\') return match[2];

      var ctx = object, expr = match[3];
      var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
      match = pattern.exec(expr);
      if (match == null) return before;

      while (match != null) {
        var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
        ctx = ctx[comp];
        if (null == ctx || '' == match[3]) break;
        expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
        match = pattern.exec(expr);
      }

      return before + String.interpret(ctx);
    });
  }
});
Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;

var $break = { };

var Enumerable = {
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != $break) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result = false;
    this.each(function(value, index) {
      if (result = !!iterator(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    iterator = iterator.bind(context);
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var results = [];

    if (Object.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator(value, index));
    });
    return results;
  },

  include: function(object) {
    if (Object.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = Object.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : Prototype.K;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.map();
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (Object.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  filter:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray,
  every:   Enumerable.all,
  some:    Enumerable.any
});
function $A(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

if (Prototype.Browser.WebKit) {
  $A = function(iterable) {
    if (!iterable) return [];
    if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
        iterable.toArray) return iterable.toArray();
    var length = iterable.length || 0, results = new Array(length);
    while (length--) results[length] = iterable[length];
    return results;
  };
}

Array.from = $A;

Object.extend(Array.prototype, Enumerable);

if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(Object.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = Object.toJSON(object);
      if (!Object.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
});

// use native browser JS 1.6 implementation if available
if (Object.isFunction(Array.prototype.forEach))
  Array.prototype._each = Array.prototype.forEach;

if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
};

if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
  i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
  var n = this.slice(0, i).reverse().indexOf(item);
  return (n < 0) ? n : i - n - 1;
};

Array.prototype.toArray = Array.prototype.clone;

function $w(string) {
  if (!Object.isString(string)) return [];
  string = string.strip();
  return string ? string.split(/\s+/) : [];
}

if (Prototype.Browser.Opera){
  Array.prototype.concat = function() {
    var array = [];
    for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
    for (var i = 0, length = arguments.length; i < length; i++) {
      if (Object.isArray(arguments[i])) {
        for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
          array.push(arguments[i][j]);
      } else {
        array.push(arguments[i]);
      }
    }
    return array;
  };
}
Object.extend(Number.prototype, {
  toColorPart: function() {
    return this.toPaddedString(2, 16);
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  },

  toPaddedString: function(length, radix) {
    var string = this.toString(radix || 10);
    return '0'.times(length - string.length) + string;
  },

  toJSON: function() {
    return isFinite(this) ? this.toString() : 'null';
  }
});

$w('abs round ceil floor').each(function(method){
  Number.prototype[method] = Math[method].methodize();
});
function $H(object) {
  return new Hash(object);
};

var Hash = Class.create(Enumerable, (function() {

  function toQueryPair(key, value) {
    if (Object.isUndefined(value)) return key;
    return key + '=' + encodeURIComponent(String.interpret(value));
  }

  return {
    initialize: function(object) {
      this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
    },

    _each: function(iterator) {
      for (var key in this._object) {
        var value = this._object[key], pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    },

    set: function(key, value) {
      return this._object[key] = value;
    },

    get: function(key) {
      return this._object[key];
    },

    unset: function(key) {
      var value = this._object[key];
      delete this._object[key];
      return value;
    },

    toObject: function() {
      return Object.clone(this._object);
    },

    keys: function() {
      return this.pluck('key');
    },

    values: function() {
      return this.pluck('value');
    },

    index: function(value) {
      var match = this.detect(function(pair) {
        return pair.value === value;
      });
      return match && match.key;
    },

    merge: function(object) {
      return this.clone().update(object);
    },

    update: function(object) {
      return new Hash(object).inject(this, function(result, pair) {
        result.set(pair.key, pair.value);
        return result;
      });
    },

    toQueryString: function() {
      return this.map(function(pair) {
        var key = encodeURIComponent(pair.key), values = pair.value;

        if (values && typeof values == 'object') {
          if (Object.isArray(values))
            return values.map(toQueryPair.curry(key)).join('&');
        }
        return toQueryPair(key, values);
      }).join('&');
    },

    inspect: function() {
      return '#<Hash:{' + this.map(function(pair) {
        return pair.map(Object.inspect).join(': ');
      }).join(', ') + '}>';
    },

    toJSON: function() {
      return Object.toJSON(this.toObject());
    },

    clone: function() {
      return new Hash(this);
    }
  }
})());

Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
Hash.from = $H;
var ObjectRange = Class.create(Enumerable, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    while (this.include(value)) {
      iterator(value);
      value = value.succ();
    }
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
};

var Ajax = {
  getTransport: function() {
    return Try.these(
      function() {return new XMLHttpRequest()},
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
    ) || false;
  },

  activeRequestCount: 0
};

Ajax.Responders = {
  responders: [],

  _each: function(iterator) {
    this.responders._each(iterator);
  },

  register: function(responder) {
    if (!this.include(responder))
      this.responders.push(responder);
  },

  unregister: function(responder) {
    this.responders = this.responders.without(responder);
  },

  dispatch: function(callback, request, transport, json) {
    this.each(function(responder) {
      if (Object.isFunction(responder[callback])) {
        try {
          responder[callback].apply(responder, [request, transport, json]);
        } catch (e) { }
      }
    });
  }
};

Object.extend(Ajax.Responders, Enumerable);

Ajax.Responders.register({
  onCreate:   function() { Ajax.activeRequestCount++ },
  onComplete: function() { Ajax.activeRequestCount-- }
});

Ajax.Base = Class.create({
  initialize: function(options) {
    this.options = {
      method:       'post',
      asynchronous: true,
      contentType:  'application/x-www-form-urlencoded',
      encoding:     'UTF-8',
      parameters:   '',
      evalJSON:     true,
      evalJS:       true
    };
    Object.extend(this.options, options || { });

    this.options.method = this.options.method.toLowerCase();

    if (Object.isString(this.options.parameters))
      this.options.parameters = this.options.parameters.toQueryParams();
    else if (Object.isHash(this.options.parameters))
      this.options.parameters = this.options.parameters.toObject();
  }
});

Ajax.Request = Class.create(Ajax.Base, {
  _complete: false,

  initialize: function($super, url, options) {
    $super(options);
    this.transport = Ajax.getTransport();
    this.request(url);
  },

  request: function(url) {
    this.url = url;
    this.method = this.options.method;
    var params = Object.clone(this.options.parameters);

    if (!['get', 'post'].include(this.method)) {
      // simulate other verbs over post
      params['_method'] = this.method;
      this.method = 'post';
    }

    this.parameters = params;

    if (params = Object.toQueryString(params)) {
      // when GET, append parameters to URL
      if (this.method == 'get')
        this.url += (this.url.include('?') ? '&' : '?') + params;
      else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
        params += '&_=';
    }

    try {
      var response = new Ajax.Response(this);
      if (this.options.onCreate) this.options.onCreate(response);
      Ajax.Responders.dispatch('onCreate', this, response);

      this.transport.open(this.method.toUpperCase(), this.url,
        this.options.asynchronous);

      if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);

      this.transport.onreadystatechange = this.onStateChange.bind(this);
      this.setRequestHeaders();

      this.body = this.method == 'post' ? (this.options.postBody || params) : null;
      this.transport.send(this.body);

      /* Force Firefox to handle ready state 4 for synchronous requests */
      if (!this.options.asynchronous && this.transport.overrideMimeType)
        this.onStateChange();

    }
    catch (e) {
      this.dispatchException(e);
    }
  },

  onStateChange: function() {
    var readyState = this.transport.readyState;
    if (readyState > 1 && !((readyState == 4) && this._complete))
      this.respondToReadyState(this.transport.readyState);
  },

  setRequestHeaders: function() {
    var headers = {
      'X-Requested-With': 'XMLHttpRequest',
      'X-Prototype-Version': Prototype.Version,
      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
    };

    if (this.method == 'post') {
      headers['Content-type'] = this.options.contentType +
        (this.options.encoding ? '; charset=' + this.options.encoding : '');

      /* Force "Connection: close" for older Mozilla browsers to work
       * around a bug where XMLHttpRequest sends an incorrect
       * Content-length header. See Mozilla Bugzilla #246651.
       */
      if (this.transport.overrideMimeType &&
          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
            headers['Connection'] = 'close';
    }

    // user-defined headers
    if (typeof this.options.requestHeaders == 'object') {
      var extras = this.options.requestHeaders;

      if (Object.isFunction(extras.push))
        for (var i = 0, length = extras.length; i < length; i += 2)
          headers[extras[i]] = extras[i+1];
      else
        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
    }

    for (var name in headers)
      this.transport.setRequestHeader(name, headers[name]);
  },

  success: function() {
    var status = this.getStatus();
    return !status || (status >= 200 && status < 300);
  },

  getStatus: function() {
    try {
      return this.transport.status || 0;
    } catch (e) { return 0 }
  },

  respondToReadyState: function(readyState) {
    var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);

    if (state == 'Complete') {
      try {
        this._complete = true;
        (this.options['on' + response.status]
         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
         || Prototype.emptyFunction)(response, response.headerJSON);
      } catch (e) {
        this.dispatchException(e);
      }

      var contentType = response.getHeader('Content-type');
      if (this.options.evalJS == 'force'
          || (this.options.evalJS && this.isSameOrigin() && contentType
          && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
        this.evalResponse();
    }

    try {
      (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
      Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
    } catch (e) {
      this.dispatchException(e);
    }

    if (state == 'Complete') {
      // avoid memory leak in MSIE: clean up
      this.transport.onreadystatechange = Prototype.emptyFunction;
    }
  },

  isSameOrigin: function() {
    var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
    return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
      protocol: location.protocol,
      domain: document.domain,
      port: location.port ? ':' + location.port : ''
    }));
  },

  getHeader: function(name) {
    try {
      return this.transport.getResponseHeader(name) || null;
    } catch (e) { return null }
  },

  evalResponse: function() {
    try {
      return eval((this.transport.responseText || '').unfilterJSON());
    } catch (e) {
      this.dispatchException(e);
    }
  },

  dispatchException: function(exception) {
    (this.options.onException || Prototype.emptyFunction)(this, exception);
    Ajax.Responders.dispatch('onException', this, exception);
  }
});

Ajax.Request.Events =
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];

Ajax.Response = Class.create({
  initialize: function(request){
    this.request = request;
    var transport  = this.transport  = request.transport,
        readyState = this.readyState = transport.readyState;

    if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
      this.status       = this.getStatus();
      this.statusText   = this.getStatusText();
      this.responseText = String.interpret(transport.responseText);
      this.headerJSON   = this._getHeaderJSON();
    }

    if(readyState == 4) {
      var xml = transport.responseXML;
      this.responseXML  = Object.isUndefined(xml) ? null : xml;
      this.responseJSON = this._getResponseJSON();
    }
  },

  status:      0,
  statusText: '',

  getStatus: Ajax.Request.prototype.getStatus,

  getStatusText: function() {
    try {
      return this.transport.statusText || '';
    } catch (e) { return '' }
  },

  getHeader: Ajax.Request.prototype.getHeader,

  getAllHeaders: function() {
    try {
      return this.getAllResponseHeaders();
    } catch (e) { return null }
  },

  getResponseHeader: function(name) {
    return this.transport.getResponseHeader(name);
  },

  getAllResponseHeaders: function() {
    return this.transport.getAllResponseHeaders();
  },

  _getHeaderJSON: function() {
    var json = this.getHeader('X-JSON');
    if (!json) return null;
    json = decodeURIComponent(escape(json));
    try {
      return json.evalJSON(this.request.options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  },

  _getResponseJSON: function() {
    var options = this.request.options;
    if (!options.evalJSON || (options.evalJSON != 'force' &&
      !(this.getHeader('Content-type') || '').include('application/json')) ||
        this.responseText.blank())
          return null;
    try {
      return this.responseText.evalJSON(options.sanitizeJSON ||
        !this.request.isSameOrigin());
    } catch (e) {
      this.request.dispatchException(e);
    }
  }
});

Ajax.Updater = Class.create(Ajax.Request, {
  initialize: function($super, container, url, options) {
    this.container = {
      success: (container.success || container),
      failure: (container.failure || (container.success ? null : container))
    };

    options = Object.clone(options);
    var onComplete = options.onComplete;
    options.onComplete = (function(response, json) {
      this.updateContent(response.responseText);
      if (Object.isFunction(onComplete)) onComplete(response, json);
    }).bind(this);

    $super(url, options);
  },

  updateContent: function(responseText) {
    var receiver = this.container[this.success() ? 'success' : 'failure'],
        options = this.options;

    if (!options.evalScripts) responseText = responseText.stripScripts();

    if (receiver = $(receiver)) {
      if (options.insertion) {
        if (Object.isString(options.insertion)) {
          var insertion = { }; insertion[options.insertion] = responseText;
          receiver.insert(insertion);
        }
        else options.insertion(receiver, responseText);
      }
      else receiver.update(responseText);
    }
  }
});

Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
  initialize: function($super, container, url, options) {
    $super(options);
    this.onComplete = this.options.onComplete;

    this.frequency = (this.options.frequency || 2);
    this.decay = (this.options.decay || 1);

    this.updater = { };
    this.container = container;
    this.url = url;

    this.start();
  },

  start: function() {
    this.options.onComplete = this.updateComplete.bind(this);
    this.onTimerEvent();
  },

  stop: function() {
    this.updater.options.onComplete = undefined;
    clearTimeout(this.timer);
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  },

  updateComplete: function(response) {
    if (this.options.decay) {
      this.decay = (response.responseText == this.lastText ?
        this.decay * this.options.decay : 1);

      this.lastText = response.responseText;
    }
    this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
  },

  onTimerEvent: function() {
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  }
});
function $(element) {
  if (arguments.length > 1) {
    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
      elements.push($(arguments[i]));
    return elements;
  }
  if (Object.isString(element))
    element = document.getElementById(element);
  return Element.extend(element);
}

if (Prototype.BrowserFeatures.XPath) {
  document._getElementsByXPath = function(expression, parentElement) {
    var results = [];
    var query = document.evaluate(expression, $(parentElement) || document,
      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    for (var i = 0, length = query.snapshotLength; i < length; i++)
      results.push(Element.extend(query.snapshotItem(i)));
    return results;
  };
}

/*--------------------------------------------------------------------------*/

if (!window.Node) var Node = { };

if (!Node.ELEMENT_NODE) {
  // DOM level 2 ECMAScript Language Binding
  Object.extend(Node, {
    ELEMENT_NODE: 1,
    ATTRIBUTE_NODE: 2,
    TEXT_NODE: 3,
    CDATA_SECTION_NODE: 4,
    ENTITY_REFERENCE_NODE: 5,
    ENTITY_NODE: 6,
    PROCESSING_INSTRUCTION_NODE: 7,
    COMMENT_NODE: 8,
    DOCUMENT_NODE: 9,
    DOCUMENT_TYPE_NODE: 10,
    DOCUMENT_FRAGMENT_NODE: 11,
    NOTATION_NODE: 12
  });
}

(function() {
  var element = this.Element;
  this.Element = function(tagName, attributes) {
    attributes = attributes || { };
    tagName = tagName.toLowerCase();
    var cache = Element.cache;
    if (Prototype.Browser.IE && attributes.name) {
      tagName = '<' + tagName + ' name="' + attributes.name + '">';
      delete attributes.name;
      return Element.writeAttribute(document.createElement(tagName), attributes);
    }
    if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
  };
  Object.extend(this.Element, element || { });
}).call(window);

Element.cache = { };

Element.Methods = {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function(element) {
    element = $(element);
    Element[Element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    $(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    $(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);
    content = Object.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    element = $(element);
    if (content && content.toElement) content = content.toElement();
    else if (!Object.isElement(content)) {
      content = Object.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = $(element);

    if (Object.isString(insertions) || Object.isNumber(insertions) ||
        Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = Element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (Object.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = Object.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = $(element);
    if (Object.isElement(wrapper))
      $(wrapper).writeAttribute(attributes || { });
    else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
    else wrapper = new Element('div', wrapper);
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  inspect: function(element) {
    element = $(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = $(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(Element.extend(element));
    return elements;
  },

  ancestors: function(element) {
    return $(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return $(element).select("*");
  },

  firstDescendant: function(element) {
    element = $(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return $(element);
  },

  immediateDescendants: function(element) {
    if (!(element = $(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat($(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return $(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return $(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = $(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (Object.isString(selector))
      selector = new Selector(selector);
    return selector.match($(element));
  },

  up: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(element.parentNode);
    var ancestors = element.ancestors();
    return Object.isNumber(expression) ? ancestors[expression] :
      Selector.findElement(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return element.firstDescendant();
    return Object.isNumber(expression) ? element.descendants()[expression] :
      element.select(expression)[index || 0];
  },

  previous: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return Object.isNumber(expression) ? previousSiblings[expression] :
      Selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = $(element);
    if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return Object.isNumber(expression) ? nextSiblings[expression] :
      Selector.findElement(nextSiblings, expression, index);
  },

  select: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = $A(arguments), element = $(args.shift());
    return Selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = $(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while ($(id));
    element.writeAttribute('id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = $(element);
    if (Prototype.Browser.IE) {
      var t = Element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = $(element);
    var attributes = { }, t = Element._attributeTranslations.write;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = Object.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t.names[attr] || attr;
      value = attributes[attr];
      if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return $(element).getDimensions().height;
  },

  getWidth: function(element) {
    return $(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    if (!element.hasClassName(className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = $(element))) return;
    return element[element.hasClassName(className) ?
      'removeClassName' : 'addClassName'](className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return $(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = $(element), ancestor = $(ancestor);
    var originalAncestor = ancestor;

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (element.sourceIndex && !Prototype.Browser.Opera) {
      var e = element.sourceIndex, a = ancestor.sourceIndex,
       nextAncestor = ancestor.nextSibling;
      if (!nextAncestor) {
        do { ancestor = ancestor.parentNode; }
        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
      }
      if (nextAncestor && nextAncestor.sourceIndex)
       return (e > a && e < nextAncestor.sourceIndex);
    }

    while (element = element.parentNode)
      if (element == originalAncestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = $(element);
    var pos = element.cumulativeOffset();
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = $(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value) {
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return $(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = $(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  getDimensions: function(element) {
    element = $(element);
    var display = $(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return element;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = $(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'absolute') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
    return element;
  },

  relativize: function(element) {
    element = $(element);
    if (element.getStyle('position') == 'relative') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: function(element) {
    if (element.offsetParent) return $(element.offsetParent);
    if (element == document.body) return $(element);

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return $(element);

    return $(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return Element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = $(source);
    var p = source.viewportOffset();

    // find coordinate system to use
    element = $(element);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  }
};

Element.Methods.identify.counter = 1;

Object.extend(Element.Methods, {
  getElementsBySelector: Element.Methods.select,
  childElements: Element.Methods.immediateDescendants
});

Element._attributeTranslations = {
  write: {
    names: {
      className: 'class',
      htmlFor:   'for'
    },
    values: { }
  }
};

if (Prototype.Browser.Opera) {
  Element.Methods.getStyle = Element.Methods.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'left': case 'top': case 'right': case 'bottom':
          if (proceed(element, 'position') === 'static') return null;
        case 'height': case 'width':
          // returns '0px' for hidden elements; we want it to return null
          if (!Element.visible(element)) return null;

          // returns the border-box dimensions rather than the content-box
          // dimensions, so we subtract padding and borders from the value
          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );

  Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (Prototype.Browser.IE) {
  // IE doesn't report offsets correctly for static elements, so we change them
  // to "relative" to get the values, then change them back.
  Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
    function(proceed, element) {
      element = $(element);
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  $w('positionedOffset viewportOffset').each(function(method) {
    Element.Methods[method] = Element.Methods[method].wrap(
      function(proceed, element) {
        element = $(element);
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);
        // Trigger hasLayout on the offset parent so that IE6 reports
        // accurate offsetTop and offsetLeft values for position: fixed.
        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  Element.Methods.getStyle = function(element, style) {
    element = $(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  Element.Methods.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = $(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };

  Element._attributeTranslations = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return $(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  Element._attributeTranslations.write = {
    names: Object.extend({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, Element._attributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  Element._attributeTranslations.has = {};

  $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc').each(function(attr) {
    Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
    Element._attributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    Object.extend(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(Element._attributeTranslations.read.values);
}

else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (Prototype.Browser.WebKit) {
  Element.Methods.setOpacity = function(element, value) {
    element = $(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if(element.tagName == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
  // KHTML/WebKit only.
  Element.Methods.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return Element._returnOffset(valueL, valueT);
  };
}

if (Prototype.Browser.IE || Prototype.Browser.Opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  Element.Methods.update = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) return element.update().insert(content);

    content = Object.toHTML(content);
    var tagName = element.tagName.toUpperCase();

    if (tagName in Element._insertionTranslations.tags) {
      $A(element.childNodes).each(function(node) { element.removeChild(node) });
      Element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

if ('outerHTML' in document.createElement('div')) {
  Element.Methods.replace = function(element, content) {
    element = $(element);

    if (content && content.toElement) content = content.toElement();
    if (Object.isElement(content)) {
      element.parentNode.replaceChild(content, element);
      return element;
    }

    content = Object.toHTML(content);
    var parent = element.parentNode, tagName = parent.tagName.toUpperCase();

    if (Element._insertionTranslations.tags[tagName]) {
      var nextSibling = element.next();
      var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
      parent.removeChild(element);
      if (nextSibling)
        fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
      else
        fragments.each(function(node) { parent.appendChild(node) });
    }
    else element.outerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}

Element._returnOffset = function(l, t) {
  var result = [l, t];
  result.left = l;
  result.top = t;
  return result;
};

Element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return $A(div.childNodes);
};

Element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  Object.extend(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(Element._insertionTranslations);

Element.Methods.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = Element._attributeTranslations.has[attribute] || attribute;
    var node = $(element).getAttributeNode(attribute);
    return node && node.specified;
  }
};

Element.Methods.ByTag = { };

Object.extend(Element, Element.Methods);

if (!Prototype.BrowserFeatures.ElementExtensions &&
    document.createElement('div').__proto__) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div').__proto__;
  Prototype.BrowserFeatures.ElementExtensions = true;
}

Element.extend = (function() {
  if (Prototype.BrowserFeatures.SpecificElementExtensions)
    return Prototype.K;

  var Methods = { }, ByTag = Element.Methods.ByTag;

  var extend = Object.extend(function(element) {
    if (!element || element._extendedByPrototype ||
        element.nodeType != 1 || element == window) return element;

    var methods = Object.clone(Methods),
      tagName = element.tagName, property, value;

    // extend methods for specific tags
    if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);

    for (property in methods) {
      value = methods[property];
      if (Object.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = Prototype.emptyFunction;
    return element;

  }, {
    refresh: function() {
      // extend methods for all tags (Safari doesn't need this)
      if (!Prototype.BrowserFeatures.ElementExtensions) {
        Object.extend(Methods, Element.Methods);
        Object.extend(Methods, Element.Methods.Simulated);
      }
    }
  });

  extend.refresh();
  return extend;
})();

Element.hasAttribute = function(element, attribute) {
  if (element.hasAttribute) return element.hasAttribute(attribute);
  return Element.Methods.Simulated.hasAttribute(element, attribute);
};

Element.addMethods = function(methods) {
  var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;

  if (!methods) {
    Object.extend(Form, Form.Methods);
    Object.extend(Form.Element, Form.Element.Methods);
    Object.extend(Element.Methods.ByTag, {
      "FORM":     Object.clone(Form.Methods),
      "INPUT":    Object.clone(Form.Element.Methods),
      "SELECT":   Object.clone(Form.Element.Methods),
      "TEXTAREA": Object.clone(Form.Element.Methods)
    });
  }

  if (arguments.length == 2) {
    var tagName = methods;
    methods = arguments[1];
  }

  if (!tagName) Object.extend(Element.Methods, methods || { });
  else {
    if (Object.isArray(tagName)) tagName.each(extend);
    else extend(tagName);
  }

  function extend(tagName) {
    tagName = tagName.toUpperCase();
    if (!Element.Methods.ByTag[tagName])
      Element.Methods.ByTag[tagName] = { };
    Object.extend(Element.Methods.ByTag[tagName], methods);
  }

  function copy(methods, destination, onlyIfAbsent) {
    onlyIfAbsent = onlyIfAbsent || false;
    for (var property in methods) {
      var value = methods[property];
      if (!Object.isFunction(value)) continue;
      if (!onlyIfAbsent || !(property in destination))
        destination[property] = value.methodize();
    }
  }

  function findDOMClass(tagName) {
    var klass;
    var trans = {
      "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
      "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
      "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
      "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
      "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
      "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
      "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
      "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
      "FrameSet", "IFRAME": "IFrame"
    };
    if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName + 'Element';
    if (window[klass]) return window[klass];
    klass = 'HTML' + tagName.capitalize() + 'Element';
    if (window[klass]) return window[klass];

    window[klass] = { };
    window[klass].prototype = document.createElement(tagName).__proto__;
    return window[klass];
  }

  if (F.ElementExtensions) {
    copy(Element.Methods, HTMLElement.prototype);
    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  }

  if (F.SpecificElementExtensions) {
    for (var tag in Element.Methods.ByTag) {
      var klass = findDOMClass(tag);
      if (Object.isUndefined(klass)) continue;
      copy(T[tag], klass.prototype);
    }
  }

  Object.extend(Element, Element.Methods);
  delete Element.ByTag;

  if (Element.extend.refresh) Element.extend.refresh();
  Element.cache = { };
};

document.viewport = {
  getDimensions: function() {
    var dimensions = { };
    var B = Prototype.Browser;
    $w('width height').each(function(d) {
      var D = d.capitalize();
      dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
        (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
    });
    return dimensions;
  },

  getWidth: function() {
    return this.getDimensions().width;
  },

  getHeight: function() {
    return this.getDimensions().height;
  },

  getScrollOffsets: function() {
    return Element._returnOffset(
      window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
      window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
  }
};
/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
 * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
 * license.  Please see http://www.yui-ext.com/ for more information. */

var Selector = Class.create({
  initialize: function(expression) {
    this.expression = expression.strip();
    this.compileMatcher();
  },

  shouldUseXPath: function() {
    if (!Prototype.BrowserFeatures.XPath) return false;

    var e = this.expression;

    // Safari 3 chokes on :*-of-type and :empty
    if (Prototype.Browser.WebKit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;

    // XPath can't do namespaced attributes, nor can it read
    // the "checked" property from DOM nodes
    if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
      return false;

    return true;
  },

  compileMatcher: function() {
    if (this.shouldUseXPath())
      return this.compileXPathMatcher();

    var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
        c = Selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      this.matcher = Selector._cache[e];
      return;
    }

    this.matcher = ["this.matcher = function(root) {",
                    "var r = root, h = Selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
    	      new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.matcher.push("return h.unique(n);\n}");
    eval(this.matcher.join('\n'));
    Selector._cache[this.expression] = this.matcher;
  },

  compileXPathMatcher: function() {
    var e = this.expression, ps = Selector.patterns,
        x = Selector.xpath, le, m;

    if (Selector._cache[e]) {
      this.xpath = Selector._cache[e]; return;
    }

    this.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    this.xpath = this.matcher.join('');
    Selector._cache[this.expression] = this.xpath;
  },

  findElements: function(root) {
    root = root || document;
    if (this.xpath) return document._getElementsByXPath(this.xpath, root);
    return this.matcher(root);
  },

  match: function(element) {
    this.tokens = [];

    var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the Selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            this.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return this.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = this.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString: function() {
    return this.expression;
  },

  inspect: function() {
    return "#<Selector:" + this.expression.inspect() + ">";
  }
});

Object.extend(Selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = Selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
      'checked':     "[@checked]",
      'disabled':    "[@disabled]",
      'enabled':     "[not(@disabled)]",
      'not': function(m) {
        var e = m[6], p = Selector.patterns,
            x = Selector.xpath, le, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return Selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

    // selectors follow
    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
    attrPresence: /^\[([\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },

  // for Selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {
    // UTILITY FUNCTIONS
    // joins two collections
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    // marks an array of nodes for counting
    mark: function(nodes) {
      var _true = Prototype.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = Prototype.emptyFunction;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = Prototype.emptyFunction;
          results.push(Element.extend(n));
        }
      return Selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
	      if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = Selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = $(id), h = Selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return Selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = Selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = Selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return Selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = Selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return Selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = Selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = Selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = Selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = Selector.handlers, selectorType, m;
      var exclusions = new Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled) results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv.startsWith(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = $$(expression), h = Selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (Object.isNumber(expression)) {
      index = expression; expression = false;
    }
    return Selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = Selector.split(expressions.join(','));
    var results = [], h = Selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
});

if (Prototype.Browser.IE) {
  Object.extend(Selector.handlers, {
    // IE returns comment nodes on getElementsByTagName("*").
    // Filter them out.
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        if (node.tagName !== "!") a.push(node);
      return a;
    },

    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node.removeAttribute('_countedByPrototype');
      return nodes;
    }
  });
}

function $$() {
  return Selector.findChildElements(document, $A(arguments));
}
var Form = {
  reset: function(form) {
    $(form).reset();
    return form;
  },

  serializeElements: function(elements, options) {
    if (typeof options != 'object') options = { hash: !!options };
    else if (Object.isUndefined(options.hash)) options.hash = true;
    var key, value, submitted = false, submit = options.submit;

    var data = elements.inject({ }, function(result, element) {
      if (!element.disabled && element.name) {
        key = element.name; value = $(element).getValue();
        if (value != null && (element.type != 'submit' || (!submitted &&
            submit !== false && (!submit || key == submit) && (submitted = true)))) {
          if (key in result) {
            // a key is already present; construct an array of values
            if (!Object.isArray(result[key])) result[key] = [result[key]];
            result[key].push(value);
          }
          else result[key] = value;
        }
      }
      return result;
    });

    return options.hash ? data : Object.toQueryString(data);
  }
};

Form.Methods = {
  serialize: function(form, options) {
    return Form.serializeElements(Form.getElements(form), options);
  },

  getElements: function(form) {
    return $A($(form).getElementsByTagName('*')).inject([],
      function(elements, child) {
        if (Form.Element.Serializers[child.tagName.toLowerCase()])
          elements.push(Element.extend(child));
        return elements;
      }
    );
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name) return $A(inputs).map(Element.extend);

    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) || (name && input.name != name))
        continue;
      matchingInputs.push(Element.extend(input));
    }

    return matchingInputs;
  },

  disable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('disable');
    return form;
  },

  enable: function(form) {
    form = $(form);
    Form.getElements(form).invoke('enable');
    return form;
  },

  findFirstElement: function(form) {
    var elements = $(form).getElements().findAll(function(element) {
      return 'hidden' != element.type && !element.disabled;
    });
    var firstByIndex = elements.findAll(function(element) {
      return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
    }).sortBy(function(element) { return element.tabIndex }).first();

    return firstByIndex ? firstByIndex : elements.find(function(element) {
      return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    form = $(form);
    form.findFirstElement().activate();
    return form;
  },

  request: function(form, options) {
    form = $(form), options = Object.clone(options || { });

    var params = options.parameters, action = form.readAttribute('action') || '';
    if (action.blank()) action = window.location.href;
    options.parameters = form.serialize(true);

    if (params) {
      if (Object.isString(params)) params = params.toQueryParams();
      Object.extend(options.parameters, params);
    }

    if (form.hasAttribute('method') && !options.method)
      options.method = form.method;

    return new Ajax.Request(action, options);
  }
};

/*--------------------------------------------------------------------------*/

Form.Element = {
  focus: function(element) {
    $(element).focus();
    return element;
  },

  select: function(element) {
    $(element).select();
    return element;
  }
};

Form.Element.Methods = {
  serialize: function(element) {
    element = $(element);
    if (!element.disabled && element.name) {
      var value = element.getValue();
      if (value != undefined) {
        var pair = { };
        pair[element.name] = value;
        return Object.toQueryString(pair);
      }
    }
    return '';
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    return Form.Element.Serializers[method](element);
  },

  setValue: function(element, value) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    Form.Element.Serializers[method](element, value);
    return element;
  },

  clear: function(element) {
    $(element).value = '';
    return element;
  },

  present: function(element) {
    return $(element).value != '';
  },

  activate: function(element) {
    element = $(element);
    try {
      element.focus();
      if (element.select && (element.tagName.toLowerCase() != 'input' ||
          !['button', 'reset', 'submit'].include(element.type)))
        element.select();
    } catch (e) { }
    return element;
  },

  disable: function(element) {
    element = $(element);
    element.blur();
    element.disabled = true;
    return element;
  },

  enable: function(element) {
    element = $(element);
    element.disabled = false;
    return element;
  }
};

/*--------------------------------------------------------------------------*/

var Field = Form.Element;
var $F = Form.Element.Methods.getValue;

/*--------------------------------------------------------------------------*/

Form.Element.Serializers = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element, value);
      default:
        return Form.Element.Serializers.textarea(element, value);
    }
  },

  inputSelector: function(element, value) {
    if (Object.isUndefined(value)) return element.checked ? element.value : null;
    else element.checked = !!value;
  },

  textarea: function(element, value) {
    if (Object.isUndefined(value)) return element.value;
    else element.value = value;
  },

  select: function(element, index) {
    if (Object.isUndefined(index))
      return this[element.type == 'select-one' ?
        'selectOne' : 'selectMany'](element);
    else {
      var opt, value, single = !Object.isArray(index);
      for (var i = 0, length = element.length; i < length; i++) {
        opt = element.options[i];
        value = this.optionValue(opt);
        if (single) {
          if (value == index) {
            opt.selected = true;
            return;
          }
        }
        else opt.selected = index.include(value);
      }
    }
  },

  selectOne: function(element) {
    var index = element.selectedIndex;
    return index >= 0 ? this.optionValue(element.options[index]) : null;
  },

  selectMany: function(element) {
    var values, length = element.length;
    if (!length) return null;

    for (var i = 0, values = []; i < length; i++) {
      var opt = element.options[i];
      if (opt.selected) values.push(this.optionValue(opt));
    }
    return values;
  },

  optionValue: function(opt) {
    // extend element because hasAttribute may not be native
    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  }
};

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
  initialize: function($super, element, frequency, callback) {
    $super(callback, frequency);
    this.element   = $(element);
    this.lastValue = this.getValue();
  },

  execute: function() {
    var value = this.getValue();
    if (Object.isString(this.lastValue) && Object.isString(value) ?
        this.lastValue != value : String(this.lastValue) != String(value)) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
});

Form.Element.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create(Abstract.TimedObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = Class.create({
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    Form.getElements(this.element).each(this.registerCallback, this);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        default:
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
});

Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create(Abstract.EventObserver, {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) var Event = { };

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: { },

  relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return Element.extend(element);
  }
});

Event.Methods = (function() {
  var isButton;

  if (Prototype.Browser.IE) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (Prototype.Browser.WebKit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      var node = Event.extend(event).target;
      return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
    },

    findElement: function(event, expression) {
      var element = Event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return Selector.findElement(elements, expression, 0);
    },

    pointer: function(event) {
      return {
        x: event.pageX || (event.clientX +
          (document.documentElement.scrollLeft || document.body.scrollLeft)),
        y: event.pageY || (event.clientY +
          (document.documentElement.scrollTop || document.body.scrollTop))
      };
    },

    pointerX: function(event) { return Event.pointer(event).x },
    pointerY: function(event) { return Event.pointer(event).y },

    stop: function(event) {
      Event.extend(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})();

Event.extend = (function() {
  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
    m[name] = Event.Methods[name].methodize();
    return m;
  });

  if (Prototype.Browser.IE) {
    Object.extend(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = Prototype.emptyFunction;
      var pointer = Event.pointer(event);
      Object.extend(event, {
        target: event.srcElement,
        relatedTarget: Event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return Object.extend(event, methods);
    };

  } else {
    Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
    Object.extend(Event.prototype, methods);
    return Prototype.K;
  }
})();

Object.extend(Event, (function() {
  var cache = Event.cache;
  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
	arguments.callee.id = arguments.callee.id || 1;
    return element._prototypeEventID = [++arguments.callee.id];
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
    return cache[id] = cache[id] || { };
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
    return c[eventName] = c[eventName] || [];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);
    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
      if (!Event || !Event.extend ||
        (event.eventName && event.eventName != eventName))
          return false;

      Event.extend(event);
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }

  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  return {
    observe: function(element, eventName, handler) {
      element = $(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = $(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        Object.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = $(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

Object.extend(Event, Event.Methods);

Element.addMethods({
  fire:          Event.fire,
  observe:       Event.observe,
  stopObserving: Event.stopObserving
});

Object.extend(document, {
  fire:          Element.Methods.fire.methodize(),
  observe:       Element.Methods.observe.methodize(),
  stopObserving: Element.Methods.stopObserving.methodize(),
  loaded:        false
});

(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards and John Resig. */

  var timer;

  function fireContentLoadedEvent() {
    if (document.loaded) return;
    if (timer) window.clearInterval(timer);
    document.fire("dom:loaded");
    document.loaded = true;
  }

  if (document.addEventListener) {
    if (Prototype.Browser.WebKit) {
      timer = window.setInterval(function() {
        if (/loaded|complete/.test(document.readyState))
          fireContentLoadedEvent();
      }, 0);

      Event.observe(window, "load", fireContentLoadedEvent);

    } else {
      document.addEventListener("DOMContentLoaded",
        fireContentLoadedEvent, false);
    }

  } else {
    document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
    $("__onDOMContentLoaded").onreadystatechange = function() {
      if (this.readyState == "complete") {
        this.onreadystatechange = null;
        fireContentLoadedEvent();
      }
    };
  }
})();
/*------------------------------- DEPRECATED -------------------------------*/

Hash.toQueryString = Object.toQueryString;

var Toggle = { display: Element.toggle };

Element.Methods.childOf = Element.Methods.descendantOf;

var Insertion = {
  Before: function(element, content) {
    return Element.insert(element, {before:content});
  },

  Top: function(element, content) {
    return Element.insert(element, {top:content});
  },

  Bottom: function(element, content) {
    return Element.insert(element, {bottom:content});
  },

  After: function(element, content) {
    return Element.insert(element, {after:content});
  }
};

var $continue = new Error('"throw $continue" is deprecated, use "return" instead');

// This should be moved to script.aculo.us; notice the deprecated methods
// further below, that map to the newer Element methods.
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = Element.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = Element.cumulativeScrollOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = Element.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  // Deprecation layer -- use newer Element methods now (1.5.2).

  cumulativeOffset: Element.Methods.cumulativeOffset,

  positionedOffset: Element.Methods.positionedOffset,

  absolutize: function(element) {
    Position.prepare();
    return Element.absolutize(element);
  },

  relativize: function(element) {
    Position.prepare();
    return Element.relativize(element);
  },

  realOffset: Element.Methods.cumulativeScrollOffset,

  offsetParent: Element.Methods.getOffsetParent,

  page: Element.Methods.viewportOffset,

  clone: function(source, target, options) {
    options = options || { };
    return Element.clonePosition(target, source, options);
  }
};

/*--------------------------------------------------------------------------*/

if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
  function iter(name) {
    return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
  }

  instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
  function(element, className) {
    className = className.toString().strip();
    var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
    return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
  } : function(element, className) {
    className = className.toString().strip();
    var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
    if (!classNames && !className) return elements;

    var nodes = $(element).getElementsByTagName('*');
    className = ' ' + className + ' ';

    for (var i = 0, child, cn; child = nodes[i]; i++) {
      if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
          (classNames && classNames.all(function(name) {
            return !name.toString().blank() && cn.include(' ' + name + ' ');
          }))))
        elements.push(Element.extend(child));
    }
    return elements;
  };

  return function(className, parentElement) {
    return $(parentElement || document.body).getElementsByClassName(className);
  };
}(Element.Methods);

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set($A(this).concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set($A(this).without(classNameToRemove).join(' '));
  },

  toString: function() {
    return $A(this).join(' ');
  }
};

Object.extend(Element.ClassNames.prototype, Enumerable);

/*--------------------------------------------------------------------------*/

Element.addMethods();

var cookie = {
  set: function(name, value, daysToExpire) {
    var expire = '';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      expire = '; expires=' + d.toGMTString()+'; path=/; domain=.madcad.com;';
    }
    return (document.cookie = escape(name) + '=' + escape(value || '') + expire);
  },
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  erase: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  },
  accept: function() {
    if (typeof navigator.cookieEnabled == 'boolean') {
      return navigator.cookieEnabled;
    }
    Cookie.set('_test', '1');
    return (Cookie.erase('_test') === '1');
  }
};

var dbug = 0; 

function d_a(ary) {
	var beg = next_entry(ary) - 1; 
	for (var i = beg ; i > -1; i--) {
		ary[i] = null;
	}
} 

function init_array() {
	if (dbug) alert('init_cookie');  
	var ary = new Array(null); 
	return ary;
} 

function set_cookie(name,value,expires) {
	if (dbug) alert('set_cookie'); 
	if (!expires) {
		expires = new Date();
	}else{
		expires=expires; //*60*1000*60*24
	};
	
	var e = new Date();
	e.setTime(e.getTime() + (86400000 * parseFloat(expires)));
	document.cookie = name + '=' + escape(value) + '; expires=' + e.toGMTString() + '; path=/; domain=madcad.com';
} 

function get_cookie(name) {
	if (dbug) alert('get_cookie'); 
	var dcookie = document.cookie; 
	var cname = name + "="; 
	var clen = dcookie.length; 
	var cbegin = 0; 
	while (cbegin < clen) {
		var vbegin = cbegin + cname.length;
		if (dcookie.substring(cbegin, vbegin) == cname) {
			var vend = dcookie.indexOf (";", vbegin); 
			if (vend == -1) vend = clen; 
			
			return unescape(dcookie.substring(vbegin, vend));
		} 
		cbegin = dcookie.indexOf(" ", cbegin) + 1; 
		if (cbegin == 0) break;
	} return null;
} 

function del_cookie(name) {
	if (dbug) alert('del_cookie');
	document.cookie = name + '=' + '; expires=Thu, 01-Feb-69 00:00:01 GMT; path=/; domain=madcad.com';
} 

function get_array(name, ary) {
	if (dbug) alert('get_array'); 
	d_a(ary); 
	var ent = get_cookie(name); 
	if (ent) {i = 1; while (ent.indexOf('^') != '-1') {
		ary[i] = ent.substring(0,ent.indexOf('^')); 
		i++;
		ent = ent.substring(ent.indexOf('^')+1, ent.length);
}}} 

function set_array(name, ary, expires) {
	if (dbug) alert('set_array'); 
	var value = ''; 
	for (var i = 1; ary[i]; i++) {value += ary[i] + '^';} 
	set_cookie(name, value, expires);
} 

function del_entry(name, ary, pos, expires) {
	if (dbug) alert('del_entry');
	var value = ''; 
	get_array(name, ary); 
	for (var i = 1; i < pos; i++) {value += ary[i] + '^';} 
	for (var j = pos + 1; ary[j]; j++) {value += ary[j] + '^';} 
	set_cookie(name, value, expires);
} 

function next_entry(ary) {
	if (dbug) alert('next_entry'); 
	var j = 0; 
	for (var i = 1; ary[i]; i++) {j = i} return j + 1;
}

function debug_on() {
		dbug = 1;
} 

function debug_off() {
		dbug = 0;
} 

function dump_cookies() {
	if (document.cookie == '') document.write('No Cookies Found'); 
	else {thisCookie = document.cookie.split('; '); 
	for (i=0; i<thisCookie.length; i++) {document.write(thisCookie[i] + '<br \/>');
	}
	}
}



//v1.0
//Copyright 2006 Adobe Systems, Inc. All rights reserved.
function AC_AddExtension(src, ext)
{
  if (src.indexOf('?') != -1)
    return src.replace(/\?/, ext+'?'); 
  else
    return src + ext;
}

function AC_Generateobj(objAttrs, params, embedAttrs) 
{ 
  var str = '<object ';
  for (var i in objAttrs)
    str += i + '="' + objAttrs[i] + '" ';
  str += '>';
  for (var i in params)
    str += '<param name="' + i + '" value="' + params[i] + '" /> ';
  str += '<embed ';
  for (var i in embedAttrs)
    str += i + '="' + embedAttrs[i] + '" ';
  str += ' ></embed></object>';

  document.write(str);
}

function AC_FL_RunContent(){
  var ret = 
    AC_GetArgs
    (  arguments, ".swf", "movie", "clsid:d27cdb6e-ae6d-11cf-96b8-444553540000"
     , "application/x-shockwave-flash"
    );
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_SW_RunContent(){
  var ret = 
    AC_GetArgs
    (  arguments, ".dcr", "src", "clsid:166B1BCA-3F9C-11CF-8075-444553540000"
     , null
    );
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_GetArgs(args, ext, srcParamName, classid, mimeType){
  var ret = new Object();
  ret.embedAttrs = new Object();
  ret.params = new Object();
  ret.objAttrs = new Object();
  for (var i=0; i < args.length; i=i+2){
    var currArg = args[i].toLowerCase();    

    switch (currArg){	
      case "classid":
        break;
      case "pluginspage":
        ret.embedAttrs[args[i]] = args[i+1];
        break;
      case "src":
      case "movie":	
        args[i+1] = AC_AddExtension(args[i+1], ext);
        ret.embedAttrs["src"] = args[i+1];
        ret.params[srcParamName] = args[i+1];
        break;
      case "onafterupdate":
      case "onbeforeupdate":
      case "onblur":
      case "oncellchange":
      case "onclick":
      case "ondblClick":
      case "ondrag":
      case "ondragend":
      case "ondragenter":
      case "ondragleave":
      case "ondragover":
      case "ondrop":
      case "onfinish":
      case "onfocus":
      case "onhelp":
      case "onmousedown":
      case "onmouseup":
      case "onmouseover":
      case "onmousemove":
      case "onmouseout":
      case "onkeypress":
      case "onkeydown":
      case "onkeyup":
      case "onload":
      case "onlosecapture":
      case "onpropertychange":
      case "onreadystatechange":
      case "onrowsdelete":
      case "onrowenter":
      case "onrowexit":
      case "onrowsinserted":
      case "onstart":
      case "onscroll":
      case "onbeforeeditfocus":
      case "onactivate":
      case "onbeforedeactivate":
      case "ondeactivate":
      case "type":
      case "codebase":
        ret.objAttrs[args[i]] = args[i+1];
        break;
      case "width":
      case "height":
      case "align":
      case "vspace": 
      case "hspace":
      case "class":
      case "title":
      case "accesskey":
      case "name":
      case "id":
      case "tabindex":
        ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
        break;
      default:
        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
    }
  }
  ret.objAttrs["classid"] = classid;
  if (mimeType) ret.embedAttrs["type"] = mimeType;
  return ret;
}


//v1.1
//Copyright 2006 Adobe Systems, Inc. All rights reserved.
function AC_AX_RunContent(){
  var ret = AC_AX_GetArgs(arguments);
  AC_Generateobj(ret.objAttrs, ret.params, ret.embedAttrs);
}

function AC_AX_GetArgs(args){
  var ret = new Object();
  ret.embedAttrs = new Object();
  ret.params = new Object();
  ret.objAttrs = new Object();
  for (var i=0; i < args.length; i=i+2){
    var currArg = args[i].toLowerCase();    

    switch (currArg){	
      case "pluginspage":
      case "type":
      case "src":
        ret.embedAttrs[args[i]] = args[i+1];
        break;
      case "data":
      case "codebase":
      case "classid":
      case "id":
      case "onafterupdate":
      case "onbeforeupdate":
      case "onblur":
      case "oncellchange":
      case "onclick":
      case "ondblClick":
      case "ondrag":
      case "ondragend":
      case "ondragenter":
      case "ondragleave":
      case "ondragover":
      case "ondrop":
      case "onfinish":
      case "onfocus":
      case "onhelp":
      case "onmousedown":
      case "onmouseup":
      case "onmouseover":
      case "onmousemove":
      case "onmouseout":
      case "onkeypress":
      case "onkeydown":
      case "onkeyup":
      case "onload":
      case "onlosecapture":
      case "onpropertychange":
      case "onreadystatechange":
      case "onrowsdelete":
      case "onrowenter":
      case "onrowexit":
      case "onrowsinserted":
      case "onstart":
      case "onscroll":
      case "onbeforeeditfocus":
      case "onactivate":
      case "onbeforedeactivate":
      case "ondeactivate":
        ret.objAttrs[args[i]] = args[i+1];
        break;
      case "width":
      case "height":
      case "align":
      case "vspace": 
      case "hspace":
      case "class":
      case "title":
      case "accesskey":
      case "name":
      case "tabindex":
        ret.embedAttrs[args[i]] = ret.objAttrs[args[i]] = args[i+1];
        break;
      default:
        ret.embedAttrs[args[i]] = ret.params[args[i]] = args[i+1];
    }
  }
  return ret;
}


 // JavaScript Document

function loadInto (objID, templateID, vars){
//loadinto loads template parts into a div	
var date = new Date(); ms=date.getTime();
	if ( typeof vars== 'undefined' || vars==null){vars="";}
	e=util.encode64(templateID+"&"+vars+"&loadIntoFlag=1");
	documentURL="tLoader.php?dvo="+ms+"&e="+e;
	debug(documentURL+"[raw:ID-"+templateID+":: vars-"+vars+"]" );
	new Ajax.Updater(objID, documentURL, {   
		method: 'get',
		evalScripts: true  
	}); 
}

function functionLoad(ID, func, vars, loadIntoVar){
//functionLoad performs a server function and loads into ID (div/false or var/true)	
	if ( typeof vars== 'undefined' || vars==null){vars="";}
	e=util.encode64(ID+"&func="+func+"&"+vars+"&loadIntoFlag=1");
	documentURL="function.php?e="+e;
	debug("SEND -> "+documentURL+"[raw:ID="+ID+":: func-"+func+":: vars-"+vars+"]" );
	new Ajax.Updater(ID, documentURL, {   
		method: 'get',
		evalScripts: true  
	});	
	
}

function loadCart(cart){
	
	window.location='http://'+server_name+'/?np=6&gc='+cart;
	//alert ("cart="+cart);
	
}

function checkQtyDiv() {
	if ($('adminUsernameResult').innerHTML=="clear"){
		$('AdminUsernameStatus').innerHTML="";
		
		result=true;
	}else{
		$('AdminUsernameStatus').innerHTML="Username NOT available.";
		result=false;
	}
	return result;
}


function checkName(){
	var date = new Date(); ms=date.getTime();
	new Ajax.Updater('adminUsernameResult','/checkUsername.php',{
	parameters:{name: $F('newAdminUserName'),
				 dvo: ms },
	onComplete: function(){
		checkQtyDiv();
		
		}
	});
	
}

function checkQtyDiv2() {
	if ($('accountUsernameResult').innerHTML=="clear"){
		$('AccountUsernameStatus').innerHTML="";
		
		result=true;
	}else{
		$('AccountUsernameStatus').innerHTML="Username NOT available.";
		result=false;
	}
	return result;
}


function checkName2(){
	var date = new Date(); ms=date.getTime();
	new Ajax.Updater('accountUsernameResult','/checkUsername.php',{
	parameters:{name: $F('newAccountUserName'),
				 dvo: ms },
	onComplete: function(){
		checkQtyDiv2();
		
		}
	});
	
}


function getByYear(){
	//x=$F('year').value;
	Obj = document.getElementById('year');
	x=Obj.options[Obj.selectedIndex].value;
	
	return (x);
}


function noenter() {
  warn ("no enter from here.");
  return !(window.event && window.event.keyCode == 13); }

function AjaxLoad (section, objID, source, data, params){
//section: top | middle | bottom
//objID: div | span to fill
//source: template | file | content 
//params: additional params to add to query string
	
	
	}


function searchIpubs(info,searchTerm){
	//alert ("searchIpubs("+info+","+"searchTerm"+")");
	functionLoad('RafArea','sortProducts','pub=combo&year=All&kw='+searchTerm);
 }
 
 
var Notifier = Class.create({
	
	_events: [[window, 'scroll'], [window, 'resize'], [document, 'mousemove'], [document, 'keydown'], [window, 'onclick']],
	_timer: null,
	_idleTime: null,
	
	initialize: function(time) {
		this.time = time;
		
		this.initObservers();
		this.setTimer();
	},
	
	initObservers: function() {
		this._events.each(function(e) {
			Event.observe(e[0], e[1], this.onInterrupt.bind(this))
		}.bind(this))
	},
	
	onInterrupt: function() {
		document.fire('state:active', { idleTime: new Date() - this._idleTime });
		this.setTimer();
	},
	
	setTimer: function() {
		clearTimeout(this._timer);
		this._idleTime = new Date();
		this._timer = setTimeout(function() {
			document.fire('state:idle');
		}, this.time)
	}
});

function dofocus(field){
	document.getElementById(field).focus(); 
	return true;
	}

function toggleLoginBar(){
	var obj = $('loginContainer');
	if(obj.style.display == 'none'){ 
		obj.style.display = 'inline';
		//Effect.SlideDown(obj.id, { duration: .25, scaleFrom: 3, scaleTo: 100});
		 try { dofocus('UserName'); } catch (e) { /**/};
		
	}else{
		Effect.SlideUp(obj.id, { duration: 0.6, scaleFrom: 100, scaleTo: 3});
	}
}

function toggleCartList(objID){
	var obj = $(objID);
	var obj2 = $('CartList');
	if(obj2.style.display == 'none'){
		Effect.toggle(obj2,'blind',{duration:0.5});
		obj.innerHTML = "<img src=\"img/store_button_close.jpg\" style=\"border:none\" align=\"absmiddle\"> Close Cart ";
	}else{
		Effect.toggle(obj2,'blind',{duration:0.5});
		obj.innerHTML = "<img src=\"img/store_button_open.jpg\" style=\"border:none\" align=\"absmiddle\"> Open Cart ";
	}
}

var tmpId = "";
var t=0;
var timer2 = 0;
var mouseOut = 0;
use=eval;
function showTooltip(id, contenttt, contType, methodData){
	if(!methodData){
		methodData = 'Get';
	}
	mouseOut = 0;
	clearTimeout(t);
	clearTimeout(timer2);
	var flyobj = $("toolTipCont");
	var obj = $(id);
	var a = String(Position.cumulativeOffset(obj));
	var b = [];
	b = a.split(',');
	var posX = parseInt(b[0]);
	var posY = parseInt(b[1]);
	var objW = Element.getWidth(obj);
	var objH = Element.getWidth(obj);
	var contentObj = '';
	flyobj.style.position = 'absolute';
	flyobj.style.left = posX + objW;
	flyobj.style.top = posY;
	if(contType == 0){
		flyobj.innerHTML = contenttt;
	}
	if(contType == 1){
		var cont = new Ajax.Request(contenttt, {asynchronous: false, method: methodData});
		var doHTML = false;
		if(cont.transport.responseXML)
		{
			var xml = cont.transport.responseXML.getElementsByTagName('SubCategories')[0];
			if(xml)
			{
				//yazilacak
			}else{
				doHTML = true;
			}

			if(doHTML)
			{
				contentObj = cont.transport.responseText;
			}
		}
	}
	//var ww = Browser.isMSIE() ? document.body.clientWidth : window.outerWidth;
	ww = window.outerWidth;
	if((posY+Element.getHeight(flyobj)) > ww){
		var posnewY = parseInt(posY - (Element.getHeight(flyobj) - Element.getHeight(obj)));
		flyobj.style.top = posnewY;
	}

	flyobj.style.display="none";
	Effect.Appear(flyobj);
	tmpId=String(id);
}
function controlMouseTooltip(id){
	if(mouseOut==1){
		var flyobj = $("toolTipCont");
		Effect.Fade(flyobj);
		clearTimeout(t);
	}else{
		t = setTimeout(controlMouseTooltip,2000,id);
	}
}

function hideTooltip(id){
	mouseOut = 1;
	clearTimeout(timer2);
	timer2 = setTimeout(controlMouseTooltip,2000,id);
}

var cartListHeight = 0;
var cartListIds = [];
function controlCartListItem() {
	cartListHeight = 0;
	for(i=0;i<cartListIds.length;i++){
		var obj2 = $(''+cartListIds[i]+'');
		if(i<20){
			cartListHeight = cartListHeight + parseInt(Element.getHeight(obj2));
		}
	}
	var obj3 = $('CartListItemCont');
	var ListItemContHeight = Element.getHeight(obj3);
	if(cartListIds.length > 20){
		if(ListItemContHeight > cartListHeight){
			obj3.style.height = cartListHeight; 
			obj3.className = "CartListItemContOverFlow";
			$('ViewFullCartBtn').style.display="inline";
		}
	}else{
		obj3.style.height = 'auto';
		obj3.className = "";
		$('ViewFullCartBtn').style.display="none";
	}
	if(cartListIds.length>0){
		if(cartListIds.length == 1){
			$('CartItemsAmount').innerHTML = "<strong>"+cartListIds.length+" Item</strong>";
		}else{
			$('CartItemsAmount').innerHTML = "<strong>"+cartListIds.length+" Items</strong>";
		}
	}else{
		$('CartItemsAmount').innerHTML = "<strong>--</strong>";
	}
}

function toggleViewCart(){
	var obj = $('ViewFullCartBtn');
	var obj3 = $('CartListItemCont');
	if(obj3.style.height == 'auto'){
		obj3.style.height = cartListHeight; 
		obj3.className = "CartListItemContOverFlow";
	}else{
		obj3.style.height = 'auto';
		obj3.className = "";
	}
}

var cartItemIdNum=0;
function addToCart(){
	$('CartListEmptyMsg').style.display = "none";
	cartListIds.push('CartListItem'+cartItemIdNum+'');
	var obj = $('CartListItemCont');
	obj.innerHTML = obj.innerHTML + "<div id=\"CartListItem"+cartItemIdNum+"\"><table width=\"100%\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr bgcolor=\"#ffffff\" height=\"22\"><td width=\"6\" bgcolor=\"#00FF99\">&nbsp;</td><td width=\"52\">&nbsp;&nbsp;<span>2006</span></td><td width=\"63\"><span>ICC</span></td><td width=\"46\"><span>IFC</span></td><td width=\"220\" id=\"bookinCart"+cartItemIdNum+"\" onClick=\"showBookInfo(this.id,\'<img src=img/2006_IFC_cover_Lg.jpg align=top hspace=3 vspace=3 style=float:left;> <strong> 2006 International Fire Code® </strong> <br><br>The 2006 International Fire Code® address topics which include fire department access, fire hydrants, automatic sprinkler systems, fire alarm systems, hazardous materials storage and use, and fire-safety requirements for new and existing buildings and premises.\', 0); return false;\" style=\"cursor:pointer;\"><span>International Fire Code</span></td><td width=\"90\"><span>$91.50</span></td><td width=\"75\"><input type=\"text\" maxlength=\"2\" value=\"1\" class=\"defTextAreaStyle\" style=\"width:18px;\"></td><td width=\"59\"><span class=\"textmain\">unlimited</span></td><td align=\"center\"><input type=\"button\" class=\"defTextAreaStyle\" value=\"Update\" style=\"width:40px;\"> <input type=\"button\" class=\"defTextAreaStyle\" value=\"Remove\" style=\"width:46px;\" onClick=\"removeCartListItem('CartListItem"+cartItemIdNum+"');\"></td></tr></table></div>";
	cartItemIdNum++;
	controlCartListItem();
}

function removeCartListItem(itemId){
	var tmpIdList = [];
	tmpIdList = cartListIds;
	for(i=0;i<tmpIdList.length;i++){
		if(tmpIdList[i] == String(itemId)){
			var removedIdNum = i;
		}
	}
	cartListIds = null;
	cartListIds = [];
	for(i=0;i<tmpIdList.length;i++){
		if(i != removedIdNum){
			cartListIds.push(tmpIdList[i]);
		}
	}
	if(!removedIdNum){
		$('CartListEmptyMsg').style.display = "block";
	}
	$(''+itemId+'').style.display = 'none';
	/*$('CartListItem'+removedIdNum+'').id = 'Removed' + removedIdNum;*/
	controlCartListItem();
}

function showBookInfo(id, contenttt, contType, methodData){
	if(!methodData){
		methodData = 'Get';
	}
	var flyobj = $("BookInfoCont");
	var explainCont = $("explainContBookInfo");
	var obj = $(id);
	var a = String(Position.cumulativeOffset(obj));
	var b = [];
	b = a.split(',');
	var posX = parseInt(b[0]);
	var posY = parseInt(b[1]);
	var objW = Element.getWidth(obj);
	var objH = Element.getHeight(obj);
	var rpos = String(Position.realOffset(obj));
	var a2 = [];
	a2 = rpos.split(',');
	var scrollAmount = parseInt(a2[1]);
	var c = String(Position.cumulativeOffset(obj.offsetParent.offsetParent));
	var d = [];
	d = c.split(',');
	var listH = Element.getHeight(obj.offsetParent) + parseInt(d[1]);
	var contH = Element.getHeight(obj.offsetParent.offsetParent);
	var contentObj = '';
	flyobj.style.position = 'absolute';
	flyobj.style.left = posX;
	if(scrollAmount > 0){
		if(posY>(contH+parseInt(d[1]))){
			flyobj.style.top = posY+objH - (listH - (contH+parseInt(d[1])));
		}else{
			flyobj.style.top = posY+objH-scrollAmount;
		}
	}else{
		if(posY>(contH+parseInt(d[1]))){
			flyobj.style.top = posY+objH - (listH - (contH+parseInt(d[1])));
		}else{
			flyobj.style.top = posY+objH;
		}
	}
	
	if(contType == 0){
		explainCont.innerHTML = contenttt;
	}
	if(contType == 1){
		var cont = new Ajax.Request(contenttt, {asynchronous: false, method: methodData});
		var doHTML = false;
		if(cont.transport.responseXML)
		{
			var xml = cont.transport.responseXML.getElementsByTagName('SubCategories')[0];
			if(xml)
			{
				//yazilacak
			}else{
				doHTML = true;
			}

			if(doHTML)
			{
				contentObj = cont.transport.responseText;
			}
		}
	}
	//var ww = Browser.isMSIE() ? document.body.clientWidth : window.outerWidth;
	var ww = window.outerWidth;
	if((posY+Element.getHeight(flyobj)) > ww){
		var posnewY = parseInt(posY - (Element.getHeight(flyobj) - Element.getHeight(obj)));
		flyobj.style.top = posnewY;
	}
	flyobj.style.display="none";
	Effect.Appear(flyobj);
	tmpId=String(id);
}

function clearText(thefield){
if (thefield.defaultValue==thefield.value)
thefield.value = ""
}


function hideBookInfo(){
		var flyobj = $("BookInfoCont");
		Effect.Fade(flyobj);
}

var mapcordinates = [];
mapcordinates['WA'] = "28,0"; mapcordinates['OR'] = "7,38"; mapcordinates['CA'] = "0,101"; mapcordinates['ID'] = "90,14";
mapcordinates['NV'] = "45,114"; mapcordinates['AZ'] = "86,206"; mapcordinates['VT'] = "109,128"; mapcordinates['MT'] = "119,16"; 
mapcordinates['WY'] = "155,87"; mapcordinates['CO'] = "171,152"; mapcordinates['NM'] = "160,215"; mapcordinates['ND'] = "243,34";
mapcordinates['SD'] = "240,80"; mapcordinates['TX'] = "193,230"; mapcordinates['NE'] = "238,127"; mapcordinates['KS'] = "258,175";
mapcordinates['OK'] = "245,222"; mapcordinates['MN'] = "317,29"; mapcordinates['IA'] = "324,120"; mapcordinates['MO'] = "335,166";
mapcordinates['AR'] = "349,229"; mapcordinates['LA'] = "359,285"; mapcordinates['WI'] = "366,65"; mapcordinates['IL'] = "386,133"; 
mapcordinates['MS'] = "392,251"; mapcordinates['MI'] = "390,55"; mapcordinates['IN'] = "430,141"; mapcordinates['KY'] = "415,182"; 
mapcordinates['TN'] = "406,215"; mapcordinates['AL'] = "432,247"; mapcordinates['FL'] = "445,306"; mapcordinates['GA'] = "465,243"; 
mapcordinates['OH'] = "462,129"; mapcordinates['WV'] = "495,151"; mapcordinates['VA'] = "486,164"; mapcordinates['NC'] = "481,203";
mapcordinates['SC'] = "494,236"; mapcordinates['DC'] = "559,165"; mapcordinates['PA'] = "511,117"; mapcordinates['NY'] = "518,65"; 
mapcordinates['VT'] = "580,60"; mapcordinates['NH'] = "596,55"; mapcordinates['ME'] = "603,15"; mapcordinates['MA'] = "590,91"; 
mapcordinates['RI'] = "609,106"; mapcordinates['CT'] = "591,107"; mapcordinates['MD'] = "529,154"; mapcordinates['DE'] = "572,151"; 
mapcordinates['NJ'] = "576,126"; mapcordinates['AK'] = "8,326"; mapcordinates['HI'] = "8,334"; mapcordinates['UT'] = "109,128";
var mapPicsURL = 'img/map/';
function mapAreaOver(obj){
	var contObj = $('mapOverContainer');
	var contImgObj =  $('areaMapOver');
	contImgObj.src='img/map/blank.gif';
	var rollOverObj = $(obj);
	var usmap=$('usmap');
	var cordsUsMap = String(Position.cumulativeOffset(usmap));
	var a = [];
	a = cordsUsMap.split(',');
	var cordsUsMapX = parseInt(a[0]);
	var cordsUsMapY = parseInt(a[1]);
	if(contObj.style.display == "none"){	
		contObj.style.display = "block";
	}
	contImgObj.src=mapPicsURL+(String(rollOverObj.id).toLowerCase())+"_over.gif";
	var xy = String(mapcordinates[''+rollOverObj.id+'']);
	var xya = [];
	xya = xy.split(',');
	contObj.style.left = cordsUsMapX + parseInt(xya[0]);
	contObj.style.top = cordsUsMapY + parseInt(xya[1]);
}

function mapAreaChosen(obj){
	var contObj = $('mapClickContainer');
	var contImgObj =  $('areaMapClick');
	var rollOverObj = $(obj);
	contImgObj.src='img/map/blank.gif';
	var usmap=$('usmap');
	var cordsUsMap = String(Position.cumulativeOffset(usmap));
	var a = [];
	a = cordsUsMap.split(',');
	var cordsUsMapX = parseInt(a[0]);
	var cordsUsMapY = parseInt(a[1]);
	if(contObj.style.display == "none"){	
		contObj.style.display = "block";
	}
	var xy = String(mapcordinates[''+rollOverObj.id+'']);
	var xya = [];
	xya = xy.split(',');
	contImgObj.src=mapPicsURL+String(rollOverObj.id).toLowerCase()+"_over.gif";
	contObj.style.left = cordsUsMapX + parseInt(xya[0]);
	contObj.style.top = cordsUsMapY + parseInt(xya[1]);
	var selectBox = $('stateSelection');
	for(i=0; i<selectBox.length; i++){
		 if(selectBox[i].value == String(rollOverObj.id)){
		 	var currentInd = i;
		 };
	}
	selectBox.selectedIndex = currentInd;
}

function setMapLocater(){
	var obj = $('usmap');
	var obj2 = $('usmapLocater');
	var cordsUsMap = String(Position.cumulativeOffset(obj));
	var a = [];
	a = cordsUsMap.split(',');
	if(document.all){
		obj2.style.display = "block";
	}else{
		obj2.style.left = parseInt(a[0]);
		obj2.style.top = parseInt(a[1]);
		obj2.style.display = "block";
	}
}

//size 1 for small, 2 for normal, 3 for big All in #contentResults
function changeFontSize(obj,size){
	var contentObj = $(obj);
	
	if(size == 1){
		contentObj.className='contentFontSizeSmall';
				var children = $A($(obj).childNodes); 
					children.each(function(child) {
 					child.className='contentFontSizeSmall';
					});
	}else if(size == 3){
		contentObj.className='contentFontSizeBig';
				var children = $A($(obj).childNodes); 
					children.each(function(child) {
 					child.className='contentFontSizeBig';
					});

   }else{
		contentObj.className='';
					var children = $A($(obj).childNodes); 
					children.each(function(child) {
 					child.className='';
					});

}

}

function toogleTabGeneralSearch(obj){
	var obj = $(obj);
	var nameObj = ''+obj.id+'Left';
	var objleft = $(nameObj);
	nameObj = ''+obj.id+'Right';
	var objright = $(nameObj);
	nameObj = ''+obj.id+'Center';
	var objcenter = $(nameObj);
	objleft.className='generalSearchTabsLeftActive';
	objcenter.className ='generalSearchTabsCenterActive';
	objright.className='generalSearchTabsRightActive';
	var objLength = 2;
	for(i=1; i<objLength; i++){
		var contObj = $('tabHeader'+i);
		if(contObj){
			objLength++;
		}
	}
	objLength--;
	var obj2 = '';
	var obj2left = '';
	var obj2right = '';
	var obj2center = '';
	for(i=1; i<objLength; i++){
		nameObj = 'tabHeader'+i+'';
		obj2 = $(nameObj);
		if(obj2 == obj){
			nameObj='tabContent'+i+'';
			$(nameObj).style.display="block";
		}else{
			nameObj='tabContent'+i+'';
			$(nameObj).style.display="none";
			nameObj = ''+obj2.id+'Left';
			obj2left = $(nameObj);
			nameObj = ''+obj2.id+'Right';
			obj2right = $(nameObj);
			nameObj = ''+obj2.id+'Center';
			obj2center = $(nameObj);
			obj2left.className='generalSearchTabsLeft';
			obj2center.className ='generalSearchTabsCenter';
			obj2right.className='generalSearchTabsRight';
		}
	}
}

function toogleTabLocal2(obj){
	var obj = $(obj);
	var nameObj = ''+obj.id+'Left';
	var objleft = $(nameObj);
	nameObj = ''+obj.id+'Right';
	var objright = $(nameObj);
	nameObj = ''+obj.id+'Center';
	var objcenter = $(nameObj);
	objleft.className='local2TabsLeftActive';
	objcenter.className ='local2TabsCenterActive';
	objright.className='local2TabsRightActive';
	var objLength = 2;
	for(i=1; i<objLength; i++){
		var contObj = $('tabHeader'+i);
		if(contObj){
			objLength++;
		}
	}
	objLength--;
	var obj2 = '';
	var obj2left = '';
	var obj2right = '';
	var obj2center = '';
	for(i=1; i<objLength; i++){
		nameObj = 'tabHeader'+i+'';
		obj2 = $(nameObj);
		if(obj2 == obj){
			nameObj='tabContent'+i+'';
			$(nameObj).style.display="block";
		}else{
			nameObj='tabContent'+i+'';
			$(nameObj).style.display="none";
			nameObj = ''+obj2.id+'Left';
			obj2left = $(nameObj);
			nameObj = ''+obj2.id+'Right';
			obj2right = $(nameObj);
			nameObj = ''+obj2.id+'Center';
			obj2center = $(nameObj);
			obj2left.className='local2TabsLeft';
			obj2center.className ='local2TabsCenter';
			obj2right.className='local2TabsRight';
		}
	}
}


/*
 * Content-seperated javascript tree widget
 * Copyright (C) 2005 SilverStripe Limited
 * Feel free to use this on your websites, but please leave this message in the fies
 * http://www.silverstripe.com/blog
*/

/*
 * Initialise all trees identified by <ul class="tree">
 */
function autoInit_trees() {
	var candidates = document.getElementsByTagName('ul');
	for(var i=0;i<candidates.length;i++) {
		if(candidates[i].className && candidates[i].className.indexOf('tree') != -1) {
			initTree(candidates[i]);
			candidates[i].className = candidates[i].className.replace(/ ?unformatted ?/, ' ');
		}
	}
}
 
/*
 * Initialise a tree node, converting all its LIs appropriately
 */
function initTree(el) {
	var i,j;
	var spanA, spanB, spanC;
	var startingPoint, stoppingPoint, childUL;
	
	// Find all LIs to process
	for(i=0;i<el.childNodes.length;i++) {
		if(el.childNodes[i].tagName && el.childNodes[i].tagName.toLowerCase() == 'li') {
			var li = el.childNodes[i];

			// Create our extra spans
			spanA = document.createElement('span');
			spanB = document.createElement('span');
			spanC = document.createElement('span');
			spanA.appendChild(spanB);
			spanA.appendChild(spanC);
			spanA.className = 'a ' + li.className.replace('closed','spanClosed');
			spanA.onMouseOver = function() {}
			spanB.className = 'b';
			spanB.innerHTML = '&nbsp;';
			spanB.onclick = treeToggle;
			spanC.className = 'c';
			
			
			// Find the UL within the LI, if it exists
			stoppingPoint = li.childNodes.length;
			startingPoint = 0;
			childUL = null;
			for(j=0;j<li.childNodes.length;j++) {
				if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'div') {
					startingPoint = j + 1;
					continue;
				}

				if(li.childNodes[j].tagName && li.childNodes[j].tagName.toLowerCase() == 'ul') {
					childUL = li.childNodes[j];
					stoppingPoint = j;
					break;					
				}
			}
				
			// Move all the nodes up until that point into spanC
			for(j=startingPoint;j<stoppingPoint;j++) {
				spanC.appendChild(li.childNodes[startingPoint]);
			}
			
			// Insert the outermost extra span into the tree
			if(li.childNodes.length > startingPoint) li.insertBefore(spanA, li.childNodes[startingPoint]);
			else li.appendChild(spanA);
			
			// Process the children
			if(childUL != null) {
				if(initTree(childUL)) {
					addClass(li, 'children', 'closed');
					addClass(spanA, 'children', 'spanClosed');
				}
			}
		}
	}
	
	if(li) {
		// li and spanA will still be set to the last item

		addClass(li, 'last', 'closed');
		addClass(spanA, 'last', 'spanClosed');
		return true;
	} else {
		return false;
	}
		
}
 

/*
 * +/- toggle the tree, where el is the <span class="b"> node
 * force, will force it to "open" or "close"
 */
function treeToggle(el, force) {
	el = this;
	
	while(el != null && (!el.tagName || el.tagName.toLowerCase() != "li")) el = el.parentNode;
	
	// Get UL within the LI
	var childSet = findChildWithTag(el, 'ul');
	var topSpan = findChildWithTag(el, 'span');

	if( force != null ){
		
		if( force == "open"){
			treeOpen( topSpan, el )
		}
		else if( force == "close" ){
			treeClose( topSpan, el )
		}
		
	}
	
	else if( childSet != null) {
		// Is open, close it
		if(!el.className.match(/(^| )closed($| )/)) {		
			treeClose( topSpan, el )
		// Is closed, open it
		} else {			
			treeOpen( topSpan, el )
		}
	}
}


function treeOpen( a, b ){
	removeClass(a,'spanClosed');
	removeClass(b,'closed');
}
	
	
function treeClose( a, b ){
	addClass(a,'spanClosed');
	addClass(b,'closed');
}

/*
 * Find the a child of el of type tag
 */
function findChildWithTag(el, tag) {
	for(var i=0;i<el.childNodes.length;i++) {
		if(el.childNodes[i].tagName != null && el.childNodes[i].tagName.toLowerCase() == tag) return el.childNodes[i];
	}
	return null;
}

/*
 * Functions to add and remove class names
 * Mac IE hates unnecessary spaces
 */
function addClass(el, cls, forceBefore) {
	if(forceBefore != null && el.className.match(new RegExp('(^| )' + forceBefore))) {
		el.className = el.className.replace(new RegExp("( |^)" + forceBefore), '$1' + cls + ' ' + forceBefore);

	} else if(!el.className.match(new RegExp('(^| )' + cls + '($| )'))) {
		el.className += ' ' + cls;
		el.className = el.className.replace(/(^ +)|( +$)/g, '');
	}
}
function removeClass(el, cls) {
	var old = el.className;
	var newCls = ' ' + el.className + ' ';
	newCls = newCls.replace(new RegExp(' (' + cls + ' +)+','g'), ' ');
	el.className = newCls.replace(/(^ +)|( +$)/g, '');
} 

/*
 * Handlers for automated loading
 */ 
 _LOADERS = Array();

function callAllLoaders() {
	var i, loaderFunc;
	for(i=0;i<_LOADERS.length;i++) {
		loaderFunc = _LOADERS[i];
		if(loaderFunc != callAllLoaders) loaderFunc();
	}
}

function appendLoader(loaderFunc) {
	if(window.onload && window.onload != callAllLoaders)
		_LOADERS[_LOADERS.length] = window.onload;

	window.onload = callAllLoaders;

	_LOADERS[_LOADERS.length] = loaderFunc;
}

appendLoader(autoInit_trees);


function Utility () {
	
	this.concatObject = function (obj)
	{
  	str='';
  	for(prop in obj)
  		{
    		str+=prop + " value :"+ obj[prop]+"\n";
  		}
  	
	return(str);
	}
	
	this.makeMoney = function (amount)
	{	
		function addCommas(nStr)
		{
			nStr += '';
			x = nStr.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? '.' + x[1] : '';
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(x1)) {
				x1 = x1.replace(rgx, '$1' + ',' + '$2');
			}
			return x1 + x2;
		}
	
		var i = parseFloat(amount);
		if(isNaN(i)) { i = 0.00; }
		var minus = '';
		if(i < 0) { minus = '-'; }
		i = Math.abs(i);
		i = parseInt((i + .005) * 100);
		i = i / 100;
		s = new String(i);
		if(s.indexOf('.') < 0) { s += '.00'; }
		if(s.indexOf('.') == (s.length - 2)) { s += '0'; }
		s = minus + s;
		//alert (s);
		//alert (addCommas(s));
		return addCommas(s);
	}
	
	// Base64 code from Tyler Akins -- http://rumkin.com
	this.keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
	
	this.encode64 = function (input) {
		
		//input = ''+input;
		
	   var output = "";
	   var chr1, chr2, chr3;
	   var enc1, enc2, enc3, enc4;
	   var i = 0;
	
	   do {
		  chr1 = input.charCodeAt(i++);
		  chr2 = input.charCodeAt(i++);
		  chr3 = input.charCodeAt(i++);
	
		  enc1 = chr1 >> 2;
		  enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
		  enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
		  enc4 = chr3 & 63;
	
		  if (isNaN(chr2)) {
			 enc3 = enc4 = 64;
		  } else if (isNaN(chr3)) {
			 enc4 = 64;
		  }
	
		  output = output + this.keyStr.charAt(enc1) + this.keyStr.charAt(enc2) + 
			 this.keyStr.charAt(enc3) + this.keyStr.charAt(enc4);
	   } while (i < input.length);
	   
	   return output;
	}
	
	this.decode64 = function (input) {
	   var output = "";
	   var chr1, chr2, chr3;
	   var enc1, enc2, enc3, enc4;
	   var i = 0;
	
	   // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
	   input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
	
	   do {
		  enc1 = this.keyStr.indexOf(input.charAt(i++));
		  enc2 = this.keyStr.indexOf(input.charAt(i++));
		  enc3 = this.keyStr.indexOf(input.charAt(i++));
		  enc4 = this.keyStr.indexOf(input.charAt(i++));
	
		  chr1 = (enc1 << 2) | (enc2 >> 4);
		  chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
		  chr3 = ((enc3 & 3) << 6) | enc4;
	
		  output = output + String.fromCharCode(chr1);
	
		  if (enc3 != 64) {
			 output = output + String.fromCharCode(chr2);
		  }
		  if (enc4 != 64) {
			 output = output + String.fromCharCode(chr3);
		  }
	   } while (i < input.length);
	
	   return output;
	}
	
} //end Util
var util=new Utility;



var Cart = Class.create({
	initialize: function(AuthenticatedSessionID){
		this.cartID=0;
		this.ASID=AuthenticatedSessionID;
		this.viewStyle=0;
		this.checkoutStage=1;
		this.qty=0;
		this.contents=new Array;
		this.contentsDesc=new Array;
		this.contentsQty=new Array;
				
		
		info("new cart created: "+ this.ASID );
		this.updateCartViews(this.qty);
		//get cart contents from cookie-server 
	},
	_pushToCartContents: function(ipubID,item,num){
		var tempIndex = this.contents.length

		debug("[_pushToCartContents] recieved: [ipubID]"+ipubID+" [item]"+item+" [num]"+num+".");
		this.contents[tempIndex] = ipubID;
		this.contentsDesc[tempIndex] = item;
		this.contentsQty[tempIndex] = parseInt(num);
	
		//todo: later, move this out to abstracted updateDisplayClass
		cart.addRow(ipubID,item,num);
	},
	_countQty: function(ipubID){

		matchingID=this.contents.indexOf(ipubID); //find the ID in the Abstracted Cart
		var newQty=this.contentsQty[matchingID];
		
		return newQty;

	},

	_updateItemQty: function(ipubID,item,num){
		info("[_updateItemQty] recieved: [ipubID]"+ipubID+" [item]"+item+" [num]"+num+". matches ID "+this.contents.indexOf(ipubID));
		matchingID=this.contents.indexOf(ipubID); //find the ID in the Abstracted Cart
		this.contentsQty[matchingID]=(parseInt(this.contentsQty[matchingID])+parseInt(num));

		var newQty=this.contentsQty[matchingID];
		
		//todo: later, move this out to abstracted updateDisplayClass
		 
	 	var length = cart.rowsCol.length;

		for (var index = 0; index < length; ++index) {   
			var distilledIpubID = cart.getRowId(index);    
			
			if (distilledIpubID==ipubID) {
				cartIndex=index;
				debug("[_updateItemQty] found matching index in cart - id"+distilledIpubID+" on row "+cartIndex);
			}
		} 
		
		cart.deleteRow(ipubID);
		
		//temp hack just to show updated in halfcart
		var itemID=item.split(",");
		itemID[5]=newQty;
		itemID[4]=util.makeMoney(itemID[5]*91.50);
		item=itemID.join(",");
		error ("New line item "+item);
		cart.addRow(ipubID,item,cartIndex);
				
		//update the cart display to show correct QTY
		//based on what is on the server

		
		
		
	
	
	},

	updateCartViews: function(qty){
		$('qty').innerHTML=qty;
		return;
	
	},
	//id= unique ipub ID, item= all the fields for cart view, num= qty of the items
	addItem: function(id,item,num){
	
		debug("Call to ::addItem (id/"+id+" item/"+item+" num/"+num+" [cartID:"+ this.ASID+"]" );
		
		var itemAlreadyExists=this.contents.include(id);
		
		
		//TODO: Add item to this.contents after scrub and register to server 
		if (!itemAlreadyExists) {
			//used when this is a new item type in cart
			debug ("New item in cart");
			this._pushToCartContents(id,item,num);
		}else{
			//used when there are-is existing item of this type
			debug ("this item exists in cart");
			this._updateItemQty(id,item,num);
		}
		
		
		
		
		x= this.contents.zip(this.contentsQty, function(a) { return a.join('x'); });
		
		warn("cart CONTENTS: "+x);
		
	//update 'my cart' indicator 
		//check to see what the indicator styatus is
		this.qty+=parseInt(num);
		this.updateCartViews(this.qty);
		
	//update cart if on page
		//todo: add check for cart existing on page prior to updating it!!
		//cart.addRow(123,"text1,text2,"+item+","+num+"",1);
		
	//for now just grab it off the dom...need to change this to pulling from an abstract products list
		//var itemID=item.split(","); 
		//_findMatch(itemID[0],"cart");
		//cart.addRow(itemID[0],item,1);
		
		return this.qty;
		
	},
	//removeItem(ipub identification):Remove an item from the cart (return true on success or false on fail
	removeItem: function(item) {
		
		//get the current qty from the obj array			
		var retrievedQty = this._countQty(item);
		if (retrievedQty=="" || typeof retrievedQty=="undefined") {retrievedQty = 0;}
		
		
		warn("[removeItem recieve information] item="+item+" retrievedQty="+retrievedQty);

		
		//update the contents - item - qty
		this._updateItemQty(item,"delete",-(retrievedQty));
			
		//make this work from the virtual item not the DOM
		//todo : add error check for 0 too; why not eh
		this.qty-=retrievedQty;
		
		//todo: check if there is a "mycart" item on page
		this.updateCartViews(this.qty);
		
		//todo: check if there is a cart item on page
		//todo: remove item from cart
		cart.deleteRow(item);
		
		return this.qty;
	},
	deleteAll: function(){
		this.qty=0;
		this.updateCartViews(this.qty);
		return this.qty;
	},
	emailCart: function() {
		return "not available yet";
	},
	saveCart: function() {
		return "not available yet";
	},
	checkout: function() {
		this.checkoutStage+=1;
		return this.checkoutStage;
	}

});



function printCart(cart){
//this will change
var load = window.open('http://'+server_name+'/printCart.php?cart='+cart,'','scrollbars=no,menubar=no,height=600,width=800,resizable=yes,toolbar=no,location=no,status=no');
}

function printContract(){
//this will change
var load = window.open('http://'+server_name+'/printContract.php','_blank','scrollbars=yes,menubar=no,height=600,width=800,resizable=yes,toolbar=no,location=no,status=no');
}

function emailCart(cart){
//this will change
var load = window.open('http://'+server_name+'/emailCart.php?cart='+cart,'','scrollbars=no,menubar=no,height=600,width=800,resizable=yes,toolbar=no,location=no,status=no');
}

function saveCart(cart){
//this will change
var load = window.open('http://'+server_name+'/saveCart.php?cart='+cart,'','scrollbars=no,menubar=no,height=600,width=800,resizable=yes,toolbar=no,location=no,status=no');
}

function helpWindow(area){
//change area to allow fo rdifferent types of help. 2. Change so that text is loaded onto div nd bkgnd fade
var load = window.open('http://'+server_name+'/ccv.php','','scrollbars=no,menubar=no,height=284,width=419,resizable=yes,toolbar=no,location=no,status=no');
}

function EUAWindow(contract){
//change area to allow fo rdifferent types of help. 2. Change so that text is loaded onto div nd bkgnd fade
var load = window.open('http://'+server_name+'/emailEUA.php','','scrollbars=yes,menubar=yes,height=284,width=600,resizable=yes,toolbar=no,location=no,status=no');
}

function CopyTextToFields(formName){
	var thisForm=formName;
	with (document.forms[thisForm]) {
		
		
	if ($F('isSameAdminInfo')!=null){	
		$('FirstName').value = $('FirstName-text').innerHTML; $('FirstName').setAttribute('disabled','disabled');
		$('LastName').value = $('LastName-text').innerHTML; $('LastName').setAttribute('disabled','disabled');
		$('Company').value = $('Company-text').innerHTML; $('Company').setAttribute('disabled','disabled');
		$('Address1').value = $('Address1-text').innerHTML; $('Address1').setAttribute('disabled','disabled');
		$('Address2').value = $('Address2-text').innerHTML; $('Address2').setAttribute('disabled','disabled');
		$('City').value = $('City-text').innerHTML; $('City').setAttribute('disabled','disabled');
		$('Industry').value = $('Industry-text').innerHTML; $('Industry').setAttribute('disabled','disabled');
		$('State').value = $('State-text').innerHTML; $('State').setAttribute('disabled','disabled');
		$('zipCode').value = $('zipCode-text').innerHTML; $('zipCode').setAttribute('disabled','disabled');
		$('Country').value = $('Country-text').innerHTML; $('Country').setAttribute('disabled','disabled');
		$('Phone').value = $('Phone-text').innerHTML; $('Phone').setAttribute('disabled','disabled');
		$('PhoneExtension').value = $('PhoneExtension-text').innerHTML; $('PhoneExtension').setAttribute('disabled','disabled');
		$('email').value = $('email-text').innerHTML; $('email').setAttribute('disabled','disabled');
		$('Fax').value = $('Fax-text').innerHTML; $('Fax').setAttribute('disabled','disabled');
		Validation.reset();
	}else{
		$('FirstName').removeAttribute('disabled');	
		$('LastName').removeAttribute('disabled');	
		$('Company').removeAttribute('disabled');	
		$('Address1').removeAttribute('disabled');	
		$('Address2').removeAttribute('disabled');	
		$('City').removeAttribute('disabled');	
		$('Industry').removeAttribute('disabled');	
		$('State').removeAttribute('disabled');	
		$('zipCode').removeAttribute('disabled');	
		$('Country').removeAttribute('disabled');	
		$('Phone').removeAttribute('disabled');	
		$('PhoneExtension').removeAttribute('disabled');	
		$('email').removeAttribute('disabled');	
		$('Fax').removeAttribute('disabled');	
	}
	
	
	
	}
}

function refreshGrids(){
	error ("resize");

	
}

function clearBillingInfoForm(frm) {

with (document.forms[frm]) {
   		$('FirstName').value ="";
		$('LastName').value ="";
		$('Company').value ="";
		$('Address1').value ="";
		$('Address2').value ="";
		$('City').value ="";
		$('Industry').value ="";
		$('State').value ="";
		$('zipCode').value ="";
		$('Country').value ="";
		$('Phone').value ="";
		$('PhoneExtension').value ="";
		$('email').value ="";
		$('Fax').value ="";
		$('isSameAdminInfo').checked=false;
				$('FirstName').removeAttribute('disabled');	
		$('LastName').removeAttribute('disabled');	
		$('Company').removeAttribute('disabled');	
		$('Address1').removeAttribute('disabled');	
		$('Address2').removeAttribute('disabled');	
		$('City').removeAttribute('disabled');	
		$('Industry').removeAttribute('disabled');	
		$('State').removeAttribute('disabled');	
		$('zipCode').removeAttribute('disabled');	
		$('Country').removeAttribute('disabled');	
		$('Phone').removeAttribute('disabled');	
		$('PhoneExtension').removeAttribute('disabled');	
		$('email').removeAttribute('disabled');	
		$('Fax').removeAttribute('disabled');	
Validation.reset();
}
}


/* START JS FOR HINT BOXES __ STILL NEEDS CLEANUP */


var horizontal_offset="9px" //horizontal offset of hint box from anchor link

var vertical_offset="0" //horizontal offset of hint box from anchor link. No need to change.
var ie=document.all
var ns6=document.getElementById&&!document.all

function getposOffset(what, offsettype){
var totaloffset=(offsettype=="left")? what.offsetLeft : what.offsetTop;
var parentEl=what.offsetParent;
while (parentEl!=null){
totaloffset=(offsettype=="left")? totaloffset+parentEl.offsetLeft : totaloffset+parentEl.offsetTop;
parentEl=parentEl.offsetParent;
}
return totaloffset;
}

function iecompattest(){
return (document.compatMode && document.compatMode!="BackCompat")? document.documentElement : document.body
}

function clearbrowseredge(obj, whichedge){
var edgeoffset=(whichedge=="rightedge")? parseInt(horizontal_offset)*-1 : parseInt(vertical_offset)*-1
if (whichedge=="rightedge"){
var windowedge=ie && !window.opera? iecompattest().scrollLeft+iecompattest().clientWidth-30 : window.pageXOffset+window.innerWidth-40
dropmenuobj.contentmeasure=dropmenuobj.offsetWidth
if (windowedge-dropmenuobj.x < dropmenuobj.contentmeasure)
edgeoffset=dropmenuobj.contentmeasure+obj.offsetWidth+parseInt(horizontal_offset)
}
else{
var windowedge=ie && !window.opera? iecompattest().scrollTop+iecompattest().clientHeight-15 : window.pageYOffset+window.innerHeight-18
dropmenuobj.contentmeasure=dropmenuobj.offsetHeight
if (windowedge-dropmenuobj.y < dropmenuobj.contentmeasure)
edgeoffset=dropmenuobj.contentmeasure-obj.offsetHeight
}
return edgeoffset
}

function showhint(menucontents, obj, e, tipwidth){
if ((ie||ns6) && document.getElementById("hintbox")){
dropmenuobj=document.getElementById("hintbox")
dropmenuobj.innerHTML=menucontents
dropmenuobj.style.left=dropmenuobj.style.top=-500
if (tipwidth!=""){
dropmenuobj.widthobj=dropmenuobj.style
dropmenuobj.widthobj.width=tipwidth
}
dropmenuobj.x=getposOffset(obj, "left")
dropmenuobj.y=getposOffset(obj, "top")
dropmenuobj.style.left=dropmenuobj.x-clearbrowseredge(obj, "rightedge")+obj.offsetWidth+"px"
dropmenuobj.style.top=dropmenuobj.y-clearbrowseredge(obj, "bottomedge")+"px"
dropmenuobj.style.visibility="visible"
obj.onmouseout=hidetip
}
}

function hidetip(e){
dropmenuobj.style.visibility="hidden"
dropmenuobj.style.left="-500px"
}

function createhintbox(){
var divblock=document.createElement("div")
divblock.setAttribute("id", "hintbox")
document.body.appendChild(divblock)
}

if (window.addEventListener)
window.addEventListener("load", createhintbox, false)
else if (window.attachEvent)
window.attachEvent("onload", createhintbox)
else if (document.getElementById)
window.onload=createhintbox

/*END HINT BOXES */


function stateNAZip(thisform){
	if ($F('State')=="NA"){ 
		$('zipCode').value='00000';
	}else{
		
}}

function lenSort(v1,v2,direction){
	alert (v1);
if(direction=="asc"){
		return v1.length>v2.length?1:-1;
	}else{
		return v1.length<v2.length?1:-1;
	}
}

function numSort(a,b,direction){
	//araw=v1.split('$');
	//braw=v2.split('$');
	//a=araw[1]; b=braw[1];
	a=a.replace('$',''); b=b.replace('$','');
	a=a.replace(',',''); b=b.replace(',','');

	error("compare "+a+" to "+b+".");
	
	if(direction=="asc"){
		return parseFloat(a)>parseFloat(b)?1:-1;
	}else{
		return parseFloat(a)<parseFloat(b)?1:-1;
	}
}


function titleSort(v1,v2,direction){

	araw=v1.split('|||'); braw=v2.split('|||');
	a=araw[1]; b=braw[1];
	
	//Start NFPA sort HACK
	//if NFPA us a little tweak this natural sort is based on ascii info 
		//therefor NFPA 202: vs NFPA 20F: vs NFPA 2F2003 has a bit of an issue
		//if there is an NFPA and a : in the title then compare the title between the two. otherwise use the whole title
		//a bit brute but it works well enough for launch
		nfpa_a=a.split('NFPA '); nfpa_b=b.split('NFPA ');
		
		if (typeof(nfpa_a[1])!='undefined' && typeof(nfpa_b[1])!='undefined'){
			nfpa_compare_a=nfpa_a[1].split(':');
			nfpa_compare_b=nfpa_b[1].split(':');
			if (typeof(nfpa_compare_a[0])!='undefined')a=nfpa_compare_a[0];
			if (typeof(nfpa_compare_b[0])!='undefined')b=nfpa_compare_b[0];
		}
	//END NFPA sort HACK

	if(direction=="asc"){ 
		return alphanum(a, b);
	}else{
		return alphanum(b, a);
	}
}

function alphanum(a, b) {
  function chunkify(t) {
    var tz = [], x = 0, y = -1, n = 0, i, j;

    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        tz[++y] = "";
        n = m;
      }
      tz[y] += j;
    }
    return tz;
  }

  var aa = chunkify(a);
  var bb = chunkify(b);

  for (x = 0; aa[x] && bb[x]; x++) {
    if (aa[x] !== bb[x]) {
      var c = Number(aa[x]), d = Number(bb[x]);
      if (c == aa[x] && d == bb[x]) {
        return c - d;
      } else return (aa[x] > bb[x]) ? 1 : -1;
    }
  }
  return aa.length - bb.length;
}



function login(srcElement) {
	
	new Ajax.Updater('loginResult', 'login.php', {   
		parameters: { UserName: $F('UserName'), UserPass: $F('UserPass'), submitted: 1},
		method: 'post',
		evalScripts: true  
	}); 
	// send an ajax request to the server
	// cancel event
	return false;
}


function forgotCredentials(){
	
	alert ("Please call our offices at 1-800-798-9296 to recover your password.");
	//window.location='http://'+server_name+'/matrix.php';
	
	}

function logout() {
	
	//do any session cleanup here
	new Ajax.Updater('loginResult', 'logout.php', {
		onComplete:function(request){loadHome()},
		method: 'post',
		evalScripts: true  
	});
	
	
	
	return false;
}

function shouldlogin(){

loadHome();
toggleLoginBar();
alert("You should Login!");

}

function loadMatrix(){
	
	//alert ("about to go");
	window.location='http://'+server_name+'/front/';
	
	}
	

	
function loadHome(){
	
	//alert ("about to go");
	window.location='http://'+server_name+'/index.php';
	
	}

document.observe("dom:loaded",function(){
	cartAuth=cookie.get('CurrentCart');
	if (cartAuth==null || cartAuth.length < 3 ){
		cartAuth=getUniqueId();                        
      	debug ("Cart Reset  ["+cartAuth+"]");
	    cookie.set('CurrentCart',cartAuth,30);
		
	}
	userCart= new Cart(cartAuth); //this is to the original javascript MVC model- delete this after all legacy code is removed 
	cart=userCart;
	
});


/*kk
Ajax.Responders.register({
        onCreate: function() 
		{
                if($('activity') && Ajax.activeRequestCount> 0)
					{
						$('activity').style.visibility = "visible"; //added for IE
						$('overlayA').style.visibility = "visible";
						//$('overlay').setOpacity(.5); //ie error???!!
						Effect.Appear('activity',{duration: 0.1, queue: 'front'});
						Effect.Appear('overlayA',{duration: 0, queue: 'front'});
                        
					}
		},
        onComplete: function() {
                if($('activity') && Ajax.activeRequestCount == 0){
                        Effect.Fade('activity',{duration: 0.25, queue: 'end'});
						Effect.Fade('overlayA',{duration: .5, queue: 'end'});
						
					}
				
        }
});
*/



//SET UP PAGE STATES FOR ACTIVE / IDLE
document.observe('dom:loaded', function() {
	
				new Notifier(30000);
				
				document.observe('state:idle', onIdle).observe('state:active', onActive);
				   //var p = $$('p')[0],
				   //var p = $(document.body);
				   //var p = $('overlay');
				
					infoDIV = $('timerInfo');
				
				function onIdle(e) {
					//p.setOpacity(0.8);
					//p.style.backgroundColor = "orange";
					//p.style.visibility = "visible";
					
					infoDIV.innerHTML = 'idled...';
					//kk window.status= 'idled...';
				
				}
				function onActive(e) {
					//p.setOpacity(0);
					//p.style.visibility = "hidden";
					//p.style.backgroundColor = "black";
					infoDIV.innerHTML = 'active... after: ' + e.memo.idleTime + ' ms';
					//kk window.status = 'active... after: ' + e.memo.idleTime + ' ms';
				}
			});

AJS={BASE_URL:"",drag_obj:null,drag_elm:null,_drop_zones:[],_cur_pos:null,getScrollTop:function(){
var t;
if(document.documentElement&&document.documentElement.scrollTop){
t=document.documentElement.scrollTop;
}else{
if(document.body){
t=document.body.scrollTop;
}
}
return t;
},addClass:function(){
var _2=AJS.forceArray(arguments);
var _3=_2.pop();
var _4=function(o){
if(!new RegExp("(^|\\s)"+_3+"(\\s|$)").test(o.className)){
o.className+=(o.className?" ":"")+_3;
}
};
AJS.map(_2,function(_6){
_4(_6);
});
},setStyle:function(){
var _7=AJS.forceArray(arguments);
var _8=_7.pop();
var _9=_7.pop();
AJS.map(_7,function(_a){
_a.style[_9]=AJS.getCssDim(_8);
});
},extend:function(_b){
var _c=new this("no_init");
for(k in _b){
var _d=_c[k];
var _e=_b[k];
if(_d&&_d!=_e&&typeof _e=="function"){
_e=this._parentize(_e,_d);
}
_c[k]=_e;
}
return new AJS.Class(_c);
},log:function(o){
if(window.console){
console.log(o);
}else{
var div=AJS.$("ajs_logger");
if(!div){
div=AJS.DIV({id:"ajs_logger","style":"color: green; position: absolute; left: 0"});
div.style.top=AJS.getScrollTop()+"px";
AJS.ACN(AJS.getBody(),div);
}
AJS.setHTML(div,""+o);
}
},setHeight:function(){
var _11=AJS.forceArray(arguments);
_11.splice(_11.length-1,0,"height");
AJS.setStyle.apply(null,_11);
},_getRealScope:function(fn,_13){
_13=AJS.$A(_13);
var _14=fn._cscope||window;
return function(){
var _15=AJS.$FA(arguments).concat(_13);
return fn.apply(_14,_15);
};
},documentInsert:function(elm){
if(typeof (elm)=="string"){
elm=AJS.HTML2DOM(elm);
}
document.write("<span id=\"dummy_holder\"></span>");
AJS.swapDOM(AJS.$("dummy_holder"),elm);
},getWindowSize:function(doc){
doc=doc||document;
var _18,_19;
if(self.innerHeight){
_18=self.innerWidth;
_19=self.innerHeight;
}else{
if(doc.documentElement&&doc.documentElement.clientHeight){
_18=doc.documentElement.clientWidth;
_19=doc.documentElement.clientHeight;
}else{
if(doc.body){
_18=doc.body.clientWidth;
_19=doc.body.clientHeight;
}
}
}
return {"w":_18,"h":_19};
},flattenList:function(_1a){
var r=[];
var _1c=function(r,l){
AJS.map(l,function(o){
if(o==null){
}else{
if(AJS.isArray(o)){
_1c(r,o);
}else{
r.push(o);
}
}
});
};
_1c(r,_1a);
return r;
},isFunction:function(obj){
return (typeof obj=="function");
},setEventKey:function(e){
e.key=e.keyCode?e.keyCode:e.charCode;
if(window.event){
e.ctrl=window.event.ctrlKey;
e.shift=window.event.shiftKey;
}else{
e.ctrl=e.ctrlKey;
e.shift=e.shiftKey;
}
switch(e.key){
case 63232:
e.key=38;
break;
case 63233:
e.key=40;
break;
case 63235:
e.key=39;
break;
case 63234:
e.key=37;
break;
}
},removeElement:function(){
var _22=AJS.forceArray(arguments);
AJS.map(_22,function(elm){
AJS.swapDOM(elm,null);
});
},_unloadListeners:function(){
if(AJS.listeners){
AJS.map(AJS.listeners,function(elm,_25,fn){
AJS.REV(elm,_25,fn);
});
}
AJS.listeners=[];
},join:function(_27,_28){
try{
return _28.join(_27);
}
catch(e){
var r=_28[0]||"";
AJS.map(_28,function(elm){
r+=_27+elm;
},1);
return r+"";
}
},getIndex:function(elm,_2c,_2d){
for(var i=0;i<_2c.length;i++){
if(_2d&&_2d(_2c[i])||elm==_2c[i]){
return i;
}
}
return -1;
},isIn:function(elm,_30){
var i=AJS.getIndex(elm,_30);
if(i!=-1){
return true;
}else{
return false;
}
},isArray:function(obj){
return obj instanceof Array;
},setLeft:function(){
var _33=AJS.forceArray(arguments);
_33.splice(_33.length-1,0,"left");
AJS.setStyle.apply(null,_33);
},appendChildNodes:function(elm){
if(arguments.length>=2){
AJS.map(arguments,function(n){
if(AJS.isString(n)){
n=AJS.TN(n);
}
if(AJS.isDefined(n)){
elm.appendChild(n);
}
},1);
}
return elm;
},getElementsByTagAndClassName:function(_36,_37,_38,_39){
var _3a=[];
if(!AJS.isDefined(_38)){
_38=document;
}
if(!AJS.isDefined(_36)){
_36="*";
}
var els=_38.getElementsByTagName(_36);
var _3c=els.length;
var _3d=new RegExp("(^|\\s)"+_37+"(\\s|$)");
for(i=0,j=0;i<_3c;i++){
if(_3d.test(els[i].className)||_37==null){
_3a[j]=els[i];
j++;
}
}
if(_39){
return _3a[0];
}else{
return _3a;
}
},isOpera:function(){
return (navigator.userAgent.toLowerCase().indexOf("opera")!=-1);
},isString:function(obj){
return (typeof obj=="string");
},hideElement:function(elm){
var _40=AJS.forceArray(arguments);
AJS.map(_40,function(elm){
elm.style.display="none";
});
},setOpacity:function(elm,p){
elm.style.opacity=p;
elm.style.filter="alpha(opacity="+p*100+")";
},insertBefore:function(elm,_45){
_45.parentNode.insertBefore(elm,_45);
return elm;
},setWidth:function(){
var _46=AJS.forceArray(arguments);
_46.splice(_46.length-1,0,"width");
AJS.setStyle.apply(null,_46);
},createArray:function(v){
if(AJS.isArray(v)&&!AJS.isString(v)){
return v;
}else{
if(!v){
return [];
}else{
return [v];
}
}
},isDict:function(o){
var _49=String(o);
return _49.indexOf(" Object")!=-1;
},isMozilla:function(){
return (navigator.userAgent.toLowerCase().indexOf("gecko")!=-1&&navigator.productSub>=20030210);
},removeEventListener:function(elm,_4b,fn,_4d){
var _4e="ajsl_"+_4b+fn;
if(!_4d){
_4d=false;
}
fn=elm[_4e]||fn;
if(elm["on"+_4b]==fn){
elm["on"+_4b]=elm[_4e+"old"];
}
if(elm.removeEventListener){
elm.removeEventListener(_4b,fn,_4d);
if(AJS.isOpera()){
elm.removeEventListener(_4b,fn,!_4d);
}
}else{
if(elm.detachEvent){
elm.detachEvent("on"+_4b,fn);
}
}
},callLater:function(fn,_50){
var _51=function(){
fn();
};
window.setTimeout(_51,_50);
},setTop:function(){
var _52=AJS.forceArray(arguments);
_52.splice(_52.length-1,0,"top");
AJS.setStyle.apply(null,_52);
},_createDomShortcuts:function(){
var _53=["ul","li","td","tr","th","tbody","table","input","span","b","a","div","img","button","h1","h2","h3","h4","h5","h6","br","textarea","form","p","select","option","optgroup","iframe","script","center","dl","dt","dd","small","pre","i"];
var _54=function(elm){
AJS[elm.toUpperCase()]=function(){
return AJS.createDOM.apply(null,[elm,arguments]);
};
};
AJS.map(_53,_54);
AJS.TN=function(_56){
return document.createTextNode(_56);
};
},addCallback:function(fn){
this.callbacks.unshift(fn);
},bindMethods:function(_58){
for(var k in _58){
var _5a=_58[k];
if(typeof (_5a)=="function"){
_58[k]=AJS.$b(_5a,_58);
}
}
},partial:function(fn){
var _5c=AJS.$FA(arguments);
_5c.shift();
return function(){
_5c=_5c.concat(AJS.$FA(arguments));
return fn.apply(window,_5c);
};
},isNumber:function(obj){
return (typeof obj=="number");
},getCssDim:function(dim){
if(AJS.isString(dim)){
return dim;
}else{
return dim+"px";
}
},isIe:function(){
return (navigator.userAgent.toLowerCase().indexOf("msie")!=-1&&navigator.userAgent.toLowerCase().indexOf("opera")==-1);
},removeClass:function(){
var _5f=AJS.forceArray(arguments);
var cls=_5f.pop();
var _61=function(o){
o.className=o.className.replace(new RegExp("\\s?"+cls,"g"),"");
};
AJS.map(_5f,function(elm){
_61(elm);
});
},setHTML:function(elm,_65){
elm.innerHTML=_65;
return elm;
},map:function(_66,fn,_68,_69){
var i=0,l=_66.length;
if(_68){
i=_68;
}
if(_69){
l=_69;
}
for(i;i<l;i++){
var val=fn(_66[i],i);
if(val!=undefined){
return val;
}
}
},addEventListener:function(elm,_6e,fn,_70,_71){
var _72="ajsl_"+_6e+fn;
if(!_71){
_71=false;
}
AJS.listeners=AJS.$A(AJS.listeners);
if(AJS.isIn(_6e,["keypress","keydown","keyup","click"])){
var _73=fn;
fn=function(e){
AJS.setEventKey(e);
return _73.apply(window,arguments);
};
}
var _75=AJS.isIn(_6e,["submit","load","scroll","resize"]);
var _76=AJS.$A(elm);
AJS.map(_76,function(_77){
if(_70){
var _78=fn;
fn=function(e){
AJS.REV(_77,_6e,fn);
return _78.apply(window,arguments);
};
}
if(_75){
var _7a=_77["on"+_6e];
var _7b=function(){
if(_7a){
fn(arguments);
return _7a(arguments);
}else{
return fn(arguments);
}
};
_77[_72]=_7b;
_77[_72+"old"]=_7a;
elm["on"+_6e]=_7b;
}else{
_77[_72]=fn;
if(_77.attachEvent){
_77.attachEvent("on"+_6e,fn);
}else{
if(_77.addEventListener){
_77.addEventListener(_6e,fn,_71);
}
}
AJS.listeners.push([_77,_6e,fn]);
}
});
},preloadImages:function(){
AJS.AEV(window,"load",AJS.$p(function(_7c){
AJS.map(_7c,function(src){
var pic=new Image();
pic.src=src;
});
},arguments));
},forceArray:function(_7f){
var r=[];
AJS.map(_7f,function(elm){
r.push(elm);
});
return r;
},update:function(l1,l2){
for(var i in l2){
l1[i]=l2[i];
}
return l1;
},getBody:function(){
return AJS.$bytc("body")[0];
},HTML2DOM:function(_85,_86){
var d=AJS.DIV();
d.innerHTML=_85;
if(_86){
return d.childNodes[0];
}else{
return d;
}
},getElement:function(id){
if(AJS.isString(id)||AJS.isNumber(id)){
return document.getElementById(id);
}else{
return id;
}
},showElement:function(){
var _89=AJS.forceArray(arguments);
AJS.map(_89,function(elm){
elm.style.display="";
});
},bind:function(fn,_8c,_8d){
fn._cscope=_8c;
return AJS._getRealScope(fn,_8d);
},createDOM:function(_8e,_8f){
var i=0,_91;
var elm=document.createElement(_8e);
var _93=_8f[0];
if(AJS.isDict(_8f[i])){
for(k in _93){
_91=_93[k];
if(k=="style"||k=="s"){
elm.style.cssText=_91;
}else{
if(k=="c"||k=="class"||k=="className"){
elm.className=_91;
}else{
elm.setAttribute(k,_91);
}
}
}
i++;
}
if(_93==null){
i=1;
}
for(var j=i;j<_8f.length;j++){
var _91=_8f[j];
if(_91){
var _95=typeof (_91);
if(_95=="string"||_95=="number"){
_91=AJS.TN(_91);
}
elm.appendChild(_91);
}
}
return elm;
},swapDOM:function(_96,src){
_96=AJS.getElement(_96);
var _98=_96.parentNode;
if(src){
src=AJS.getElement(src);
_98.replaceChild(src,_96);
}else{
_98.removeChild(_96);
}
return src;
},isDefined:function(o){
return (o!="undefined"&&o!=null);
}};
AJS.$=AJS.getElement;
AJS.$$=AJS.getElements;
AJS.$f=AJS.getFormElement;
AJS.$p=AJS.partial;
AJS.$b=AJS.bind;
AJS.$A=AJS.createArray;
AJS.DI=AJS.documentInsert;
AJS.ACN=AJS.appendChildNodes;
AJS.RCN=AJS.replaceChildNodes;
AJS.AEV=AJS.addEventListener;
AJS.REV=AJS.removeEventListener;
AJS.$bytc=AJS.getElementsByTagAndClassName;
AJS.$AP=AJS.absolutePosition;
AJS.$FA=AJS.forceArray;
AJS.addEventListener(window,"unload",AJS._unloadListeners);
AJS._createDomShortcuts();
AJS.Class=function(_9a){
var fn=function(){
if(arguments[0]!="no_init"){
return this.init.apply(this,arguments);
}
};
fn.prototype=_9a;
AJS.update(fn,AJS.Class.prototype);
return fn;
};
AJS.Class.prototype={extend:function(_9c){
var _9d=new this("no_init");
for(k in _9c){
var _9e=_9d[k];
var cur=_9c[k];
if(_9e&&_9e!=cur&&typeof cur=="function"){
cur=this._parentize(cur,_9e);
}
_9d[k]=cur;
}
return new AJS.Class(_9d);
},implement:function(_a0){
AJS.update(this.prototype,_a0);
},_parentize:function(cur,_a2){
return function(){
this.parent=_a2;
return cur.apply(this,arguments);
};
}};
script_loaded=true;


script_loaded=true;

AJS.fx={_shades:{0:"ffffff",1:"ffffee",2:"ffffdd",3:"ffffcc",4:"ffffbb",5:"ffffaa",6:"ffff99"},highlight:function(_1,_2){
var _3=new AJS.fx.Base();
_3.elm=AJS.$(_1);
_3.options.duration=600;
_3.setOptions(_2);
AJS.update(_3,{increase:function(){
if(this.now==7){
_1.style.backgroundColor="#fff";
}else{
_1.style.backgroundColor="#"+AJS.fx._shades[Math.floor(this.now)];
}
}});
return _3.custom(6,0);
},fadeIn:function(_4,_5){
_5=_5||{};
if(!_5.from){
_5.from=0;
AJS.setOpacity(_4,0);
}
if(!_5.to){
_5.to=1;
}
var s=new AJS.fx.Style(_4,"opacity",_5);
return s.custom(_5.from,_5.to);
},fadeOut:function(_7,_8){
_8=_8||{};
if(!_8.from){
_8.from=1;
}
if(!_8.to){
_8.to=0;
}
_8.duration=300;
var s=new AJS.fx.Style(_7,"opacity",_8);
return s.custom(_8.from,_8.to);
},setWidth:function(_a,_b){
var s=new AJS.fx.Style(_a,"width",_b);
return s.custom(_b.from,_b.to);
},setHeight:function(_d,_e){
var s=new AJS.fx.Style(_d,"height",_e);
return s.custom(_e.from,_e.to);
}};
AJS.fx.Base=new AJS.Class({init:function(_10){
this.options={onStart:function(){
},onComplete:function(){
},transition:AJS.fx.Transitions.sineInOut,duration:500,wait:true,fps:50};
AJS.update(this.options,_10);
AJS.bindMethods(this);
},setOptions:function(_11){
AJS.update(this.options,_11);
},step:function(){
var _12=new Date().getTime();
if(_12<this.time+this.options.duration){
this.cTime=_12-this.time;
this.setNow();
}else{
setTimeout(AJS.$b(this.options.onComplete,this,[this.elm]),10);
this.clearTimer();
this.now=this.to;
}
this.increase();
},setNow:function(){
this.now=this.compute(this.from,this.to);
},compute:function(_13,to){
var _15=to-_13;
return this.options.transition(this.cTime,_13,_15,this.options.duration);
},clearTimer:function(){
clearInterval(this.timer);
this.timer=null;
return this;
},_start:function(_16,to){
if(!this.options.wait){
this.clearTimer();
}
if(this.timer){
return;
}
setTimeout(AJS.$p(this.options.onStart,this.elm),10);
this.from=_16;
this.to=to;
this.time=new Date().getTime();
this.timer=setInterval(this.step,Math.round(1000/this.options.fps));
return this;
},custom:function(_18,to){
return this._start(_18,to);
},set:function(to){
this.now=to;
this.increase();
return this;
},setStyle:function(elm,_1c,val){
if(this.property=="opacity"){
AJS.setOpacity(elm,val);
}else{
AJS.setStyle(elm,_1c,val);
}
}});
AJS.fx.Style=AJS.fx.Base.extend({init:function(elm,_1f,_20){
this.parent();
this.elm=elm;
this.setOptions(_20);
this.property=_1f;
},increase:function(){
this.setStyle(this.elm,this.property,this.now);
}});
AJS.fx.Styles=AJS.fx.Base.extend({init:function(elm,_22){
this.parent();
this.elm=AJS.$(elm);
this.setOptions(_22);
this.now={};
},setNow:function(){
for(p in this.from){
this.now[p]=this.compute(this.from[p],this.to[p]);
}
},custom:function(obj){
if(this.timer&&this.options.wait){
return;
}
var _24={};
var to={};
for(p in obj){
_24[p]=obj[p][0];
to[p]=obj[p][1];
}
return this._start(_24,to);
},increase:function(){
for(var p in this.now){
this.setStyle(this.elm,p,this.now[p]);
}
}});
AJS.fx.Transitions={linear:function(t,b,c,d){
return c*t/d+b;
},sineInOut:function(t,b,c,d){
return -c/2*(Math.cos(Math.PI*t/d)-1)+b;
}};
script_loaded=true;


script_loaded=true;

var GB_CURRENT=null;
GB_hide=function(cb){
GB_CURRENT.hide(cb);
};
GreyBox=new AJS.Class({init:function(_2){
this.use_fx=AJS.fx;
this.type="page";
this.overlay_click_close=false;
this.salt=0;
this.root_dir=GB_ROOT_DIR;
this.callback_fns=[];
this.reload_on_close=false;
this.src_loader=this.root_dir+"loader_frame.html";
var _3=window.location.hostname.indexOf("www");
var _4=this.src_loader.indexOf("www");
if(_3!=-1&&_4==-1){
this.src_loader=this.src_loader.replace("://","://www.");
}
if(_3==-1&&_4!=-1){
this.src_loader=this.src_loader.replace("://www.","://");
}
this.show_loading=true;
AJS.update(this,_2);
},addCallback:function(fn){
if(fn){
this.callback_fns.push(fn);
}
},show:function(_6){
GB_CURRENT=this;
this.url=_6;
var _7=[AJS.$bytc("object"),AJS.$bytc("select")];
AJS.map(AJS.flattenList(_7),function(_8){
_8.style.visibility="hidden";
});
this.createElements();
return false;
},hide:function(cb){
var me=this;
AJS.callLater(function(){
var _b=me.callback_fns;
if(_b!=[]){
AJS.map(_b,function(fn){
fn();
});
}
me.onHide();
if(me.use_fx){
var _d=me.overlay;
AJS.fx.fadeOut(me.overlay,{onComplete:function(){
AJS.removeElement(_d);
_d=null;
},duration:300});
AJS.removeElement(me.g_window);
}else{
AJS.removeElement(me.g_window,me.overlay);
}
me.removeFrame();
AJS.REV(window,"scroll",_GB_setOverlayDimension);
AJS.REV(window,"resize",_GB_update);
var _e=[AJS.$bytc("object"),AJS.$bytc("select")];
AJS.map(AJS.flattenList(_e),function(_f){
_f.style.visibility="visible";
});
GB_CURRENT=null;
if(me.reload_on_close){
window.location.reload();
}
if(AJS.isFunction(cb)){
cb();
}
},10);
},update:function(){
this.setOverlayDimension();
this.setFrameSize();
this.setWindowPosition();
},createElements:function(){
this.initOverlay();
this.g_window=AJS.DIV({"id":"GB_window"});
AJS.hideElement(this.g_window);
AJS.getBody().insertBefore(this.g_window,this.overlay.nextSibling);
this.initFrame();
this.initHook();
this.update();
var me=this;
if(this.use_fx){
AJS.fx.fadeIn(this.overlay,{duration:300,to:0.7,onComplete:function(){
me.onShow();
AJS.showElement(me.g_window);
me.startLoading();
}});
}else{
AJS.setOpacity(this.overlay,0.7);
AJS.showElement(this.g_window);
this.onShow();
this.startLoading();
}
AJS.AEV(window,"scroll",_GB_setOverlayDimension);
AJS.AEV(window,"resize",_GB_update);
},removeFrame:function(){
try{
AJS.removeElement(this.iframe);
}
catch(e){
}
this.iframe=null;
},startLoading:function(){
this.iframe.src=this.src_loader+"?s="+this.salt++;
AJS.showElement(this.iframe);
},setOverlayDimension:function(){
var _11=AJS.getWindowSize();
if(AJS.isMozilla()||AJS.isOpera()){
AJS.setWidth(this.overlay,"100%");
}else{
AJS.setWidth(this.overlay,_11.w);
}
var _12=Math.max(AJS.getScrollTop()+_11.h,AJS.getScrollTop()+this.height);
if(_12<AJS.getScrollTop()){
AJS.setHeight(this.overlay,_12);
}else{
AJS.setHeight(this.overlay,AJS.getScrollTop()+_11.h);
}
},initOverlay:function(){
this.overlay=AJS.DIV({"id":"GB_overlay"});
if(this.overlay_click_close){
AJS.AEV(this.overlay,"click",GB_hide);
}
AJS.setOpacity(this.overlay,0);
AJS.getBody().insertBefore(this.overlay,AJS.getBody().firstChild);
},initFrame:function(){
if(!this.iframe){
var d={"name":"GB_frame","class":"GB_frame","frameBorder":0};
if(AJS.isIe()){
d.src="javascript:false;document.write(\"\");";
}
this.iframe=AJS.IFRAME(d);
this.middle_cnt=AJS.DIV({"class":"content"},this.iframe);
this.top_cnt=AJS.DIV();
this.bottom_cnt=AJS.DIV();
AJS.ACN(this.g_window,this.top_cnt,this.middle_cnt,this.bottom_cnt);
}
},onHide:function(){
},onShow:function(){
},setFrameSize:function(){
},setWindowPosition:function(){
},initHook:function(){
}});
_GB_update=function(){
if(GB_CURRENT){
GB_CURRENT.update();
}
};
_GB_setOverlayDimension=function(){
if(GB_CURRENT){
GB_CURRENT.setOverlayDimension();
}
};
AJS.preloadImages(GB_ROOT_DIR+"indicator.gif");
script_loaded=true;
var GB_SETS={};
function decoGreyboxLinks(){
var as=AJS.$bytc("a");
AJS.map(as,function(a){
if(a.getAttribute("href")&&a.getAttribute("rel")){
var rel=a.getAttribute("rel");
if(rel.indexOf("gb_")==0){
var _17=rel.match(/\w+/)[0];
var _18=rel.match(/\[(.*)\]/)[1];
var _19=0;
var _1a={"caption":a.title||"","url":a.href};
if(_17=="gb_pageset"||_17=="gb_imageset"){
if(!GB_SETS[_18]){
GB_SETS[_18]=[];
}
GB_SETS[_18].push(_1a);
_19=GB_SETS[_18].length;
}
if(_17=="gb_pageset"){
a.onclick=function(){
GB_showFullScreenSet(GB_SETS[_18],_19);
return false;
};
}
if(_17=="gb_imageset"){
a.onclick=function(){
GB_showImageSet(GB_SETS[_18],_19);
return false;
};
}
if(_17=="gb_image"){
a.onclick=function(){
GB_showImage(_1a.caption,_1a.url);
return false;
};
}
if(_17=="gb_page"){
a.onclick=function(){
var sp=_18.split(/, ?/);
GB_show(_1a.caption,_1a.url,parseInt(sp[1]),parseInt(sp[0]));
return false;
};
}
if(_17=="gb_page_fs"){
a.onclick=function(){
GB_showFullScreen(_1a.caption,_1a.url);
return false;
};
}
if(_17=="gb_page_center"){
a.onclick=function(){
var sp=_18.split(/, ?/);
GB_showCenter(_1a.caption,_1a.url,parseInt(sp[1]),parseInt(sp[0]));
return false;
};
}
}
}
});
}
AJS.AEV(window,"load",decoGreyboxLinks);
GB_showImage=function(_1d,url,_1f){
var _20={width:300,height:300,type:"image",fullscreen:false,center_win:true,caption:_1d,callback_fn:_1f};
var win=new GB_Gallery(_20);
return win.show(url);
};
GB_showPage=function(_22,url,_24){
var _25={type:"page",caption:_22,callback_fn:_24,fullscreen:true,center_win:false};
var win=new GB_Gallery(_25);
return win.show(url);
};
GB_Gallery=GreyBox.extend({init:function(_27){
this.parent({});
this.img_close=this.root_dir+"g_close.gif";
AJS.update(this,_27);
this.addCallback(this.callback_fn);
},initHook:function(){
AJS.addClass(this.g_window,"GB_Gallery");
var _28=AJS.DIV({"class":"inner"});
this.header=AJS.DIV({"class":"GB_header"},_28);
AJS.setOpacity(this.header,0);
AJS.getBody().insertBefore(this.header,this.overlay.nextSibling);
var _29=AJS.TD({"id":"GB_caption","class":"caption","width":"40%"},this.caption);
var _2a=AJS.TD({"id":"GB_middle","class":"middle","width":"20%"});
var _2b=AJS.IMG({"src":this.img_close});
AJS.AEV(_2b,"click",GB_hide);
var _2c=AJS.TD({"class":"close","width":"40%"},_2b);
var _2d=AJS.TBODY(AJS.TR(_29,_2a,_2c));
var _2e=AJS.TABLE({"cellspacing":"0","cellpadding":0,"border":0},_2d);
AJS.ACN(_28,_2e);
if(this.fullscreen){
AJS.AEV(window,"scroll",AJS.$b(this.setWindowPosition,this));
}else{
AJS.AEV(window,"scroll",AJS.$b(this._setHeaderPos,this));
}
},setFrameSize:function(){
var _2f=this.overlay.offsetWidth;
var _30=AJS.getWindowSize();
if(this.fullscreen){
this.width=_2f-40;
this.height=_30.h-80;
}
AJS.setWidth(this.iframe,this.width);
AJS.setHeight(this.iframe,this.height);
AJS.setWidth(this.header,_2f);
},_setHeaderPos:function(){
AJS.setTop(this.header,AJS.getScrollTop()+10);
},setWindowPosition:function(){
var _31=this.overlay.offsetWidth;
var _32=AJS.getWindowSize();
AJS.setLeft(this.g_window,((_31-50-this.width)/2));
var _33=AJS.getScrollTop()+55;
if(!this.center_win){
AJS.setTop(this.g_window,_33);
}else{
var fl=((_32.h-this.height)/2)+20+AJS.getScrollTop();
if(fl<0){
fl=0;
}
if(_33>fl){
fl=_33;
}
AJS.setTop(this.g_window,fl);
}
this._setHeaderPos();
},onHide:function(){
AJS.removeElement(this.header);
AJS.removeClass(this.g_window,"GB_Gallery");
},onShow:function(){
if(this.use_fx){
AJS.fx.fadeIn(this.header,{to:1});
}else{
AJS.setOpacity(this.header,1);
}
}});
AJS.preloadImages(GB_ROOT_DIR+"g_close.gif");
GB_showFullScreenSet=function(set,_36,_37){
var _38={type:"page",fullscreen:true,center_win:false};
var _39=new GB_Sets(_38,set);
_39.addCallback(_37);
_39.showSet(_36-1);
return false;
};
GB_showImageSet=function(set,_3b,_3c){
var _3d={type:"image",fullscreen:false,center_win:true,width:300,height:300};
var _3e=new GB_Sets(_3d,set);
_3e.addCallback(_3c);
_3e.showSet(_3b-1);
return false;
};
GB_Sets=GB_Gallery.extend({init:function(_3f,set){
this.parent(_3f);
if(!this.img_next){
this.img_next=this.root_dir+"next.gif";
}
if(!this.img_prev){
this.img_prev=this.root_dir+"prev.gif";
}
this.current_set=set;
},showSet:function(_41){
this.current_index=_41;
var _42=this.current_set[this.current_index];
this.show(_42.url);
this._setCaption(_42.caption);
this.btn_prev=AJS.IMG({"class":"left",src:this.img_prev});
this.btn_next=AJS.IMG({"class":"right",src:this.img_next});
AJS.AEV(this.btn_prev,"click",AJS.$b(this.switchPrev,this));
AJS.AEV(this.btn_next,"click",AJS.$b(this.switchNext,this));
GB_STATUS=AJS.SPAN({"class":"GB_navStatus"});
AJS.ACN(AJS.$("GB_middle"),this.btn_prev,GB_STATUS,this.btn_next);
this.updateStatus();
},updateStatus:function(){
AJS.setHTML(GB_STATUS,(this.current_index+1)+" / "+this.current_set.length);
if(this.current_index==0){
AJS.addClass(this.btn_prev,"disabled");
}else{
AJS.removeClass(this.btn_prev,"disabled");
}
if(this.current_index==this.current_set.length-1){
AJS.addClass(this.btn_next,"disabled");
}else{
AJS.removeClass(this.btn_next,"disabled");
}
},_setCaption:function(_43){
AJS.setHTML(AJS.$("GB_caption"),_43);
},updateFrame:function(){
var _44=this.current_set[this.current_index];
this._setCaption(_44.caption);
this.url=_44.url;
this.startLoading();
},switchPrev:function(){
if(this.current_index!=0){
this.current_index--;
this.updateFrame();
this.updateStatus();
}
},switchNext:function(){
if(this.current_index!=this.current_set.length-1){
this.current_index++;
this.updateFrame();
this.updateStatus();
}
}});
AJS.AEV(window,"load",function(){
AJS.preloadImages(GB_ROOT_DIR+"next.gif",GB_ROOT_DIR+"prev.gif");
});
GB_show=function(_45,url,_47,_48,_49){
var _4a={caption:_45,height:_47||500,width:_48||500,fullscreen:false,callback_fn:_49};
var win=new GB_Window(_4a);
return win.show(url);
};
GB_showCenter=function(_4c,url,_4e,_4f,_50){
var _51={caption:_4c,center_win:true,height:_4e||500,width:_4f||500,fullscreen:false,callback_fn:_50};
var win=new GB_Window(_51);
return win.show(url);
};
GB_showFullScreen=function(_53,url,_55){
var _56={caption:_53,fullscreen:true,callback_fn:_55};
var win=new GB_Window(_56);
return win.show(url);
};
GB_Window=GreyBox.extend({init:function(_58){
this.parent({});
this.img_header=this.root_dir+"header_bg.gif";
this.img_close=this.root_dir+"w_close.gif";
this.show_close_img=true;
AJS.update(this,_58);
this.addCallback(this.callback_fn);
},initHook:function(){
AJS.addClass(this.g_window,"GB_Window");
this.header=AJS.TABLE({"class":"header"});
this.header.style.backgroundImage="url("+this.img_header+")";
var _59=AJS.TD({"class":"caption"},this.caption);
var _5a=AJS.TD({"class":"close"});
if(this.show_close_img){
var _5b=AJS.IMG({"src":this.img_close});
var _5c=AJS.SPAN("Close");
var btn=AJS.DIV(_5b,_5c);
AJS.AEV([_5b,_5c],"mouseover",function(){
AJS.addClass(_5c,"on");
});
AJS.AEV([_5b,_5c],"mouseout",function(){
AJS.removeClass(_5c,"on");
});
AJS.AEV([_5b,_5c],"mousedown",function(){
AJS.addClass(_5c,"click");
});
AJS.AEV([_5b,_5c],"mouseup",function(){
AJS.removeClass(_5c,"click");
});
AJS.AEV([_5b,_5c],"click",GB_hide);
AJS.ACN(_5a,btn);
}
tbody_header=AJS.TBODY();
AJS.ACN(tbody_header,AJS.TR(_59,_5a));
AJS.ACN(this.header,tbody_header);
AJS.ACN(this.top_cnt,this.header);
if(this.fullscreen){
AJS.AEV(window,"scroll",AJS.$b(this.setWindowPosition,this));
}
},setFrameSize:function(){
if(this.fullscreen){
var _5e=AJS.getWindowSize();
overlay_h=_5e.h;
this.width=Math.round(this.overlay.offsetWidth-(this.overlay.offsetWidth/100)*10);
this.height=Math.round(overlay_h-(overlay_h/100)*10);
}
AJS.setWidth(this.header,this.width+6);
AJS.setWidth(this.iframe,this.width);
AJS.setHeight(this.iframe,this.height);
},setWindowPosition:function(){
var _5f=AJS.getWindowSize();
AJS.setLeft(this.g_window,((_5f.w-this.width)/2)-13);
if(!this.center_win){
AJS.setTop(this.g_window,AJS.getScrollTop());
}else{
var fl=((_5f.h-this.height)/2)-20+AJS.getScrollTop();
if(fl<0){
fl=0;
}
AJS.setTop(this.g_window,fl);
}
}});
AJS.preloadImages(GB_ROOT_DIR+"w_close.gif",GB_ROOT_DIR+"header_bg.gif");


script_loaded=true;

var Validator = Class.create();

Validator.prototype = {
	initialize : function(className, error, test, options) {
		if(typeof test == 'function'){
			this.options = $H(options);
			this._test = test;
		} else {
			this.options = $H(test);
			this._test = function(){return true};
		}
		this.error = error || 'Validation failed.';
		this.className = className;
	},
	test : function(v, elm) {
		return (this._test(v,elm) && this.options.all(function(p){
			return Validator.methods[p.key] ? Validator.methods[p.key](v,elm,p.value) : true;
		}));
	}
}
Validator.methods = {
	pattern : function(v,elm,opt) {return Validation.get('IsEmpty').test(v) || opt.test(v)},
	minLength : function(v,elm,opt) {return v.length >= opt},
	maxLength : function(v,elm,opt) {return v.length <= opt},
	min : function(v,elm,opt) {return v >= parseFloat(opt)}, 
	max : function(v,elm,opt) {return v <= parseFloat(opt)},
	notOneOf : function(v,elm,opt) {return $A(opt).all(function(value) {
		return v != value;
	})},
	oneOf : function(v,elm,opt) {return $A(opt).any(function(value) {
		return v == value;
	})},
	is : function(v,elm,opt) {return v == opt},
	isNot : function(v,elm,opt) {return v != opt},
	equalToField : function(v,elm,opt) {return v == $F(opt)},
	notEqualToField : function(v,elm,opt) {return v != $F(opt)},
	include : function(v,elm,opt) {return $A(opt).all(function(value) {
		return Validation.get(value).test(v,elm);
	})}
}

var Validation = Class.create();

Validation.prototype = {
	initialize : function(form, options){
		this.options = Object.extend({
			onSubmit : true,
			stopOnFirst : false,
			immediate : false,
			focusOnError : true,
			useTitles : false,
			onFormValidate : function(result, form) {},
			onElementValidate : function(result, elm) {}
		}, options || {});
		this.form = $(form);
		if(this.options.onSubmit) Event.observe(this.form,'submit',this.onSubmit.bind(this),false);
		if(this.options.immediate) {
			var useTitles = this.options.useTitles;
			var callback = this.options.onElementValidate;
			Form.getElements(this.form).each(function(input) { // Thanks Mike!
				Event.observe(input, 'blur', function(ev) { Validation.validate(Event.element(ev),{useTitle : useTitles, onElementValidate : callback}); });
			});
		}
	},
	onSubmit :  function(ev){
		if(!this.validate()) Event.stop(ev);
	},
	validate : function() {
		
		var result = false;
		var useTitles = this.options.useTitles;
		var callback = this.options.onElementValidate;
		if(this.options.stopOnFirst) {
			result = Form.getElements(this.form).all(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); });
		} else {
			result = Form.getElements(this.form).collect(function(elm) { return Validation.validate(elm,{useTitle : useTitles, onElementValidate : callback}); }).all();
		}
		if(!result && this.options.focusOnError) {
			Form.getElements(this.form).findAll(function(elm){return $(elm).hasClassName('validation-failed')}).first().focus()
		}
		this.options.onFormValidate(result, this.form);
		return result;
	},
	reset : function() {
		Form.getElements(this.form).each(Validation.reset);
	}
}

Object.extend(Validation, {
	validate : function(elm, options){
		options = Object.extend({
			useTitle : false,
			onElementValidate : function(result, elm) {}
		}, options || {});
		elm = $(elm);
		var cn = elm.classNames();
		return result = cn.all(function(value) {
			var test = Validation.test(value,elm,options.useTitle);
			
			options.onElementValidate(test, elm);
			return test;
		});
	},
	test : function(name, elm, useTitle) {
		var v = Validation.get(name);
		var prop = '__advice'+name.camelize();
		try {
		if(Validation.isVisible(elm) && !v.test($F(elm), elm)) {
			if(!elm[prop]) {
				var advice = Validation.getAdvice(name, elm);
				if(advice == null) {
					var errorMsg = useTitle ? ((elm && elm.title) ? elm.title : v.error) : v.error;
					advice = '<div class="validation-advice" id="advice-' + name + '-' + Validation.getElmID(elm) +'" style="display:none">' + errorMsg + '</div>'
					switch (elm.type.toLowerCase()) {
						case 'checkbox':
						case 'radio':
							var p = elm.parentNode;
							if(p) {
								new Insertion.Bottom(p, advice);
							} else {
								new Insertion.After(elm, advice);
							}
							break;
						default:
							new Insertion.After(elm, advice);
				    }
					advice = Validation.getAdvice(name, elm);
				}
				if(typeof Effect == 'undefined') {
					advice.style.display = 'block';
				} else {
					new Effect.Appear(advice, {duration : 1 });
				}
			}
			elm[prop] = true;
			elm.removeClassName('validation-passed');
			elm.addClassName('validation-failed');
			return false;
		} else {
			var advice = Validation.getAdvice(name, elm);
			if(advice != null) advice.hide();
			elm[prop] = '';
			elm.removeClassName('validation-failed');
			elm.addClassName('validation-passed');
			return true;
		}
		} catch(e) {
			throw(e)
		}
	},
	isVisible : function(elm) {
		while(elm.tagName != 'BODY') {
			if(!$(elm).visible()) return false;
			elm = elm.parentNode;
		}
		return true;
	},
	getAdvice : function(name, elm) {
		return $('advice-' + name + '-' + Validation.getElmID(elm)) || $('advice-' + Validation.getElmID(elm));
	},
	getElmID : function(elm) {
		return elm.id ? elm.id : elm.name;
	},
	reset : function(elm) {
		elm = $(elm);
		var cn = elm.classNames();
		cn.each(function(value) {
			var prop = '__advice'+value.camelize();
			if(elm[prop]) {
				var advice = Validation.getAdvice(value, elm);
				advice.hide();
				elm[prop] = '';
			}
			elm.removeClassName('validation-failed');
			elm.removeClassName('validation-passed');
		});
	},
	add : function(className, error, test, options) {
		var nv = {};
		nv[className] = new Validator(className, error, test, options);
		Object.extend(Validation.methods, nv);
	},
	addAllThese : function(validators) {
		var nv = {};
		$A(validators).each(function(value) {
				nv[value[0]] = new Validator(value[0], value[1], value[2], (value.length > 3 ? value[3] : {}));
			});
		Object.extend(Validation.methods, nv);
	},
	get : function(name) {
		return  Validation.methods[name] ? Validation.methods[name] : Validation.methods['_LikeNoIDIEverSaw_'];
	},
	methods : {
		'_LikeNoIDIEverSaw_' : new Validator('_LikeNoIDIEverSaw_','',{})
	}
});

Validation.add('IsEmpty', '', function(v) {
				return  ((v == null) || (v.length == 0)); // || /^\s+$/.test(v));
			});








Validation.addAllThese([
	['required', 'This is a required field.', function(v) {
				return !Validation.get('IsEmpty').test(v);
			}],
	['zipInState', 'Zip Code Invalid for this State.', function(v) {
			    var state = $F('State');										   
				return  checkIllegalZip(v,state);
			}],
	['validate-number', 'Please enter a valid number in this field.', function(v) {
				return Validation.get('IsEmpty').test(v) || (!isNaN(v) && !/^\s+$/.test(v));
			}],
	['validate-digits', 'Please use numbers only in this field. please avoid spaces or other characters such as dots or commas.', function(v) {
				return Validation.get('IsEmpty').test(v) ||  !/[^\d]/.test(v);
			}],
	['validate-alpha', 'Please use letters only (a-z) in this field.', function (v) {
				return Validation.get('IsEmpty').test(v) ||  /^[a-zA-Z]+$/.test(v)
			}],
	['validate-alphanum', 'Please use only letters (a-z) or numbers (0-9) only in this field. No spaces or other characters are allowed.', function(v) {
				return Validation.get('IsEmpty').test(v) ||  !/\W/.test(v)
			}],
	['validate-date', 'Please enter a valid date.', function(v) {
				var test = new Date(v);
				return Validation.get('IsEmpty').test(v) || !isNaN(test);
			}],
	['validate-email', 'Please enter a valid email address. For example fred@domain.com .', function (v) {
				return Validation.get('IsEmpty').test(v) || /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/.test(v)
			}],
	['validate-url', 'Please enter a valid URL.', function (v) {
				return Validation.get('IsEmpty').test(v) || /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(v)
			}],
	['validate-date-au', 'Please use this date format: dd/mm/yyyy. For example 17/03/2006 for the 17th of March, 2006.', function(v) {
				if(Validation.get('IsEmpty').test(v)) return true;
				var regex = /^(\d{2})\/(\d{2})\/(\d{4})$/;
				if(!regex.test(v)) return false;
				var d = new Date(v.replace(regex, '$2/$1/$3'));
				return ( parseInt(RegExp.$2, 10) == (1+d.getMonth()) ) && 
							(parseInt(RegExp.$1, 10) == d.getDate()) && 
							(parseInt(RegExp.$3, 10) == d.getFullYear() );
			}],
	['validate-currency-dollar', 'Please enter a valid $ amount. For example $100.00 .', function(v) {
				// [$]1[##][,###]+[.##]
				// [$]1###+[.##]
				// [$]0.##
				// [$].##
				return Validation.get('IsEmpty').test(v) ||  /^\$?\-?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}\d*(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/.test(v)
			}],
	['validate-selection', 'Please make a selection', function(v,elm){
				return elm.options ? elm.selectedIndex > 0 : !Validation.get('IsEmpty').test(v);
			}],
	['validate-one-required', 'Please select one of the above options.', function (v,elm) {
				var p = elm.parentNode;
				var options = p.getElementsByTagName('INPUT');
				return $A(options).any(function(elm) {
					return $F(elm);
				});
			}]
]);

/*

Recode version of fvlogger v1.0, (c) 2005 davidfmiller|http://www.fivevoltlogic.com/code/fvlogger/ : Client side only logging functions
Version 2.0 K.Corey: allows for server side logging via ajax w/ response info
*/
var FVLOGGER_VERSION = '2.0';
var FVL_LOG_ON = true;

// all logging statements that whose level is greater than or equal to FVL_DEFAULT_LOG_LEVEL will be processed;
// all others will be ignored
var FVL_DEFAULT_LOG_LEVEL = FVL_DEBUG;

// reporting id 
var FVL_LOG_ID = 'logger';

// wrapper element
var FVL_LOG_ELEMENT = 'p';

// constants for logging levels
var FVL_DEBUG = 0;
var FVL_INFO = 1;
var FVL_WARN = 2;
var FVL_ERROR = 3;
var FVL_FATAL = 4;

// the css classes that will be applied to the logging elements
var FVL_LOG_CLASSES = new Array("debug","info","warn","error","fatal");


// retrieves the element whose id is equal to FVL_LOG_ID
function getLogger(id) {
	if (arguments.length == 0) { id = FVL_LOG_ID; }
	return $(id);
}

function showDebug() { FVL_showMessages(FVL_DEBUG); }
function showInfo() { FVL_showMessages(FVL_INFO); }
function showWarn() { FVL_showMessages(FVL_WARN); }
function showError() { FVL_showMessages(FVL_ERROR); }
function showFatal() { FVL_showMessages(FVL_FATAL); }
function showAll() { FVL_showMessages(); }
function toggleDiv(){$('logger').toggle();}

// removes all logging information from the logging element
function eraseLog(ask) {
	var debug = getLogger();
	if (! debug) { return false; }

	if (ask && ! confirm("Are you sure you wish to erase the log?")) {
		return false;
	}

	var ps = debug.getElementsByTagName(FVL_LOG_ELEMENT);
	var length = ps.length;
	for (var i = 0; i < length; i++) { debug.removeChild(ps[length - i - 1]); }
	return true;
}

function debug(message) { FVL_log("" + message, FVL_DEBUG); }
function warn(message) { FVL_log("" + message, FVL_WARN); }
function info(message) { FVL_log("" + message, FVL_INFO); }
function error(message) { FVL_log("" + message, FVL_ERROR);}
function fatal(message) { FVL_log("" + message, FVL_FATAL);}

function windowError(message, url, line) {
	FVL_log('Error on line ' + line + ' of document ' + url + ': ' + message, FVL_FATAL);
	return true; //
}

// only override the window's error handler if logging is on
if (FVL_LOG_ON) {
	window.onerror = windowError;
}

// 
function FVL_showMessages(level, hideOthers) {

//	alert('showing ' + level);



	var showAll = false;
	// if no level has been specified, use the default
	if (arguments.length == 0) { level = FVL_DEFAULT_LOG_LEVEL; showAll = true; }
	if (arguments.length < 2) { hideOthers = true; }

	// retrieve the element and current statements
	var debug = getLogger();
	if (! debug) { return false; }
	var ps = debug.getElementsByTagName("p");
	if (ps.length == 0) { return true; }

	// get the number of nodes in the list
	var l = ps.length; 

	// get the class name for the specified level
	var lookup = FVL_LOG_CLASSES[level]; 

	// loop through all logging statements/<p> elements...
	for (var i = l - 1; i >= 0; i--) {

		// hide all elements by default, if specified
		if (hideOthers) { hide(ps[i]); } 

		// get the class name for this <p>
		var c = getNodeClass(ps[i]);
//		alert(c);
//		alert("Node #" + i + "'s class is:" + c);
		if (c && c.indexOf(lookup) > -1 || showAll) { show(ps[i]); } 
	}
}



// appends a statement to the logging element if the threshold level is exceeded
function FVL_log(message, level) {

	// check to make sure logging is turned on
	if (! FVL_LOG_ON) { return false; } 

	// retrieve the infrastructure
	if (arguments.length == 1) { level = FVL_INFO;}
	if (level < FVL_DEFAULT_LOG_LEVEL) { return false; }
	var div = getLogger();
	if (! div) { return false; }

	// append the statement
	var p = document.createElement(FVL_LOG_ELEMENT);

	// this is a hack work around a bug in ie
	if (p.getAttributeNode("class")) {
		for (var i = 0; i < p.attributes.length; i++) {
			if (p.attributes[i].name.toUpperCase() == 'CLASS') {
				p.attributes[i].value = FVL_LOG_CLASSES[level];
			}
		}
	} else {
		p.setAttribute("class", FVL_LOG_CLASSES[level]);
	}
	
	var digital = new Date();
	var hours = digital.getHours();
	var minutes = digital.getMinutes();
	var seconds = digital.getSeconds();
	var amOrPm = "AM";
		if (hours > 11) amOrPm = "PM";
		if (hours > 12) hours = hours - 12;
		if (hours == 0) hours = 12;
		if (minutes <= 9) minutes = "0" + minutes;
		if (seconds <= 9) seconds = "0" + seconds;
	dispTime = hours + ":" + minutes + ":" + seconds + " " + amOrPm;

	var text = document.createTextNode('['+dispTime+']  '+ message);
	p.appendChild(text);
	div.appendChild(p);

	//$('logStart').insert(p, 'before');
	//new insertion is depriciated. change for insert (once insert actually works right)

	new Insertion.Top('logStart', p);
	
	return true;
}

// show a node
function show(target) {
	target.style.display = "";
	return true;
}

// hide a node
function hide(target) {
	target.style.display = "none";
	return true;
}

// returns the class attribute of a node
function getNodeClass(obj) {
	var result = false;

	if (obj.getAttributeNode("class")) {
		result = obj.attributes.getNamedItem("class").value;
	}
	return result;
}

function keyHandler(e)
{
    var pressedKey;
    
    if (document.all)    { e = window.event; }
	if (document.layers || e.which) { pressedKey = e.which; }
    if (document.all)    { pressedKey = e.keyCode; }

	pressedCharacter = String.fromCharCode(pressedKey);
    //alert(' Character = ' + pressedCharacter );
	//alert(' Decimal value = ' + pressedKey );
	if (pressedKey==126){ $('loggercontainer').toggle();}
}


//Load logger and check for the shift+~ combo
document.observe("dom:loaded",function(){
	$$('#logger').invoke('hide');
	$$('#loggercontainer').invoke('hide');
	document.onkeypress = keyHandler;
});



// script.aculo.us builder.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/

var Builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();
    
    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;
      
    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];
    
    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);
    
    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1]) 
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        } 

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return element;
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e)
        else
          if(Builder._isStringOrNumber(e))
            element.appendChild(Builder._text(e));
      });
    } else
      if(Builder._isStringOrNumber(children))
        element.appendChild(Builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    $(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) { 
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
  
    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
  
    tags.each( function(tag){ 
      scope[tag] = function() { 
        return Builder.node.apply(Builder, [tag].concat($A(arguments)));  
      } 
    });
  }
}


// script.aculo.us effects.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007

// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// Contributors:
//  Justin Palmer (http://encytemedia.com/)
//  Mark Pilgrim (http://diveintomark.org/)
//  Martin Bialasinki
// 
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/ 

// converts rgb() and #xxx to #xxxxxx format,  
// returns self (or first argument) if not convertable  
String.prototype.parseColor = function() {  
  var color = '#';
  if (this.slice(0,4) == 'rgb(') {  
    var cols = this.slice(4,this.length-1).split(',');  
    var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);  
  } else {  
    if (this.slice(0,1) == '#') {  
      if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();  
      if (this.length==7) color = this.toLowerCase();  
    }  
  }  
  return (color.length==7 ? color : (arguments[0] || this));  
};

/*--------------------------------------------------------------------------*/

Element.collectTextNodes = function(element) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
  }).flatten().join('');
};

Element.collectTextNodesIgnoreClass = function(element, className) {  
  return $A($(element).childNodes).collect( function(node) {
    return (node.nodeType==3 ? node.nodeValue : 
      ((node.hasChildNodes() && !Element.hasClassName(node,className)) ? 
        Element.collectTextNodesIgnoreClass(node, className) : ''));
  }).flatten().join('');
};

Element.setContentZoom = function(element, percent) {
  element = $(element);  
  element.setStyle({fontSize: (percent/100) + 'em'});   
  if (Prototype.Browser.WebKit) window.scrollBy(0,0);
  return element;
};

Element.getInlineOpacity = function(element){
  return $(element).style.opacity || '';
};

Element.forceRerendering = function(element) {
  try {
    element = $(element);
    var n = document.createTextNode(' ');
    element.appendChild(n);
    element.removeChild(n);
  } catch(e) { }
};

/*--------------------------------------------------------------------------*/

var Effect = {
  _elementDoesNotExistError: {
    name: 'ElementDoesNotExistError',
    message: 'The specified DOM element does not exist, but is required for this effect to operate'
  },
  Transitions: {
    linear: Prototype.K,
    sinoidal: function(pos) {
      return (-Math.cos(pos*Math.PI)/2) + 0.5;
    },
    reverse: function(pos) {
      return 1-pos;
    },
    flicker: function(pos) {
      var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
      return pos > 1 ? 1 : pos;
    },
    wobble: function(pos) {
      return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
    },
    pulse: function(pos, pulses) { 
      pulses = pulses || 5; 
      return (
        ((pos % (1/pulses)) * pulses).round() == 0 ? 
              ((pos * pulses * 2) - (pos * pulses * 2).floor()) : 
          1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
        );
    },
    spring: function(pos) { 
      return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6)); 
    },
    none: function(pos) {
      return 0;
    },
    full: function(pos) {
      return 1;
    }
  },
  DefaultOptions: {
    duration:   1.0,   // seconds
    fps:        100,   // 100= assume 66fps max.
    sync:       false, // true for combining
    from:       0.0,
    to:         1.0,
    delay:      0.0,
    queue:      'parallel'
  },
  tagifyText: function(element) {
    var tagifyStyle = 'position:relative';
    if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
    
    element = $(element);
    $A(element.childNodes).each( function(child) {
      if (child.nodeType==3) {
        child.nodeValue.toArray().each( function(character) {
          element.insertBefore(
            new Element('span', {style: tagifyStyle}).update(
              character == ' ' ? String.fromCharCode(160) : character), 
              child);
        });
        Element.remove(child);
      }
    });
  },
  multiple: function(element, effect) {
    var elements;
    if (((typeof element == 'object') || 
        Object.isFunction(element)) && 
       (element.length))
      elements = element;
    else
      elements = $(element).childNodes;
      
    var options = Object.extend({
      speed: 0.1,
      delay: 0.0
    }, arguments[2] || { });
    var masterDelay = options.delay;

    $A(elements).each( function(element, index) {
      new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
    });
  },
  PAIRS: {
    'slide':  ['SlideDown','SlideUp'],
    'blind':  ['BlindDown','BlindUp'],
    'appear': ['Appear','Fade']
  },
  toggle: function(element, effect) {
    element = $(element);
    effect = (effect || 'appear').toLowerCase();
    var options = Object.extend({
      queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
    }, arguments[2] || { });
    Effect[element.visible() ? 
      Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
  }
};

Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;

/* ------------- core effects ------------- */

Effect.ScopedQueue = Class.create(Enumerable, {
  initialize: function() {
    this.effects  = [];
    this.interval = null;    
  },
  _each: function(iterator) {
    this.effects._each(iterator);
  },
  add: function(effect) {
    var timestamp = new Date().getTime();
    
    var position = Object.isString(effect.options.queue) ? 
      effect.options.queue : effect.options.queue.position;
    
    switch(position) {
      case 'front':
        // move unstarted effects after this effect  
        this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
            e.startOn  += effect.finishOn;
            e.finishOn += effect.finishOn;
          });
        break;
      case 'with-last':
        timestamp = this.effects.pluck('startOn').max() || timestamp;
        break;
      case 'end':
        // start effect after last queued effect has finished
        timestamp = this.effects.pluck('finishOn').max() || timestamp;
        break;
    }
    
    effect.startOn  += timestamp;
    effect.finishOn += timestamp;

    if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
      this.effects.push(effect);
    
    if (!this.interval)
      this.interval = setInterval(this.loop.bind(this), 15);
  },
  remove: function(effect) {
    this.effects = this.effects.reject(function(e) { return e==effect });
    if (this.effects.length == 0) {
      clearInterval(this.interval);
      this.interval = null;
    }
  },
  loop: function() {
    var timePos = new Date().getTime();
    for(var i=0, len=this.effects.length;i<len;i++) 
      this.effects[i] && this.effects[i].loop(timePos);
  }
});

Effect.Queues = {
  instances: $H(),
  get: function(queueName) {
    if (!Object.isString(queueName)) return queueName;
    
    return this.instances.get(queueName) ||
      this.instances.set(queueName, new Effect.ScopedQueue());
  }
};
Effect.Queue = Effect.Queues.get('global');

Effect.Base = Class.create({
  position: null,
  start: function(options) {
    function codeForEvent(options,eventName){
      return (
        (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
        (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
      );
    }
    if (options && options.transition === false) options.transition = Effect.Transitions.linear;
    this.options      = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
    this.currentFrame = 0;
    this.state        = 'idle';
    this.startOn      = this.options.delay*1000;
    this.finishOn     = this.startOn+(this.options.duration*1000);
    this.fromToDelta  = this.options.to-this.options.from;
    this.totalTime    = this.finishOn-this.startOn;
    this.totalFrames  = this.options.fps*this.options.duration;
    
    eval('this.render = function(pos){ '+
      'if (this.state=="idle"){this.state="running";'+
      codeForEvent(this.options,'beforeSetup')+
      (this.setup ? 'this.setup();':'')+ 
      codeForEvent(this.options,'afterSetup')+
      '};if (this.state=="running"){'+
      'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
      'this.position=pos;'+
      codeForEvent(this.options,'beforeUpdate')+
      (this.update ? 'this.update(pos);':'')+
      codeForEvent(this.options,'afterUpdate')+
      '}}');
    
    this.event('beforeStart');
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' : this.options.queue.scope).add(this);
  },
  loop: function(timePos) {
    if (timePos >= this.startOn) {
      if (timePos >= this.finishOn) {
        this.render(1.0);
        this.cancel();
        this.event('beforeFinish');
        if (this.finish) this.finish(); 
        this.event('afterFinish');
        return;  
      }
      var pos   = (timePos - this.startOn) / this.totalTime,
          frame = (pos * this.totalFrames).round();
      if (frame > this.currentFrame) {
        this.render(pos);
        this.currentFrame = frame;
      }
    }
  },
  cancel: function() {
    if (!this.options.sync)
      Effect.Queues.get(Object.isString(this.options.queue) ? 
        'global' : this.options.queue.scope).remove(this);
    this.state = 'finished';
  },
  event: function(eventName) {
    if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
    if (this.options[eventName]) this.options[eventName](this);
  },
  inspect: function() {
    var data = $H();
    for(property in this)
      if (!Object.isFunction(this[property])) data.set(property, this[property]);
    return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
  }
});

Effect.Parallel = Class.create(Effect.Base, {
  initialize: function(effects) {
    this.effects = effects || [];
    this.start(arguments[1]);
  },
  update: function(position) {
    this.effects.invoke('render', position);
  },
  finish: function(position) {
    this.effects.each( function(effect) {
      effect.render(1.0);
      effect.cancel();
      effect.event('beforeFinish');
      if (effect.finish) effect.finish(position);
      effect.event('afterFinish');
    });
  }
});

Effect.Tween = Class.create(Effect.Base, {
  initialize: function(object, from, to) {
    object = Object.isString(object) ? $(object) : object;
    var args = $A(arguments), method = args.last(), 
      options = args.length == 5 ? args[3] : null;
    this.method = Object.isFunction(method) ? method.bind(object) :
      Object.isFunction(object[method]) ? object[method].bind(object) : 
      function(value) { object[method] = value };
    this.start(Object.extend({ from: from, to: to }, options || { }));
  },
  update: function(position) {
    this.method(position);
  }
});

Effect.Event = Class.create(Effect.Base, {
  initialize: function() {
    this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
  },
  update: Prototype.emptyFunction
});

Effect.Opacity = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    // make this work on IE on elements without 'layout'
    if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
      this.element.setStyle({zoom: 1});
    var options = Object.extend({
      from: this.element.getOpacity() || 0.0,
      to:   1.0
    }, arguments[1] || { });
    this.start(options);
  },
  update: function(position) {
    this.element.setOpacity(position);
  }
});

Effect.Move = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      x:    0,
      y:    0,
      mode: 'relative'
    }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    this.element.makePositioned();
    this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
    this.originalTop  = parseFloat(this.element.getStyle('top')  || '0');
    if (this.options.mode == 'absolute') {
      this.options.x = this.options.x - this.originalLeft;
      this.options.y = this.options.y - this.originalTop;
    }
  },
  update: function(position) {
    this.element.setStyle({
      left: (this.options.x  * position + this.originalLeft).round() + 'px',
      top:  (this.options.y  * position + this.originalTop).round()  + 'px'
    });
  }
});

// for backwards compatibility
Effect.MoveBy = function(element, toTop, toLeft) {
  return new Effect.Move(element, 
    Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
};

Effect.Scale = Class.create(Effect.Base, {
  initialize: function(element, percent) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      scaleX: true,
      scaleY: true,
      scaleContent: true,
      scaleFromCenter: false,
      scaleMode: 'box',        // 'box' or 'contents' or { } with provided values
      scaleFrom: 100.0,
      scaleTo:   percent
    }, arguments[2] || { });
    this.start(options);
  },
  setup: function() {
    this.restoreAfterFinish = this.options.restoreAfterFinish || false;
    this.elementPositioning = this.element.getStyle('position');
    
    this.originalStyle = { };
    ['top','left','width','height','fontSize'].each( function(k) {
      this.originalStyle[k] = this.element.style[k];
    }.bind(this));
      
    this.originalTop  = this.element.offsetTop;
    this.originalLeft = this.element.offsetLeft;
    
    var fontSize = this.element.getStyle('font-size') || '100%';
    ['em','px','%','pt'].each( function(fontSizeType) {
      if (fontSize.indexOf(fontSizeType)>0) {
        this.fontSize     = parseFloat(fontSize);
        this.fontSizeType = fontSizeType;
      }
    }.bind(this));
    
    this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
    
    this.dims = null;
    if (this.options.scaleMode=='box')
      this.dims = [this.element.offsetHeight, this.element.offsetWidth];
    if (/^content/.test(this.options.scaleMode))
      this.dims = [this.element.scrollHeight, this.element.scrollWidth];
    if (!this.dims)
      this.dims = [this.options.scaleMode.originalHeight,
                   this.options.scaleMode.originalWidth];
  },
  update: function(position) {
    var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
    if (this.options.scaleContent && this.fontSize)
      this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
    this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
  },
  finish: function(position) {
    if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
  },
  setDimensions: function(height, width) {
    var d = { };
    if (this.options.scaleX) d.width = width.round() + 'px';
    if (this.options.scaleY) d.height = height.round() + 'px';
    if (this.options.scaleFromCenter) {
      var topd  = (height - this.dims[0])/2;
      var leftd = (width  - this.dims[1])/2;
      if (this.elementPositioning == 'absolute') {
        if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
        if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
      } else {
        if (this.options.scaleY) d.top = -topd + 'px';
        if (this.options.scaleX) d.left = -leftd + 'px';
      }
    }
    this.element.setStyle(d);
  }
});

Effect.Highlight = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
    this.start(options);
  },
  setup: function() {
    // Prevent executing on elements not in the layout flow
    if (this.element.getStyle('display')=='none') { this.cancel(); return; }
    // Disable background image during the effect
    this.oldStyle = { };
    if (!this.options.keepBackgroundImage) {
      this.oldStyle.backgroundImage = this.element.getStyle('background-image');
      this.element.setStyle({backgroundImage: 'none'});
    }
    if (!this.options.endcolor)
      this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
    if (!this.options.restorecolor)
      this.options.restorecolor = this.element.getStyle('background-color');
    // init color calculations
    this._base  = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
    this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
  },
  update: function(position) {
    this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
      return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
  },
  finish: function() {
    this.element.setStyle(Object.extend(this.oldStyle, {
      backgroundColor: this.options.restorecolor
    }));
  }
});

Effect.ScrollTo = function(element) {
  var options = arguments[1] || { },
    scrollOffsets = document.viewport.getScrollOffsets(),
    elementOffsets = $(element).cumulativeOffset(),
    max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();  

  if (options.offset) elementOffsets[1] += options.offset;

  return new Effect.Tween(null,
    scrollOffsets.top,
    elementOffsets[1] > max ? max : elementOffsets[1],
    options,
    function(p){ scrollTo(scrollOffsets.left, p.round()) }
  );
};

/* ------------- combination effects ------------- */

Effect.Fade = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  var options = Object.extend({
    from: element.getOpacity() || 1.0,
    to:   0.0,
    afterFinishInternal: function(effect) { 
      if (effect.options.to!=0) return;
      effect.element.hide().setStyle({opacity: oldOpacity}); 
    }
  }, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Appear = function(element) {
  element = $(element);
  var options = Object.extend({
  from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
  to:   1.0,
  // force Safari to render floated elements properly
  afterFinishInternal: function(effect) {
    effect.element.forceRerendering();
  },
  beforeSetup: function(effect) {
    effect.element.setOpacity(effect.options.from).show(); 
  }}, arguments[1] || { });
  return new Effect.Opacity(element,options);
};

Effect.Puff = function(element) {
  element = $(element);
  var oldStyle = { 
    opacity: element.getInlineOpacity(), 
    position: element.getStyle('position'),
    top:  element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height
  };
  return new Effect.Parallel(
   [ new Effect.Scale(element, 200, 
      { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }), 
     new Effect.Opacity(element, { sync: true, to: 0.0 } ) ], 
     Object.extend({ duration: 1.0, 
      beforeSetupInternal: function(effect) {
        Position.absolutize(effect.effects[0].element)
      },
      afterFinishInternal: function(effect) {
         effect.effects[0].element.hide().setStyle(oldStyle); }
     }, arguments[1] || { })
   );
};

Effect.BlindUp = function(element) {
  element = $(element);
  element.makeClipping();
  return new Effect.Scale(element, 0,
    Object.extend({ scaleContent: false, 
      scaleX: false, 
      restoreAfterFinish: true,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping();
      } 
    }, arguments[1] || { })
  );
};

Effect.BlindDown = function(element) {
  element = $(element);
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false,
    scaleFrom: 0,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.undoClipping();
    }
  }, arguments[1] || { }));
};

Effect.SwitchOff = function(element) {
  element = $(element);
  var oldOpacity = element.getInlineOpacity();
  return new Effect.Appear(element, Object.extend({
    duration: 0.4,
    from: 0,
    transition: Effect.Transitions.flicker,
    afterFinishInternal: function(effect) {
      new Effect.Scale(effect.element, 1, { 
        duration: 0.3, scaleFromCenter: true,
        scaleX: false, scaleContent: false, restoreAfterFinish: true,
        beforeSetup: function(effect) { 
          effect.element.makePositioned().makeClipping();
        },
        afterFinishInternal: function(effect) {
          effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
        }
      })
    }
  }, arguments[1] || { }));
};

Effect.DropOut = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left'),
    opacity: element.getInlineOpacity() };
  return new Effect.Parallel(
    [ new Effect.Move(element, {x: 0, y: 100, sync: true }), 
      new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
    Object.extend(
      { duration: 0.5,
        beforeSetup: function(effect) {
          effect.effects[0].element.makePositioned(); 
        },
        afterFinishInternal: function(effect) {
          effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
        } 
      }, arguments[1] || { }));
};

Effect.Shake = function(element) {
  element = $(element);
  var options = Object.extend({
    distance: 20,
    duration: 0.5
  }, arguments[1] || {});
  var distance = parseFloat(options.distance);
  var split = parseFloat(options.duration) / 10.0;
  var oldStyle = {
    top: element.getStyle('top'),
    left: element.getStyle('left') };
    return new Effect.Move(element,
      { x:  distance, y: 0, duration: split, afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x:  distance*2, y: 0, duration: split*2,  afterFinishInternal: function(effect) {
    new Effect.Move(effect.element,
      { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
        effect.element.undoPositioned().setStyle(oldStyle);
  }}) }}) }}) }}) }}) }});
};

Effect.SlideDown = function(element) {
  element = $(element).cleanWhitespace();
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, 100, Object.extend({ 
    scaleContent: false, 
    scaleX: false, 
    scaleFrom: window.opera ? 0 : 1,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().setStyle({height: '0px'}).show(); 
    },
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' }); 
    },
    afterFinishInternal: function(effect) {
      effect.element.undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
    }, arguments[1] || { })
  );
};

Effect.SlideUp = function(element) {
  element = $(element).cleanWhitespace();
  var oldInnerBottom = element.down().getStyle('bottom');
  var elementDimensions = element.getDimensions();
  return new Effect.Scale(element, window.opera ? 0 : 1,
   Object.extend({ scaleContent: false, 
    scaleX: false, 
    scaleMode: 'box',
    scaleFrom: 100,
    scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
    restoreAfterFinish: true,
    afterSetup: function(effect) {
      effect.element.makePositioned();
      effect.element.down().makePositioned();
      if (window.opera) effect.element.setStyle({top: ''});
      effect.element.makeClipping().show();
    },  
    afterUpdateInternal: function(effect) {
      effect.element.down().setStyle({bottom:
        (effect.dims[0] - effect.element.clientHeight) + 'px' });
    },
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping().undoPositioned();
      effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
    }
   }, arguments[1] || { })
  );
};

// Bug in opera makes the TD containing this element expand for a instance after finish 
Effect.Squish = function(element) {
  return new Effect.Scale(element, window.opera ? 1 : 0, { 
    restoreAfterFinish: true,
    beforeSetup: function(effect) {
      effect.element.makeClipping(); 
    },  
    afterFinishInternal: function(effect) {
      effect.element.hide().undoClipping(); 
    }
  });
};

Effect.Grow = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.full
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();    
  var initialMoveX, initialMoveY;
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      initialMoveX = initialMoveY = moveX = moveY = 0; 
      break;
    case 'top-right':
      initialMoveX = dims.width;
      initialMoveY = moveY = 0;
      moveX = -dims.width;
      break;
    case 'bottom-left':
      initialMoveX = moveX = 0;
      initialMoveY = dims.height;
      moveY = -dims.height;
      break;
    case 'bottom-right':
      initialMoveX = dims.width;
      initialMoveY = dims.height;
      moveX = -dims.width;
      moveY = -dims.height;
      break;
    case 'center':
      initialMoveX = dims.width / 2;
      initialMoveY = dims.height / 2;
      moveX = -dims.width / 2;
      moveY = -dims.height / 2;
      break;
  }
  
  return new Effect.Move(element, {
    x: initialMoveX,
    y: initialMoveY,
    duration: 0.01, 
    beforeSetup: function(effect) {
      effect.element.hide().makeClipping().makePositioned();
    },
    afterFinishInternal: function(effect) {
      new Effect.Parallel(
        [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
          new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
          new Effect.Scale(effect.element, 100, {
            scaleMode: { originalHeight: dims.height, originalWidth: dims.width }, 
            sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
        ], Object.extend({
             beforeSetup: function(effect) {
               effect.effects[0].element.setStyle({height: '0px'}).show(); 
             },
             afterFinishInternal: function(effect) {
               effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle); 
             }
           }, options)
      )
    }
  });
};

Effect.Shrink = function(element) {
  element = $(element);
  var options = Object.extend({
    direction: 'center',
    moveTransition: Effect.Transitions.sinoidal,
    scaleTransition: Effect.Transitions.sinoidal,
    opacityTransition: Effect.Transitions.none
  }, arguments[1] || { });
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    height: element.style.height,
    width: element.style.width,
    opacity: element.getInlineOpacity() };

  var dims = element.getDimensions();
  var moveX, moveY;
  
  switch (options.direction) {
    case 'top-left':
      moveX = moveY = 0;
      break;
    case 'top-right':
      moveX = dims.width;
      moveY = 0;
      break;
    case 'bottom-left':
      moveX = 0;
      moveY = dims.height;
      break;
    case 'bottom-right':
      moveX = dims.width;
      moveY = dims.height;
      break;
    case 'center':  
      moveX = dims.width / 2;
      moveY = dims.height / 2;
      break;
  }
  
  return new Effect.Parallel(
    [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
      new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
      new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
    ], Object.extend({            
         beforeStartInternal: function(effect) {
           effect.effects[0].element.makePositioned().makeClipping(); 
         },
         afterFinishInternal: function(effect) {
           effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
       }, options)
  );
};

Effect.Pulsate = function(element) {
  element = $(element);
  var options    = arguments[1] || { };
  var oldOpacity = element.getInlineOpacity();
  var transition = options.transition || Effect.Transitions.sinoidal;
  var reverser   = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
  reverser.bind(transition);
  return new Effect.Opacity(element, 
    Object.extend(Object.extend({  duration: 2.0, from: 0,
      afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
    }, options), {transition: reverser}));
};

Effect.Fold = function(element) {
  element = $(element);
  var oldStyle = {
    top: element.style.top,
    left: element.style.left,
    width: element.style.width,
    height: element.style.height };
  element.makeClipping();
  return new Effect.Scale(element, 5, Object.extend({   
    scaleContent: false,
    scaleX: false,
    afterFinishInternal: function(effect) {
    new Effect.Scale(element, 1, { 
      scaleContent: false, 
      scaleY: false,
      afterFinishInternal: function(effect) {
        effect.element.hide().undoClipping().setStyle(oldStyle);
      } });
  }}, arguments[1] || { }));
};

Effect.Morph = Class.create(Effect.Base, {
  initialize: function(element) {
    this.element = $(element);
    if (!this.element) throw(Effect._elementDoesNotExistError);
    var options = Object.extend({
      style: { }
    }, arguments[1] || { });
    
    if (!Object.isString(options.style)) this.style = $H(options.style);
    else {
      if (options.style.include(':'))
        this.style = options.style.parseStyle();
      else {
        this.element.addClassName(options.style);
        this.style = $H(this.element.getStyles());
        this.element.removeClassName(options.style);
        var css = this.element.getStyles();
        this.style = this.style.reject(function(style) {
          return style.value == css[style.key];
        });
        options.afterFinishInternal = function(effect) {
          effect.element.addClassName(effect.options.style);
          effect.transforms.each(function(transform) {
            effect.element.style[transform.style] = '';
          });
        }
      }
    }
    this.start(options);
  },
  
  setup: function(){
    function parseColor(color){
      if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
      color = color.parseColor();
      return $R(0,2).map(function(i){
        return parseInt( color.slice(i*2+1,i*2+3), 16 ) 
      });
    }
    this.transforms = this.style.map(function(pair){
      var property = pair[0], value = pair[1], unit = null;

      if (value.parseColor('#zzzzzz') != '#zzzzzz') {
        value = value.parseColor();
        unit  = 'color';
      } else if (property == 'opacity') {
        value = parseFloat(value);
        if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
          this.element.setStyle({zoom: 1});
      } else if (Element.CSS_LENGTH.test(value)) {
          var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
          value = parseFloat(components[1]);
          unit = (components.length == 3) ? components[2] : null;
      }

      var originalValue = this.element.getStyle(property);
      return { 
        style: property.camelize(), 
        originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0), 
        targetValue: unit=='color' ? parseColor(value) : value,
        unit: unit
      };
    }.bind(this)).reject(function(transform){
      return (
        (transform.originalValue == transform.targetValue) ||
        (
          transform.unit != 'color' &&
          (isNaN(transform.originalValue) || isNaN(transform.targetValue))
        )
      )
    });
  },
  update: function(position) {
    var style = { }, transform, i = this.transforms.length;
    while(i--)
      style[(transform = this.transforms[i]).style] = 
        transform.unit=='color' ? '#'+
          (Math.round(transform.originalValue[0]+
            (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
          (Math.round(transform.originalValue[1]+
            (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
          (Math.round(transform.originalValue[2]+
            (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
        (transform.originalValue +
          (transform.targetValue - transform.originalValue) * position).toFixed(3) + 
            (transform.unit === null ? '' : transform.unit);
    this.element.setStyle(style, true);
  }
});

Effect.Transform = Class.create({
  initialize: function(tracks){
    this.tracks  = [];
    this.options = arguments[1] || { };
    this.addTracks(tracks);
  },
  addTracks: function(tracks){
    tracks.each(function(track){
      track = $H(track);
      var data = track.values().first();
      this.tracks.push($H({
        ids:     track.keys().first(),
        effect:  Effect.Morph,
        options: { style: data }
      }));
    }.bind(this));
    return this;
  },
  play: function(){
    return new Effect.Parallel(
      this.tracks.map(function(track){
        var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
        var elements = [$(ids) || $$(ids)].flatten();
        return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
      }).flatten(),
      this.options
    );
  }
});

Element.CSS_PROPERTIES = $w(
  'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' + 
  'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
  'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
  'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
  'fontSize fontWeight height left letterSpacing lineHeight ' +
  'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
  'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
  'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
  'right textIndent top width wordSpacing zIndex');
  
Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;

String.__parseStyleElement = document.createElement('div');
String.prototype.parseStyle = function(){
  var style, styleRules = $H();
  if (Prototype.Browser.WebKit)
    style = new Element('div',{style:this}).style;
  else {
    String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
    style = String.__parseStyleElement.childNodes[0].style;
  }
  
  Element.CSS_PROPERTIES.each(function(property){
    if (style[property]) styleRules.set(property, style[property]); 
  });
  
  if (Prototype.Browser.IE && this.include('opacity'))
    styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);

  return styleRules;
};

if (document.defaultView && document.defaultView.getComputedStyle) {
  Element.getStyles = function(element) {
    var css = document.defaultView.getComputedStyle($(element), null);
    return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
      styles[property] = css[property];
      return styles;
    });
  };
} else {
  Element.getStyles = function(element) {
    element = $(element);
    var css = element.currentStyle, styles;
    styles = Element.CSS_PROPERTIES.inject({ }, function(hash, property) {
      hash.set(property, css[property]);
      return hash;
    });
    if (!styles.opacity) styles.set('opacity', element.getOpacity());
    return styles;
  };
};

Effect.Methods = {
  morph: function(element, style) {
    element = $(element);
    new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
    return element;
  },
  visualEffect: function(element, effect, options) {
    element = $(element)
    var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
    new Effect[klass](element, options);
    return element;
  },
  highlight: function(element, options) {
    element = $(element);
    new Effect.Highlight(element, options);
    return element;
  }
};

$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
  'pulsate shake puff squish switchOff dropOut').each(
  function(effect) { 
    Effect.Methods[effect] = function(element, options){
      element = $(element);
      Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
      return element;
    }
  }
);

$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each( 
  function(f) { Effect.Methods[f] = Element[f]; }
);

Element.addMethods(Effect.Methods);


		/**
          *     @desc: xmlLoader object
          *     @type: private
          *     @param: funcObject - xml parser function
          *     @param: object - jsControl object
          *     @param: async - sync/async mode (async by default)
          *     @param: rSeed - enable/disable random seed ( prevent IE caching)
		  *     @topic: 0
          */
function dtmlXMLLoaderObject(funcObject, dhtmlObject,async,rSeed){
	this.xmlDoc="";
	
	if (typeof(async) != "undefined")
		this.async = async;
	else this.async = true;

	this.onloadAction=funcObject||null;
	this.mainObject=dhtmlObject||null;
    this.waitCall=null;
	this.rSeed=rSeed||false;
	return this;
};
		/**
          *     @desc: xml loading handler
          *     @type: private
          *     @param: dtmlObject - xmlLoader object
		  *     @topic: 0
          */
	dtmlXMLLoaderObject.prototype.waitLoadFunction=function(dhtmlObject){ 
		var once=true;
		this.check=function (){
			if ((dhtmlObject)&&(dhtmlObject.onloadAction!=null)){
				if  ((!dhtmlObject.xmlDoc.readyState)||(dhtmlObject.xmlDoc.readyState == 4)){
					if (!once) return; once=false; //IE 5 fix
					dhtmlObject.onloadAction(dhtmlObject.mainObject,null,null,null,dhtmlObject);
                    if (dhtmlObject.waitCall) { dhtmlObject.waitCall(); dhtmlObject.waitCall=null; }
                    }
			}
		};
		return this.check;
	};

		/**
          *     @desc: return XML top node
		  *     @param: tagName - top XML node tag name (not used in IE, required for Safari and Mozilla)
          *     @type: private
		  *     @returns: top XML node
		  *     @topic: 0  
          */
	dtmlXMLLoaderObject.prototype.getXMLTopNode=function(tagName,oldObj){ 
			if (this.xmlDoc.responseXML)  { 
				var temp=this.xmlDoc.responseXML.getElementsByTagName(tagName);
				var z=temp[0];  
			}else
				var z=this.xmlDoc.documentElement;
			if (z){
				this._retry=false;
				return z;
				}

            if ((_isIE)&&(!this._retry)){    
                //fall back to MS.XMLDOM
                var xmlString=this.xmlDoc.responseText;
                var oldObj=this.xmlDoc;
                this._retry=true;
           			this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
           			this.xmlDoc.async=false;
            		this.xmlDoc["loadXM"+"L"](xmlString);

                    return this.getXMLTopNode(tagName,oldObj);
            }
            dhtmlxError.throwError("LoadXML","Incorrect XML",[(oldObj||this.xmlDoc),this.mainObject]);
			return document.createElement("DIV");
	};

		/**
          *     @desc: load XML from string
          *     @type: private
          *     @param: xmlString - xml string
		  *     @topic: 0  
          */
	dtmlXMLLoaderObject.prototype.loadXMLString=function(xmlString){
 	{
     try
	 {
		 var parser = new DOMParser();
		 this.xmlDoc = parser.parseFromString(xmlString,"text/xml");
	 }
	 catch(e){
		this.xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
		this.xmlDoc.async=this.async;
		this.xmlDoc["loadXM"+"L"](xmlString);
	 }
 	}
 	
	  this.onloadAction(this.mainObject,null,null,null,this);
      if (this.waitCall) { this.waitCall(); this.waitCall=null; }
	}
		/**
          *     @desc: load XML
          *     @type: private
          *     @param: filePath - xml file path
          *     @param: postMode - send POST request
          *     @param: postVars - list of vars for post request
		  *     @topic: 0
          */
	dtmlXMLLoaderObject.prototype.loadXML=function(filePath,postMode,postVars,rpc){
	 if (this.rSeed) filePath+=((filePath.indexOf("?")!=-1)?"&":"?")+"a_dhx_rSeed="+(new Date()).valueOf();
     this.filePath=filePath;	
      	
     if ((!_isIE)&&(window.XMLHttpRequest))
	 	this.xmlDoc = new XMLHttpRequest();
	else{

    		if (document.implementation && document.implementation.createDocument)
    		{
    			this.xmlDoc = document.implementation.createDocument("", "", null);
    			this.xmlDoc.onload = new this.waitLoadFunction(this);
				this.xmlDoc.load(filePath);
				return;
    		}
    		else
        			this.xmlDoc = new ActiveXObject("Microsoft.XMLHTTP");
	}

	
 	
	this.xmlDoc.open(postMode?"POST":"GET",filePath,this.async);
	if (rpc){
      	this.xmlDoc.setRequestHeader("User-Agent", "dhtmlxRPC v0.1 (" + navigator.userAgent + ")");
        this.xmlDoc.setRequestHeader("Content-type", "text/xml");
	} else
		if (postMode) this.xmlDoc.setRequestHeader('Content-type','application/x-www-form-urlencoded');
	this.xmlDoc.onreadystatechange=new this.waitLoadFunction(this);
	this.xmlDoc.send(null||postVars);

	};
		/**
          *     @desc: destructor, cleans used memory
          *     @type: private
		  *     @topic: 0
          */
    dtmlXMLLoaderObject.prototype.destructor=function(){
        this.onloadAction=null;
	    this.mainObject=null;
        this.xmlDoc=null;
        return null;
    }
	
		/**  
          *     @desc: Call wrapper
          *     @type: private
          *     @param: funcObject - action handler
          *     @param: dhtmlObject - user data
		  *     @returns: function handler
		  *     @topic: 0  
          */
function callerFunction(funcObject,dhtmlObject){
	this.handler=function(e){
		if (!e) e=window.event;
		funcObject(e,dhtmlObject);
		return true;
	};
	return this.handler;
};

		/**  
          *     @desc: Calculate absolute position of html object
          *     @type: private
          *     @param: htmlObject - html object
		  *     @topic: 0  
          */
function getAbsoluteLeft(htmlObject){
        var xPos = htmlObject.offsetLeft;
        var temp = htmlObject.offsetParent;
        while (temp != null) {
            xPos += temp.offsetLeft;
            temp = temp.offsetParent;
        }
        return xPos;
    }
		/**
          *     @desc: Calculate absolute position of html object
          *     @type: private
          *     @param: htmlObject - html object
		  *     @topic: 0  
          */
function getAbsoluteTop(htmlObject) {
        var yPos = htmlObject.offsetTop;
        var temp = htmlObject.offsetParent;
        while (temp != null) {
            yPos += temp.offsetTop;
            temp = temp.offsetParent;
        }
        return yPos;
   }
   
   
/**  
*     @desc: Convert string to it boolean representation
*     @type: private
*     @param: inputString - string for covertion
*     @topic: 0
*/	  
function convertStringToBoolean(inputString){ if (typeof(inputString)=="string") inputString=inputString.toLowerCase();
	switch(inputString){
		case "1":
		case "true":
		case "yes":
		case "y":
		case 1:		
		case true:		
					return true; 
					break;
		default: 	return false;
	}
}

/**  
*     @desc: find out what symbol to use as url param delimiters in further params
*     @type: private
*     @param: str - current url string
*     @topic: 0  
*/
function getUrlSymbol(str){
		if(str.indexOf("?")!=-1)
			return "&"
		else
			return "?"
	}
	
	
function dhtmlDragAndDropObject(){
		if (window.dhtmlDragAndDrop) return window.dhtmlDragAndDrop;
		this.lastLanding=0;
		this.dragNode=0;
		this.dragStartNode=0;
		this.dragStartObject=0;
		this.tempDOMU=null;
		this.tempDOMM=null;
		this.waitDrag=0;
		window.dhtmlDragAndDrop=this;

		return this;
	};
	
	dhtmlDragAndDropObject.prototype.removeDraggableItem=function(htmlNode){
		htmlNode.onmousedown=null;
		htmlNode.dragStarter=null;
		htmlNode.dragLanding=null;
	}
	dhtmlDragAndDropObject.prototype.addDraggableItem=function(htmlNode,dhtmlObject){
		htmlNode.onmousedown=this.preCreateDragCopy;
		htmlNode.dragStarter=dhtmlObject;
		this.addDragLanding(htmlNode,dhtmlObject);
	}
	dhtmlDragAndDropObject.prototype.addDragLanding=function(htmlNode,dhtmlObject){
		htmlNode.dragLanding=dhtmlObject;
	}
	dhtmlDragAndDropObject.prototype.preCreateDragCopy=function(e)
	{
		if ((e||event).button==2) return;
		if (window.dhtmlDragAndDrop.waitDrag) {
			 window.dhtmlDragAndDrop.waitDrag=0;
			 document.body.onmouseup=window.dhtmlDragAndDrop.tempDOMU;
			 document.body.onmousemove=window.dhtmlDragAndDrop.tempDOMM;
			 return false;
		}
		
		window.dhtmlDragAndDrop.waitDrag=1;
		window.dhtmlDragAndDrop.tempDOMU=document.body.onmouseup;
		window.dhtmlDragAndDrop.tempDOMM=document.body.onmousemove;
		window.dhtmlDragAndDrop.dragStartNode=this;
		window.dhtmlDragAndDrop.dragStartObject=this.dragStarter;
		document.body.onmouseup=window.dhtmlDragAndDrop.preCreateDragCopy;
		document.body.onmousemove=window.dhtmlDragAndDrop.callDrag;

            	if ((e)&&(e.preventDefault)) { e.preventDefault(); return false; }
            	return false;
	};
	dhtmlDragAndDropObject.prototype.callDrag=function(e){
		if (!e) e=window.event;
		dragger=window.dhtmlDragAndDrop;

	   	if ((e.button==0)&&(_isIE)) return dragger.stopDrag();
		if (!dragger.dragNode && dragger.waitDrag) { 
			dragger.dragNode=dragger.dragStartObject._createDragNode(dragger.dragStartNode,e);
            if (!dragger.dragNode) return dragger.stopDrag();
			dragger.gldragNode=dragger.dragNode;
			document.body.appendChild(dragger.dragNode);
			document.body.onmouseup=dragger.stopDrag;
			dragger.waitDrag=0;
			dragger.dragNode.pWindow=window;
		   	dragger.initFrameRoute();
			}


		if (dragger.dragNode.parentNode!=window.document.body){
			var grd=dragger.gldragNode;
			if (dragger.gldragNode.old) grd=dragger.gldragNode.old;

			//if (!document.all) dragger.calculateFramePosition();
			grd.parentNode.removeChild(grd);
			var oldBody=dragger.dragNode.pWindow;
	//		var oldp=dragger.dragNode.parentObject;
			if (_isIE){
			var div=document.createElement("Div");
			div.innerHTML=dragger.dragNode.outerHTML;
			dragger.dragNode=div.childNodes[0];	}
			else dragger.dragNode=dragger.dragNode.cloneNode(true);

			dragger.dragNode.pWindow=window;
	  //		dragger.dragNode.parentObject=oldp;

			dragger.gldragNode.old=dragger.dragNode;
	  		document.body.appendChild(dragger.dragNode);
			oldBody.dhtmlDragAndDrop.dragNode=dragger.dragNode;
		}

			dragger.dragNode.style.left=e.clientX+15+(dragger.fx?dragger.fx*(-1):0)+(document.body.scrollLeft||document.documentElement.scrollLeft)+"px";
			dragger.dragNode.style.top=e.clientY+3+(dragger.fy?dragger.fy*(-1):0)+(document.body.scrollTop||document.documentElement.scrollTop)+"px";
		if (!e.srcElement) 	var z=e.target; 	else 	z=e.srcElement;
		dragger.checkLanding(z,e);
	}
	
	dhtmlDragAndDropObject.prototype.calculateFramePosition=function(n){
		//this.fx = 0, this.fy = 0;
		if (window.name)  {
		  var el =parent.frames[window.name].frameElement.offsetParent;
		  var fx=0;
		  var fy=0;
		  while (el)	{      fx += el.offsetLeft;      fy += el.offsetTop;      el = el.offsetParent;   }
		  if 	((parent.dhtmlDragAndDrop))	{ 	 var ls=parent.dhtmlDragAndDrop.calculateFramePosition(1);  fx+=ls.split('_')[0]*1;  fy+=ls.split('_')[1]*1;  }
		  if (n) return fx+"_"+fy;
		  else this.fx=fx; this.fy=fy;
		  }
		  return "0_0";
	}
	dhtmlDragAndDropObject.prototype.checkLanding=function(htmlObject,e){

		if ((htmlObject)&&(htmlObject.dragLanding)) {
			if (this.lastLanding)
				this.lastLanding.dragLanding._dragOut(this.lastLanding);
			this.lastLanding=htmlObject;
			this.lastLanding=this.lastLanding.dragLanding._dragIn(this.lastLanding,this.dragStartNode,e.clientX, e.clientY,e);
			this.lastLanding_scr=(_isIE?e.srcElement:e.target);
		}
		else {
			 if ((htmlObject)&&(htmlObject.tagName!="BODY")) this.checkLanding(htmlObject.parentNode,e);
			 else  {
			 	 if (this.lastLanding) this.lastLanding.dragLanding._dragOut(this.lastLanding,e.clientX, e.clientY,e); this.lastLanding=0;
				 if (this._onNotFound) this._onNotFound();
				 }
			 }
	}
	dhtmlDragAndDropObject.prototype.stopDrag=function(e,mode){
		dragger=window.dhtmlDragAndDrop;
		if (!mode)
			{
			  dragger.stopFrameRoute();
              var temp=dragger.lastLanding;
        	  dragger.lastLanding=null;
			  if (temp) temp.dragLanding._drag(dragger.dragStartNode,dragger.dragStartObject,temp,(_isIE?event.srcElement:e.target));
			}
        dragger.lastLanding=null;
		if ((dragger.dragNode)&&(dragger.dragNode.parentNode==document.body)) dragger.dragNode.parentNode.removeChild(dragger.dragNode);
		dragger.dragNode=0;
		dragger.gldragNode=0;
		dragger.fx=0;
		dragger.fy=0;
		dragger.dragStartNode=0;
		dragger.dragStartObject=0;
		document.body.onmouseup=dragger.tempDOMU;
		document.body.onmousemove=dragger.tempDOMM;
		dragger.tempDOMU=null;
		dragger.tempDOMM=null;
		dragger.waitDrag=0;
	}	
	
	dhtmlDragAndDropObject.prototype.stopFrameRoute=function(win){
		if (win)
			window.dhtmlDragAndDrop.stopDrag(1,1);

		for (var i=0; i<window.frames.length; i++)
			if ((window.frames[i]!=win)&&(window.frames[i].dhtmlDragAndDrop))
				window.frames[i].dhtmlDragAndDrop.stopFrameRoute(window);
		if ((parent.dhtmlDragAndDrop)&&(parent!=window)&&(parent!=win)) 
				parent.dhtmlDragAndDrop.stopFrameRoute(window);
	}
	dhtmlDragAndDropObject.prototype.initFrameRoute=function(win,mode){
		if (win)	{


        			    window.dhtmlDragAndDrop.preCreateDragCopy();
					window.dhtmlDragAndDrop.dragStartNode=win.dhtmlDragAndDrop.dragStartNode;
					window.dhtmlDragAndDrop.dragStartObject=win.dhtmlDragAndDrop.dragStartObject;
					window.dhtmlDragAndDrop.dragNode=win.dhtmlDragAndDrop.dragNode;
					window.dhtmlDragAndDrop.gldragNode=win.dhtmlDragAndDrop.dragNode;
					window.document.body.onmouseup=window.dhtmlDragAndDrop.stopDrag;
					window.waitDrag=0;
					if (((!_isIE)&&(mode))&&((!_isFF)||(_FFrv<1.8)))
                         window.dhtmlDragAndDrop.calculateFramePosition();
				}
	if ((parent.dhtmlDragAndDrop)&&(parent!=window)&&(parent!=win))
				parent.dhtmlDragAndDrop.initFrameRoute(window);
		for (var i=0; i<window.frames.length; i++)
			if ((window.frames[i]!=win)&&(window.frames[i].dhtmlDragAndDrop))
				window.frames[i].dhtmlDragAndDrop.initFrameRoute(window,((!win||mode)?1:0));

	}

var _isFF=false; var _isIE=false; var _isOpera=false; var _isKHTML=false; var _isMacOS=false;

if (navigator.userAgent.indexOf('Macintosh') != -1) _isMacOS=true;
if ((navigator.userAgent.indexOf('Safari') != -1)||(navigator.userAgent.indexOf('Konqueror')!= -1)){
	var _KHTMLrv=parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Safari')+7,5));
	if (_KHTMLrv > 525){ //mimic FF behavior for Safari 3.1+
		
		_isFF=true;
		var _FFrv=1.9;
	} else 
	    _isKHTML=true;
}
else if (navigator.userAgent.indexOf('Opera') != -1){
    _isOpera=true;
    _OperaRv=parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('Opera')+6,3));
    }
else if(navigator.appName.indexOf("Microsoft")!=-1)
    _isIE=true;
else {
    _isFF=true;
    var _FFrv=parseFloat(navigator.userAgent.split("rv:")[1])
    }

//deprecated, use global constant instead
//determines if current browser is IE
function isIE(){
	if(navigator.appName.indexOf("Microsoft")!=-1)
        if (navigator.userAgent.indexOf('Opera') == -1)
    		return true;
	return false;
}

//multibrowser Xpath processor
dtmlXMLLoaderObject.prototype.doXPath = function(xpathExp,docObj,namespace,result_type){  
    if ((_isKHTML)) return this.doXPathOpera(xpathExp,docObj);
	if(_isIE){//IE
		if(!docObj)
			if(!this.xmlDoc.nodeName)
				docObj = this.xmlDoc.responseXML
			else
				docObj = this.xmlDoc;
		if (!docObj) dhtmlxError.throwError("LoadXML","Incorrect XML",[(docObj||this.xmlDoc),this.mainObject]);

		if(namespace!=null)
			docObj.setProperty("SelectionNamespaces","xmlns:xsl='"+namespace+"'");//
		if(result_type=='single'){
			return docObj.selectSingleNode(xpathExp);
		}else{
			return docObj.selectNodes(xpathExp)||new Array(0);
		}
	}else{//Mozilla
		var nodeObj = docObj;
		if(!docObj){
			if(!this.xmlDoc.nodeName){
			docObj = this.xmlDoc.responseXML
			}else{
			docObj = this.xmlDoc;
			}
		}
		
		if (!docObj) dhtmlxError.throwError("LoadXML","Incorrect XML",[(docObj||this.xmlDoc),this.mainObject]);
		if(docObj.nodeName.indexOf("document")!=-1){
			nodeObj = docObj;
		}else{
			nodeObj = docObj;
			docObj = docObj.ownerDocument;

		}		
		var retType = XPathResult.ANY_TYPE;
		if(result_type=='single')
			retType = XPathResult.FIRST_ORDERED_NODE_TYPE
		var rowsCol = new Array();
    		var col = docObj.evaluate(xpathExp, nodeObj, function(pref){return namespace}, retType,null);
    		if(retType == XPathResult.FIRST_ORDERED_NODE_TYPE){
				return col.singleNodeValue ;
			}
    		var thisColMemb = col.iterateNext();
	    	while (thisColMemb) {
		    	rowsCol[rowsCol.length] = thisColMemb;
			    thisColMemb = col.iterateNext();
    		}
	    	return rowsCol;
	}
}
   

function _dhtmlxError(type,name,params){
    if (!this.catches)
        this.catches=new Array();

    return this;
}

_dhtmlxError.prototype.catchError=function(type,func_name){
    this.catches[type]=func_name;
}
_dhtmlxError.prototype.throwError=function(type,name,params){
        if (this.catches[type]) return  this.catches[type](type,name,params);
        if (this.catches["ALL"]) return  this.catches["ALL"](type,name,params);
        alert("Error type: " + arguments[0]+"\nDescription: " + arguments[1] );
        return null;
}

window.dhtmlxError=new  _dhtmlxError();


//opera fake, while 9.0 not released
//multibrowser Xpath processor
dtmlXMLLoaderObject.prototype.doXPathOpera = function(xpathExp,docObj){
    //this is fake for Opera
    var z=xpathExp.replace(/[\/]+/gi,"/").split('/');
    var obj=null;
    var i=1;

    if (!z.length) return [];
    if (z[0]==".")
        obj=[docObj];
    else if (z[0]=="")
        {
        obj=(this.xmlDoc.responseXML||this.xmlDoc).getElementsByTagName(z[i].replace(/\[[^\]]*\]/g,""));
        i++;
        }
    else return [];

    for (i; i<z.length; i++)
        obj=this._getAllNamedChilds(obj,z[i]);

    if (z[i-1].indexOf("[")!=-1)
        obj=this._filterXPath(obj,z[i-1]);
    return obj;
}

dtmlXMLLoaderObject.prototype._filterXPath = function(a,b){
    var c=new Array();
    var b=b.replace(/[^\[]*\[\@/g,"").replace(/[\[\]\@]*/g,"");
    for (var i=0; i<a.length; i++)
        if (a[i].getAttribute(b))
            c[c.length]=a[i];

    return c;
}
dtmlXMLLoaderObject.prototype._getAllNamedChilds = function(a,b){
    var c=new Array();
    if (_isKHTML) b=b.toUpperCase();
    for (var i=0; i<a.length; i++)
        for (var j=0; j<a[i].childNodes.length; j++){
        	if (_isKHTML) {
        		if (a[i].childNodes[j].tagName && a[i].childNodes[j].tagName.toUpperCase()==b)
        			 c[c.length]=a[i].childNodes[j];
	        }
        	else
            if (a[i].childNodes[j].tagName==b) c[c.length]=a[i].childNodes[j];
        }

    return c;
}

function dhtmlXHeir(a,b){
	for (var c in b)
		if (typeof(b[c])=="function") a[c]=b[c];
	return a;
}
function dhtmlxEvent(el,event,handler){
    if (el.addEventListener)
		el.addEventListener(event,handler,false);
	else if (el.attachEvent)
		el.attachEvent("on"+event,handler);
}

//============= XSL Extension ===================================

dtmlXMLLoaderObject.prototype.xslDoc = null;
dtmlXMLLoaderObject.prototype.setXSLParamValue = function(paramName,paramValue,xslDoc){
	if(!xslDoc)
		xslDoc = this.xslDoc
	if(xslDoc.responseXML)
		xslDoc = xslDoc.responseXML;
	var item = this.doXPath("/xsl:stylesheet/xsl:variable[@name='"+paramName+"']",xslDoc,"http:/\/www.w3.org/1999/XSL/Transform","single");
	if(item!=null)
		item.firstChild.nodeValue=paramValue
	
}
dtmlXMLLoaderObject.prototype.doXSLTransToObject = function(xslDoc,xmlDoc)
{
	if(!xslDoc)
		xslDoc = this.xslDoc;
	if(xslDoc.responseXML)
		xslDoc = xslDoc.responseXML
		
	if(!xmlDoc)
		xmlDoc = this.xmlDoc;
	if(xmlDoc.responseXML)
		xmlDoc = xmlDoc.responseXML
		
	//MOzilla
	if(!isIE()){
		if(!this.XSLProcessor){
			this.XSLProcessor = new XSLTProcessor();
			this.XSLProcessor.importStylesheet(xslDoc);
		}
		var result = this.XSLProcessor.transformToDocument(xmlDoc);
	}else{
		var result = new ActiveXObject("Msxml2.DOMDocument.3.0");
		xmlDoc.transformNodeToObject(xslDoc,result);
	}
	return result;
}

dtmlXMLLoaderObject.prototype.doXSLTransToString = function(xslDoc,xmlDoc)
{
	return this.doSerialization(this.doXSLTransToObject(xslDoc,xmlDoc));
}

dtmlXMLLoaderObject.prototype.doSerialization = function(xmlDoc){
	if(!isIE()){
		var xmlSerializer = new XMLSerializer();
		return xmlSerializer.serializeToString(xmlDoc);
	}else
		return xmlDoc.xml;
}

//(c)dhtmlx ltd. www.dhtmlx.com

/*_TOPICS_
@0:initialization
@1:selection control
@2:rows control
@3:colums control
@4:cells controll
@5:data manipulation
@6:appearence control
@7:overal control
@8:tools
@9:treegrid
@10: event handlers
@11: paginal output
*/

var globalActiveDHTMLGridObject;
String.prototype._dhx_trim = function(){
                     return this.replace(/&nbsp;/g," ").replace(/(^[ \t]*)|([ \t]*$)/g,"");
                  }


function dhtmlxArray(ar){ return dhtmlXHeir((ar||new Array()),new _dhtmlxArray()); };
function _dhtmlxArray(){ return this; };
_dhtmlxArray.prototype._dhx_find = function(pattern){
   for(var i=0;i<this.length;i++){
      if(pattern==this[i])
            return i;
   }
   return -1;
}
_dhtmlxArray.prototype._dhx_delAt = function(ind){
   if(Number(ind)<0 || this.length==0)
      return false;
   for(var i=ind;i<this.length;i++){
      this[i]=this[i+1];
   }
   this.length--;
}
_dhtmlxArray.prototype._dhx_insertAt = function(ind,value){
   this[this.length] = null;
   for(var i=this.length-1;i>=ind;i--){
      this[i] = this[i-1]
   }
   this[ind] = value
}
_dhtmlxArray.prototype._dhx_removeAt = function(ind){
   for(var i=ind;i<this.length;i++){
      this[i] = this[i+1]
   }
   this.length--;
}
_dhtmlxArray.prototype._dhx_swapItems = function(ind1,ind2){
   var tmp = this[ind1];
   this[ind1] = this[ind2]
   this[ind2] = tmp;
}

/**
*   @desc: dhtmlxGrid constructor
*   @param: id - (optional) id of div element to base grid on
*   @returns: dhtmlxGrid object
*   @type: public
*/
function dhtmlXGridObject(id){
   if (_isIE) try { document.execCommand("BackgroundImageCache", false, true); } catch (e){}
   if(id){
      if(typeof(id)=='object'){
         this.entBox = id
         this.entBox.id = "cgrid2_"+(new Date()).getTime();
      }else
         this.entBox = document.getElementById(id);
   }else{
      this.entBox = document.createElement("DIV");
      this.entBox.id = "cgrid2_"+(new Date()).getTime();
   }

    this.dhx_Event();

    this._tttag=this._tttag||"rows";
    this._cttag=this._cttag||"cell";
    this._rttag=this._rttag||"row";

   var self = this;

    this._wcorr=0;
   this.nm = this.entBox.nm || "grid";
   this.cell = null;
   this.row = null;
   this.editor=null;
    this._f2kE=true; this._dclE=true;
   this.combos=new Array(0);
    this.defVal=new Array(0);
   this.rowsAr = new Array(0);//array of rows by idd
   this.rowsCol = new dhtmlxArray(0);//array of rows by index
    //this.hiddenRowsAr = new Array(0);//nb added for paging
   this._maskArr=new Array(0);
   this.selectedRows = new dhtmlxArray(0);//selected rows array
   this.rowsBuffer = new Array(new dhtmlxArray(0),new dhtmlxArray(0));//buffer of rows loaded, but not rendered (array of ids, array of cell values arrays)
   this.loadedKidsHash = null;//not null if there is tree cell in grid
   this.UserData = new Array(0)//array of rows (and for grid - "gridglobaluserdata") user data elements

/*MAIN OBJECTS*/

   this.styleSheet = document.styleSheets;
      this.entBox.className += " gridbox";
     
       this.entBox.style.width = this.entBox.getAttribute("width") ||   (window.getComputedStyle?(this.entBox.style.width||window.getComputedStyle(this.entBox,null)["width"]):(this.entBox.currentStyle?this.entBox.currentStyle["width"]:0)) || "100%";
       this.entBox.style.height = this.entBox.getAttribute("height") || (window.getComputedStyle?(this.entBox.style.height||window.getComputedStyle(this.entBox,null)["height"]):(this.entBox.currentStyle?this.entBox.currentStyle["height"]:0)) || "100%";
      //cursor and text selection
      this.entBox.style.cursor = 'default';
        this.entBox.onselectstart = function(){return false};//avoid text select
   this.obj = document.createElement("TABLE");
      this.obj.cellSpacing = 0;
      this.obj.cellPadding = 0;
      this.obj.style.width = "100%";//nb:
      this.obj.style.tableLayout = "fixed";
      this.obj.className = "c_obj".substr(2);

        this.obj._rows=function(i){ return this.rows[i+1]; }
        this.obj._rowslength=function(){ return this.rows.length-1; }

   this.hdr = document.createElement("TABLE");
        this.hdr.style.border="1px solid gray";  //FF 1.0 fix
      this.hdr.cellSpacing = 0;
      this.hdr.cellPadding = 0;
      if ((!_isOpera)||(_OperaRv>=8.5))
             this.hdr.style.tableLayout = "fixed";
      this.hdr.className = "c_hdr".substr(2);
      this.hdr.width = "100%";

   this.xHdr = document.createElement("TABLE");
   	  this.xHdr.className = "xhdr";
      this.xHdr.cellPadding = 0;
      this.xHdr.cellSpacing = 0;
      this.xHdr.style.width='100%'
      var r = this.xHdr.insertRow(0)
      var c = r.insertCell(0);
         r.insertCell(1).innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
         r.childNodes[1].style.width='100%';
         c.appendChild(this.hdr)
   this.objBuf = document.createElement("DIV");
      this.objBuf.appendChild(this.obj);
   this.entCnt = document.createElement("TABLE");
      this.entCnt.insertRow(0).insertCell(0)
      this.entCnt.insertRow(1).insertCell(0);



      this.entCnt.cellPadding = 0;
      this.entCnt.cellSpacing = 0;
      this.entCnt.width = "100%";
      this.entCnt.height = "100%";

          this.entCnt.style.tableLayout = "fixed";

   this.objBox = document.createElement("DIV");
      this.objBox.style.width = "100%";
      this.objBox.style.height = this.entBox.style.height;
      this.objBox.style.overflow = "auto";
      this.objBox.style.position = "relative";
      this.objBox.appendChild(this.objBuf);
      this.objBox.className = "objbox";


   this.hdrBox = document.createElement("DIV");
      this.hdrBox.style.width = "100%"
        if (((_isOpera)&&(_OperaRv<9)) )
            this.hdrSizeA=25; else this.hdrSizeA=200;

           this.hdrBox.style.height=this.hdrSizeA+"px";
       if (_isIE)
            this.hdrBox.style.overflowX="hidden";
      else
          this.hdrBox.style.overflow = "hidden";

      this.hdrBox.style.position = "relative";
      this.hdrBox.appendChild(this.xHdr);



   this.preloadImagesAr = new Array(0)

   this.sortImg = document.createElement("IMG")
   this.sortImg.style.display = "none";
   this.hdrBox.insertBefore(this.sortImg,this.xHdr)
    this.entCnt.rows[0].cells[0].vAlign="top";
   this.entCnt.rows[0].cells[0].appendChild(this.hdrBox);
   this.entCnt.rows[1].cells[0].appendChild(this.objBox);


   this.entBox.appendChild(this.entCnt);
   //add links to current object
   this.entBox.grid = this;
   this.objBox.grid = this;
   this.hdrBox.grid = this;
   this.obj.grid = this;
   this.hdr.grid = this;
/*PROPERTIES*/
   this.cellWidthPX = new Array(0);//current width in pixels
   this.cellWidthPC = new Array(0);//width in % if cellWidthType set in pc
   this.cellWidthType = this.entBox.cellwidthtype || "px";//px or %

   this.delim = this.entBox.delimiter || ",";
   this._csvDelim = ",";

   this.hdrLabels = (this.entBox.hdrlabels || "").split(",");
   this.columnIds = (this.entBox.columnids || "").split(",");
   this.columnColor = (this.entBox.columncolor || "").split(",");
   this.cellType =  dhtmlxArray((this.entBox.cellstype || "").split(","));
   this.cellAlign =  (this.entBox.cellsalign || "").split(",");
   this.initCellWidth = (this.entBox.cellswidth || "").split(",");
   this.fldSort = (this.entBox.fieldstosort || "").split(",")
   this.imgURL = this.entBox.imagesurl || "gridCfx/";
   this.isActive = false;//fl to indicate if grid is in work now
   this.isEditable = true;
   this.raNoState = this.entBox.ranostate || null;
   this.chNoState = this.entBox.chnostate || null;
   this.selBasedOn = (this.entBox.selbasedon || "cell").toLowerCase()
   this.selMultiRows = this.entBox.selmultirows || false;
   this.multiLine = this.entBox.multiline || false;
   this.noHeader = this.entBox.noheader || false;
   this.xmlFileUrl = this.entBox.xmlfileurl || "";
   this.recordsNoMore = this.entBox.infinitloading || true;;//if true, then it will not attempt to fill the buffer from server
   this.useImagesInHeader = false;//use images in header or not
   this.pagingOn = false;//paging on/off
   this.rowsBufferOutSize = 0;//number of rows rendered at a moment
/*EVENTS*/
   dhtmlxEvent(window,"unload",function(){try{ self.destructor(); } catch(e){}});

/*XML LOADER(S)*/
   /**
   *   @desc: loads xml content from specified url into grid
   *   @param: url - XML file url
   *   @param: afterCall - function which will be called after xml loading
   *   @type: public
   *   @topic: 0,5
   */
   this.loadXML = function(url,afterCall){ 
        if (this._dload) { this._dload=url; this._askRealRows(null,afterCall); return true; };
        if (this._xmlaR) this.setXMLAutoLoading(url);


      if(url.indexOf("?")!=-1)
         var s = "&";
      else
         var s = "?";
      var obj = this;
	  this.callEvent("onXLS",[this]);

      if (afterCall) this.xmlLoader.waitCall=afterCall;
      this.xmlLoader.loadXML(url+""+s+"rowsLoaded="+this.getRowsNum()+"&lastid="+this.getRowId(this.getRowsNum()-1));
     
       //nb:
      //if (this.onXLS) setTimeout(function(){obj.onXLS(obj)},0);
      //setTimeout(function(){obj.xmlLoader.loadXML(url+""+s+"rowsLoaded="+obj.getRowsNum()+"&lastid="+obj.getRowId(obj.getRowsNum()-1)+"&sn="+Date.parse(new Date()));},1)
   }

   /**
   *   @desc: set one of predefined css styles (xp, mt, gray, light, clear, modern)
   *   @param: name - style name
   *   @type: public
   *   @topic: 0,6
   */
   this.setSkin = function(name){
      this.entBox.className = "gridbox gridbox_"+name;
      this.enableAlterCss("ev_"+name,"odd_"+name,this.isTreeGrid())
      this._fixAlterCss()      
      this._sizeFix=this._borderFix=0;
      switch(name){
      	 case "clear":
      	 	this._topMb=document.createElement("DIV");
      	 	this._topMb.className="topMumba";
      	 	//this._topMb.innerHTML="<img style='left:0px'   src='"+this.imgURL+"skinC_top_left.gif'><img style='right:0px' src='"+this.imgURL+"skinC_top_right.gif'>";
      	 	this.entBox.appendChild(this._topMb);
      	 	this._botMb=document.createElement("DIV");
      	 	this._botMb.className="bottomMumba";
			//this._botMb.innerHTML="<img style='left:0px'   src='"+this.imgURL+"skinD_bottom_left.gif'><img style='right:0px' src='"+this.imgURL+"skinD_bottom_right.gif'>";
      	 	this.entBox.appendChild(this._botMb);      	 	
      	 	this.entBox.style.position="relative";
      	 	this._gcCorr=20;
      	 	break;
      	 case "modern":
      	 case "light": 
  	 		this.forceDivInHeader=true;
  	 		this._sizeFix=1;
      	 	break;
         case "xp":    	 		this.forceDivInHeader=true; this._srdh=22; this._sizeFix=1; break;
		 case "mt":
		 	this._srdh=22;
		 	this._sizeFix=1;
		 	this._borderFix=(_isIE?1:0);break;
		 break;
		 case "gray": if ((_isIE)&&(document.compatMode != "BackCompat")) this._srdh=22;
		 	this._sizeFix=1;
		 	this._borderFix=(_isIE?1:0);break;

		case "madcad": if ((_isIE)&&(document.compatMode != "BackCompat")) this._srdh=22;
      	 
      	 	break;
      }
      
      if (_isIE && this.hdr){
      	var d=this.hdr.parentNode;
      	d.removeChild(this.hdr);
      	d.appendChild(this.hdr);
      	}
      this.setSizes();
      	
   }

//#__pro_feature:21092006{
//#loadfrom_string:21092006{
   /**
   *   @desc: loads xml content from specified string
   *   @param: str - XML string
   *   @param: afterCall - function which will be called after xml loading
   *   @type: public
   *   @topic: 0,5
   *   @edition: Professional
   */
   this.loadXMLString = function(str,afterCall){
        if (this._dload) { this._dloadStr=str; this._askRealRows(null,afterCall); return true; };
		this.callEvent("onXLS",[this]);

      if (afterCall) this.xmlLoader.waitCall=afterCall;
      this.xmlLoader.loadXMLString(str);
   }
//#}
//#}
   /**
   *   @desc: puts xml to parser
   *   @type: private
   *   @topic: 0,5
   */
   this.doLoadDetails = function(obj){
      var root = self.xmlLoader.getXMLTopNode(self._tttag)
        if (root.tagName!="DIV")
	  if (self._refresh_mode){
		self._refreshFromXML(self.xmlLoader);
	  	self._refresh_mode=null;
	  }else
      if(!self.xmlLoader.xmlDoc.nodeName){
         self.parseXML(self.xmlLoader.xmlDoc.responseXML)
      }else{
         self.parseXML(self.xmlLoader.xmlDoc)
        }
      //nb:paging
      if(self.pagingOn)
         self.createPagingBlock()
   }
   this.xmlLoader = new dtmlXMLLoaderObject(this.doLoadDetails,this,true,this.no_cashe);
   if (_isIE) this.preventIECashing(true);
   this.dragger=new dhtmlDragAndDropObject();

/*METHODS. SERVICE*/
      /**
      *   @desc: on scroll grid inner actions
      *   @type: private
      *   @topic: 7
      */
      this._doOnScroll = function(e,mode){
		this.callEvent("onScroll",[this.objBox.scrollLeft,this.objBox.scrollTop]);
		this.doOnScroll(e,mode);
	}
      /**
      *   @desc: on scroll grid more inner action
      *   @type: private
      *   @topic: 7
      */
      this.doOnScroll = function(e,mode){
                  this.hdrBox.scrollLeft = this.objBox.scrollLeft;
              if (this.ftr)
                 this.ftr.parentNode.scrollLeft = this.objBox.scrollLeft;
                  this.setSortImgPos(null,true);
                        if (mode) return;
                  
                  //load more rows on scroll
                  if(!this.pagingOn && this.objBox.scrollTop+this.hdrSizeA+this.objBox.offsetHeight>this.objBox.scrollHeight){
                     if(this._xml_ready && (this.objBox._oldScrollTop!=this.objBox.scrollTop) && this.addRowsFromBuffer()){
                        this.objBox.scrollTop = this.objBox.scrollHeight - (this.hdrSizeA+1+this.objBox.offsetHeight)
                  this.objBox._oldScrollTop=this.objBox.scrollTop;
                  }
                  }

                        if (this._dload){
                        if (this._dLoadTimer)  window.clearTimeout(this._dLoadTimer);
                        this._dLoadTimer=window.setTimeout(function(){ if (self.limit) self._askRealRows(); },500);
                        }
               }
      /**
      *    @desc: attach grid to some object in DOM
      *    @param: obj - object to attach to
      *   @type: public
      *   @topic: 0,7
      */
      this.attachToObject = function(obj){
                        obj.appendChild(this.entBox)
                        this.objBox.style.height = this.entBox.style.height;

                     }
      /**
      *   @desc: initialize grid
      *   @param: fl - if to parse on page xml data island 
      *   @type: public
      *   @topic: 0,7
      */
      this.init =    function(fl){

	  				 if ((this.isTreeGrid()) && (!this._h2)){
						this._aEx=new _dhtmlxArray();
						this._h2=new dhtmlxHierarchy();
						if ((this._fake)&&(!this._realfake)) this._fake._h2=this._h2;
						this._tgc={imgURL:null};
	   				}
if(!this._hstyles) return;
                     this.editStop()
                     /*TEMPORARY STATES*/
                     this.lastClicked = null;//row clicked without shift key. used in multiselect only
                     this.resized = null;//hdr cell that is resized now
                     this.fldSorted = this.r_fldSorted = null;//hdr cell last sorted
                     this.gridWidth = 0;
                     this.gridHeight = 0;
                     //empty grid if it already was initialized
                     this.cellWidthPX = new Array(0);
                     this.cellWidthPC = new Array(0);
                     if(this.hdr.rows.length>0){
                        this.clearAll(true);
                     }
                     if(this.cellType._dhx_find("tree")!=-1){//create hashtable for treegrid
                        this.loadedKidsHash = new Hashtable();
                        this.loadedKidsHash.put("hashOfParents",new Hashtable())
                     }

                     var hdrRow = this.hdr.insertRow(0);
                            for(var i=0;i<this.hdrLabels.length;i++){
                                hdrRow.appendChild(document.createElement("TH"));
                                hdrRow.childNodes[i]._cellIndex=i;
                        hdrRow.childNodes[i].style.height="0px";
                                }
                            if (_isIE) hdrRow.style.position="absolute";
                            else  hdrRow.style.height='auto';

                     var hdrRow = this.hdr.insertRow(_isKHTML?2:1);

                            hdrRow._childIndexes=new Array();
                            var col_ex=0;
                     for(var i=0;i<this.hdrLabels.length;i++){
                                hdrRow._childIndexes[i]=i-col_ex;

                            if ((this.hdrLabels[i]==this.splitSign)&&(i!=0)){
                        if (_isKHTML)
                           hdrRow.insertCell(i-col_ex);
                                hdrRow.cells[i-col_ex-1].colSpan=(hdrRow.cells[i-col_ex-1].colSpan||1)+1;
                                hdrRow.childNodes[i-col_ex-1]._cellIndex++;
                                col_ex++;
                                hdrRow._childIndexes[i]=i-col_ex;
                                continue;
                                }

                     hdrRow.insertCell(i-col_ex);

                            hdrRow.childNodes[i-col_ex]._cellIndex=i;
                            hdrRow.childNodes[i-col_ex]._cellIndexS=i;
                           this.setHeaderCol(i,this.hdrLabels[i]);
                        }
                  if (col_ex==0) hdrRow._childIndexes=null;
                  this._cCount=this.hdrLabels.length;
	
                  if (_isIE) window.setTimeout(function(){ self.setSizes(); },1);

//create virtual top row
                                if (!this.obj.firstChild)
                                    this.obj.appendChild(document.createElement("TBODY"));

                                var tar=this.obj.firstChild;
                        if (!tar.firstChild){
                                    tar.appendChild(document.createElement("TR"));
                                    tar=tar.firstChild;
                                    if (_isIE) tar.style.position="absolute";
                                    else tar.style.height='auto';

                                   for(var i=0;i<this.hdrLabels.length;i++){
                                       tar.appendChild(document.createElement("TH"));
                              tar.childNodes[i].style.height="0px";
                              }
                        }


                     this.setColumnIds()
                	 this._c_order=null;
                     if(this.multiLine==-1)
                        this.multiLine = true;
                     if(this.multiLine != true)
                        this.obj.className+=" row20px";

                     //
                     //this.combos = new Array(this.hdrLabels.length);
                     //set sort image to initial state
                     this.sortImg.style.position = "absolute";
                     this.sortImg.style.display = "none";
                     this.sortImg.src = this.imgURL+"sort_desc.gif";
                     this.sortImg.defLeft = 0;
                     //create and kill a row to set initial size
                     //this.addRow("deletethisrtowafterresize",new Array("",""))
                     this.entCnt.rows[0].style.display = ''//display header
                     if(this.noHeader){
                        this.entCnt.rows[0].style.display = 'none';
                     }else{
                        this.noHeader = false
                     }

//#__pro_feature:21092006{
//#column_hidden:21092006{
                 if (this._ivizcol)   this.setColHidden();
//
//#}


                this.attachHeader();
                this.attachHeader(0,0,"_aFoot");
                this.setSizes();
                     if(fl)
                        this.parseXML()
                     this.obj.scrollTop = 0

                            if (this.dragAndDropOff)  this.dragger.addDragLanding(this.entBox,this);
                            if (this._initDrF) this._initD();
					if (this._init_point) this._init_point();
                  };
      /**
      *    @desc: sets sizes of grid elements
      *   @type: private
      *   @topic: 0,7
      */
      this.setSizes  =    function(fl){ 
	  				 if ((!this.hdr.rows[0])) return;
                     if (!this.entBox.offsetWidth) {
					 	if (this._sizeTime)
							window.clearTimeout(this._sizeTime);
							this._sizeTime=window.setTimeout(function(){ self.setSizes()},250);
					 	return;
					 }
			   		if (((_isFF)&&(this.entBox.style.height=="100%"))||(this._fixLater)){
						this.entBox.style.height=this.entBox.parentNode.clientHeight;
						this._fixLater=true;
					}

                     if(fl && this.gridWidth==this.entBox.offsetWidth && this.gridHeight==this.entBox.offsetHeight){
                        return false
                     }else if(fl){
                        this.gridWidth = this.entBox.offsetWidth
                        this.gridHeight = this.entBox.offsetHeight
                     }





                            if ((!this.hdrBox.offsetHeight)&&(this.hdrBox.offsetHeight>0))
                         this.entCnt.rows[0].cells[0].height = this.hdrBox.offsetHeight+"px";

                     var gridWidth = parseInt(this.entBox.offsetWidth)-(this._gcCorr||0);
                     var gridHeight = parseInt(this.entBox.offsetHeight)-((!_isIE)?(this._sizeFix||0):0);



                     var _isVSroll=(this.objBox.scrollHeight>this.objBox.offsetHeight);
                     if (((!this._ahgr)&&(_isVSroll))||((this._ahgrM)&&(this._ahgrM<this.objBox.scrollHeight)))
                                gridWidth-=(this._scrFix||(_isFF?17:17));





                     var len = this.hdr.rows[0].cells.length
//                var pcx_widht=(this._fake?(gridWidth-this._fake.entBox.offsetWidth):gridWidth);

                     for(var i=0;i<this._cCount;i++){
                        if(this.cellWidthType=='px' && this.cellWidthPX.length < len){
                           this.cellWidthPX[i] = this.initCellWidth[i] - this._wcorr;
                        }else if(this.cellWidthType=='%' && this.cellWidthPC.length < len){
                           this.cellWidthPC[i] = this.initCellWidth[i];
                        }
                        if(this.cellWidthType=='%' && this.cellWidthPC.length!=0 && this.cellWidthPC[i]){
                           this.cellWidthPX[i] = parseInt(gridWidth*this.cellWidthPC[i]/100);
                        }
                     }

                    var wcor=this.entBox.offsetWidth-this.entBox.clientWidth;

                    var summ = 0;
                	var fcols=new Array();

                     for(var i=0;i<this._cCount;i++)
                   if ((this.initCellWidth[i]=="*")&&((!this._hrrar)||(!this._hrrar[i])))
                           fcols[fcols.length]=i;
                  else
                           summ += parseInt(this.cellWidthPX[i]);
                if (fcols.length){
                   var ms=Math.floor((gridWidth-summ-wcor)/fcols.length);
                  if (ms<0) ms=1;
                   for(var i=0;i<fcols.length; i++){
                     var min=(this._drsclmW?this._drsclmW[fcols[i]]:0);
                           this.cellWidthPX[fcols[i]]=(min?(min>ms?min:ms):ms)-this._wcorr;
                     summ+=ms;
                     }
                }

                     var summ = 0;
                     for(var i=0;i<this._cCount;i++)
                        summ += parseInt(this.cellWidthPX[i])
                   if (_isOpera) summ-=1;
                   
                this.chngCellWidth();
                if ((this._awdth)&&(this._awdth[0])){
                	//convert percents to PX
                	if (this.cellWidthType=='%') {
                			this.cellWidthType="px";
                			this.cellWidthPC=[];
            			}
                	var gs=(summ>this._awdth[1]?this._awdth[1]:(summ<this._awdth[2]?this._awdth[2]:summ)); 
                	this.entBox.style.width=gs+((_isVSroll && !this._ahgr)?(_isFF?20:18):0)+"px";
            	}                   

                       this.objBuf.style.width = summ + "px";
                  if ((this.ftr)&&(!this._realfake))
                       this.ftr.style.width = summ + "px";

                       this.objBuf.childNodes[0].style.width = summ + "px";
                            //if (_isOpera) this.hdr.style.width = summ + this.cellWidthPX.length*2 + "px";
                     //set auto page size of dyn scroll
                     this.doOnScroll(0,1);

                     //set header part of container visible height to header's height
                     //this.entCnt.rows[0].cells[0].style.height = this.hdr.offsetHeight;

                                 this.hdr.style.border="0px solid gray";  //FF 1.0 fix
/*                         if ((_isMacOS)&&(_isFF))
                                    var zheight=20;
                                 else*/
                                var zheight=this.hdr.offsetHeight+(this._borderFix?this._borderFix:0);
				                if (this.ftr) zheight+=this.ftr.offsetHeight;

                                if (this._ahgr)
                                    if (this.objBox.scrollHeight){
                                        if (_isIE)
                                            var z2=this.objBox.scrollHeight;
                                        else
                                            var z2=this.objBox.childNodes[0].scrollHeight;
                                       var scrfix=this.parentGrid?1:((this.objBox.offsetWidth<this.objBox.scrollWidth)?(_isFF?20:18):1);
                              if (this._ahgrMA)
                                 z2=this.entBox.parentNode.offsetHeight-zheight-scrfix-(this._sizeFix?this._sizeFix:0)*2;


                                 if (((this._ahgrM)&&((this._ahgrF?(z2+zheight+scrfix):z2)>this._ahgrM)))
                                              gridHeight=this._ahgrM*1+(this._ahgrF?0:(zheight+scrfix));
                                 else
                                    gridHeight=z2+zheight+scrfix;

                                        this.entBox.style.height=gridHeight+"px";
                                  }

				                if (this.ftr) zheight-=this.ftr.offsetHeight;

                var aRow=this.entCnt.rows[1].cells[0].childNodes[0];
               
                if(!this.noHeader)
                     aRow.style.top = (zheight-this.hdrBox.offsetHeight+((_isIE && !window.XMLHttpRequest)?(-wcor):0) )+"px";
                if (this._topMb) {
                	this._topMb.style.top=(zheight||0)+"px";
					this._topMb.style.width=(gridWidth+20)+"px";
            	}
				if (this._botMb) {
					this._botMb.style.top=(gridHeight-3)+"px";
					this._botMb.style.width=(gridWidth+20)+"px";					
				}

                     //nb 072006:
                     aRow.style.height = (((gridHeight - zheight-1)<0 && _isIE)?20:(gridHeight - zheight-1))-(this.ftr?this.ftr.offsetHeight:0)+"px";
                if (this.ftr && this.entBox.offsetHeight>this.ftr.offsetHeight) this.entCnt.style.height=this.entBox.offsetHeight-this.ftr.offsetHeight+"px";

                            if (this._dload)
                                this._dloadSize=Math.floor(parseInt(this.entBox.offsetHeight)/20)+(_isKHTML?4:2); //rough, but will work

                  };

      /**
      *   @desc: changes cell width
      *   @param: [ind] - index of row in grid
      *   @type: private
      *   @topic: 4,7
      */
      this.chngCellWidth = function(){
                       if ((_isOpera)&&(this.ftr))
                           this.ftr.width=this.objBox.scrollWidth+"px";
                     var l=this._cCount;
                           for(var i=0;i<l;i++){
                              this.hdr.rows[0].cells[i].style.width = this.cellWidthPX[i]+"px";
                              this.obj.rows[0].childNodes[i].style.width = this.cellWidthPX[i]+"px";
                       if (this.ftr)
                                 this.ftr.rows[0].cells[i].style.width = this.cellWidthPX[i]+"px";
                           }
                        }
      /**
      *   @desc: set delimiter character used in list values (default is ",")
      *   @param: delim - delimiter as string
      *   @before_init: 1
      *   @type: public
      *   @topic: 0
      */
      this.setDelimiter = function(delim){
         this.delim = delim;
      }
      /**
      *   @desc: set width of columns in percents
      *   @type: public
      *   @before_init: 1
      *   @param: wp - list of column width in percents
      *   @topic: 0,7
      */
      this.setInitWidthsP = function(wp){
         this.cellWidthType = "%";
         this.initCellWidth = wp.split(this.delim.replace(/px/gi,""));
         this._setAutoResize();
      }
      /**
	  *	@desc:
	  *	@type: private
	  *	@topic: 0
	  */
      this._setAutoResize=function(){
            var el=window;
            var self=this;
           
            if(el.addEventListener){
                if ((_isFF)&&(_FFrv<1.8))
                    el.addEventListener("resize",function (){
                        if (!self.entBox) return;
                        var z=self.entBox.style.width;
                        self.entBox.style.width="1px";

                        window.setTimeout(function(){ self.entBox.style.width=z; self.setSizes();  if (self._fake) self._fake._correctSplit(); },10);
                        },false);
                else
                    el.addEventListener("resize",function (){ if (self.setSizes) self.setSizes(); if (self._fake) self._fake._correctSplit();  },false);
                }
            else if (el.attachEvent)
                el.attachEvent("onresize",function(){ 
                    if (self._resize_timer) window.clearTimeout(self._resize_timer);
                    if (self.setSizes)
                        self._resize_timer=window.setTimeout(function(){ self.setSizes();  if (self._fake) self._fake._correctSplit(); },500);
                });
            this._setAutoResize=function(){};   	
      }
      /**
      *   @desc: set width of columns in pixels
      *   @type: public
      *   @before_init: 1
      *   @param: wp - list of column width in pixels
      *   @topic: 0,7
      */
      this.setInitWidths = function(wp){
         this.cellWidthType = "px";
         this.initCellWidth = wp.split(this.delim);
            if (_isFF){
                for (var i=0; i<this.initCellWidth.length; i++)
               if (this.initCellWidth[i]!="*")
                    this.initCellWidth[i]=parseInt(this.initCellWidth[i])-2;
            }

      }

      /**
      *   @desc: set multiline rows support to enabled or disabled state
      *   @type: public
      *   @before_init: 1
      *   @param: state - true or false
      *   @topic: 0,7
      */
      this.enableMultiline = function(state){
         this.multiLine = convertStringToBoolean(state);
      }

      /**
      *   @desc: set multiselect mode to enabled or disabled state
      *   @type: public
      *   @param: state - true or false
      *   @topic: 0,7
      */
      this.enableMultiselect = function(state){
         this.selMultiRows = convertStringToBoolean(state);
      }

      /**
      *   @desc: set path to grid internal images (sort direction, any images used in editors, checkbox, radiobutton)
      *   @type: public
      *   @param: path - url (or relative path) of images folder with closing "/"
      *   @topic: 0,7
      */
      this.setImagePath = function(path){
         this.imgURL = path;
      }

      /**
      *   @desc: part of column resize routine
      *   @type: private
      *   @param: ev - event
      *   @topic: 3
      */
      this.changeCursorState = function (ev){
                           var el = ev.target||ev.srcElement;
                     if(el.tagName!="TD")
                           el = this.getFirstParentOfType(el,"TD")
                           if ((el.tagName=="TD")&&(this._drsclmn)&&(!this._drsclmn[el._cellIndex])) return  el.style.cursor = "default";
						   var check = ev.layerX+(((!_isIE)&&(ev.target.tagName=="DIV"))?el.offsetLeft:0);
                           if((el.offsetWidth - (ev.offsetX||(parseInt(this.getPosition(el,this.hdrBox))-check)*-1))<10){
                              el.style.cursor = "E-resize";
                           }else
                              el.style.cursor = "default";
                       if (_isOpera) this.hdrBox.scrollLeft = this.objBox.scrollLeft;
                        }
      /**
      *   @desc: part of column resize routine
      *   @type: private
      *   @param: ev - event
      *   @topic: 3
      */
      this.startColResize = function(ev){
                           this.resized = null;
                           var el = ev.target||ev.srcElement;
                     if(el.tagName!="TD")
                        el = this.getFirstParentOfType(el,"TD")
                           var x = ev.clientX;
                           var tabW = this.hdr.offsetWidth;
                           var startW = parseInt(el.offsetWidth)
                           if(el.tagName=="TD" && el.style.cursor!="default"){
                                        if ((this._drsclmn)&&(!this._drsclmn[el._cellIndex])) return;
                              this.entBox.onmousemove = function(e){this.grid.doColResize(e||window.event,el,startW,x,tabW)}
                              document.body.onmouseup = new Function("","document.getElementById('"+this.entBox.id+"').grid.stopColResize()");
                           } 
                        }
      /**
      *   @desc: part of column resize routine
      *   @type: private
      *   @param: ev - event
      *   @topic: 3
      */
      this.stopColResize = function(){
                           this.entBox.onmousemove = "";//removeEventListener("mousemove")//
                           document.body.onmouseup = "";
                           this.setSizes();
                           this.doOnScroll(0,1)
						   this.callEvent("onResizeEnd",[this]);
                        }
      /**
      *   @desc: part of column resize routine
      *   @param: el - element (column resizing)
      *   @param: startW - started width
      *   @param: x - x coordinate to resize from
      *   @param: tabW - started width of header table
      *   @type: private
      *   @topic: 3
      */
      this.doColResize = function(ev,el,startW,x,tabW){
                        el.style.cursor = "E-resize";
                        this.resized = el;
                        var fcolW = startW + (ev.clientX-x);
                        var wtabW = tabW + (ev.clientX-x)
                                if (!(this.callEvent("onResize",[el._cellIndex,fcolW,this]))) return;
                                if (el.colSpan>1){
                                    var a_sizes=new Array();
                                    for (var i=0; i<el.colSpan; i++)
                                         a_sizes[i]=Math.round(fcolW*this.hdr.rows[0].childNodes[el._cellIndexS+i].offsetWidth/el.offsetWidth);
                                    for (var i=0; i<el.colSpan; i++)
                                        this._setColumnSizeR(el._cellIndexS+i*1,a_sizes[i]);
                                }
                        else
                                this._setColumnSizeR(el._cellIndex,fcolW);
                                this.doOnScroll(0,1);
                              if (_isOpera) this.setSizes();
                              this.objBuf.childNodes[0].style.width = "";
                     }

      /**
      *   @desc: set width of grid columns ( zero row of header and body )
      *   @type: private
      *   @topic: 7
      */
       this._setColumnSizeR=function(ind, fcolW){
                        if(fcolW>((this._drsclmW  && !this._notresize)?(this._drsclmW[ind]||10):10)){
                           this.obj.firstChild.firstChild.childNodes[ind].style.width = fcolW+"px";
                           this.hdr.rows[0].childNodes[ind].style.width = fcolW+"px";
                     if (this.ftr)
                        this.ftr.rows[0].childNodes[ind].style.width = fcolW+"px";
                           if(this.cellWidthType=='px'){
                              this.cellWidthPX[ind]=fcolW;
                           }else{
                             var gridWidth = parseInt(this.entBox.offsetWidth);
                              if (this.objBox.scrollHeight>this.objBox.offsetHeight)
                          gridWidth-=(this._scrFix||(_isFF?17:17));
                              var pcWidth = Math.round(fcolW/gridWidth*100)
                              this.cellWidthPC[ind]=pcWidth;
                           }
                        }
         }
      /**
      *    @desc: sets position and visibility of sort arrow
      *    @param: state - true/false - show/hide image
      *    @param: ind - index of field
      *    @param: order - asc/desc - type of image
	  *    @param: row - one based index of header row ( used in multirow headers, top row by default )
      *   @type: public
      *   @topic: 7
      */
         this.setSortImgState=function(state,ind,order,row){
         	order=(order||"asc").toLowerCase();
            if (!convertStringToBoolean(state)){
             this.sortImg.style.display = "none";
             this.fldSorted=null;
                return;
                }

            if  (order=="asc")
             this.sortImg.src =  this.imgURL; //+"sort_asc.gif";
            else
             this.sortImg.src = this.imgURL;//+"sort_desc.gif";
            this.sortImg.style.display="";
            this.fldSorted=this.hdr.rows[0].childNodes[ind];
            var r=this.hdr.rows[row||1];
            for (var i=0; i < r.childNodes.length; i++) 
            	if (r.childNodes[i]._cellIndex==ind)
	            	this.r_fldSorted=r.childNodes[i];
            		this.setSortImgPos();
        }

      /**
      *    @desc: sets position and visibility of sort arrow
      *    @param: ind - index of field
      *    @param: ind - index of field
      *    @param: hRowInd - index of row in case of complex header, one-based, optional

      *   @type: private
      *   @topic: 7
      */
      this.setSortImgPos = function(ind,mode,hRowInd,el){
              if (!el){
                           if(!ind)
                              var el = this.r_fldSorted;
                           else
                              var el = this.hdr.rows[hRowInd||0].cells[ind];
                     }

                           if(el!=null){
                              var pos = this.getPosition(el,this.hdrBox)
                              var wdth = el.offsetWidth;
                              this.sortImg.style.left = Number(pos[0]+wdth-13)+"px";//Number(pos[0]+5)+"px";
                              this.sortImg.defLeft = parseInt(this.sortImg.style.left)
                              this.sortImg.style.top = Number(pos[1]+5)+"px";

                              if ((!this.useImagesInHeader)&&(!mode))
                                 this.sortImg.style.display = "inline";
                              this.sortImg.style.left = this.sortImg.defLeft+"px";//-parseInt(this.hdrBox.scrollLeft)
                           }
                        }

      /**
      *   @desc: manage activity of the grid.
      *   @param: fl - true to activate,false to deactivate
      *   @type: private
      *   @topic: 1,7
      */
      this.setActive = function(fl){
                     if(arguments.length==0)
                        var fl = true;
                     if(fl==true){
                           //document.body.onkeydown = new Function("","document.getElementById('"+this.entBox.id+"').grid.doKey()")//
                   if (globalActiveDHTMLGridObject && ( globalActiveDHTMLGridObject != this ))
                     globalActiveDHTMLGridObject.editStop();

                        globalActiveDHTMLGridObject = this;
                        this.isActive = true;
                     }else{
                        this.isActive = false;
                     }
                  };
      /**
      *     @desc: called on click occured
      *     @type: private
      */
      this._doClick = function(ev){
                     var selMethod = 0;
                     var el = this.getFirstParentOfType(_isIE?ev.srcElement:ev.target,"TD");
                     var fl = true;
					 
					  //mm
					 //markers start
					 if(this.markedCells){
					 	var markMethod = 0;
						
					 	if(ev.shiftKey || ev.metaKey){
                           markMethod = 1;
                        }
                        if(ev.ctrlKey){
                           markMethod = 2;
                        }
						this.doMark(el,markMethod);
						return true;
					 } 
					 //markers end
					 //mm
					 
                     if(this.selMultiRows!=false){
                        if(ev.shiftKey && this.row!=null){
                           selMethod = 1;
                        }
                        if(ev.ctrlKey || ev.metaKey){
                           selMethod = 2;
                        }

                     }
                     this.doClick(el,fl,selMethod)
                  };


      /**
      *   @desc: called onmousedown inside grid area
      *   @type: private
      */
        this._doContClick=function(ev){
                     var el = this.getFirstParentOfType(_isIE?ev.srcElement:ev.target,"TD");
                            if ((!el)||(typeof(el.parentNode.idd)=="undefined")) return true;

                            if (ev.button==2 || (_isMacOS && ev.ctrlKey)){
                        if (!this.callEvent("onRightClick",[el.parentNode.idd,el._cellIndex,ev])) {
                           var z=function(e){ document.body.oncontextmenu=Function("return true;"); (e||event).cancelBubble=true; return false; }
                           if (_isIE) ev.srcElement.oncontextmenu=z;
                              else if (!_isMacOS) document.body.oncontextmenu=z;

                           return false;
                        }
                        if (this._ctmndx){
                                   if (!(this.callEvent("onBeforeContextMenu",[el.parentNode.idd,el._cellIndex,this]))) return true;
                                   el.contextMenuId=el.parentNode.idd+"_"+el._cellIndex;
                                   el.contextMenu=this._ctmndx;
                                   el.a=this._ctmndx._contextStart;
                                   if (_isIE)
                                       ev.srcElement.oncontextmenu = function(){ event.cancelBubble=true; return false; };
                                   el.a(el,ev);
                                   el.a=null;
                           }
                            }
                     else
                        if(this._ctmndx) this._ctmndx._contextEnd();
            return true;
        }

      /**
      *    @desc: occures on cell click (supports treegrid)
      *   @param: [el] - cell to click on
      *   @param:   [fl] - true if to call onRowSelect function
      *   @param: [selMethod] - 0 - simple click, 1 - shift, 2 - ctrl
      *   @param: show - true/false - scroll row to view, true by defaul    
      *   @type: private
      *   @topic: 1,2,4,9
      */
/*ORIG
	this.doClick = function(el,fl,selMethod,show){ 

                  var psid=this.row?this.row.idd:0;

                     this.setActive(true);
                     if(!selMethod)
                        selMethod = 0;
                     if(this.cell!=null)
                        this.cell.className = this.cell.className.replace(/cellselected/g,"");
                     if(el.tagName=="TD" && (this.rowsCol._dhx_find(this.rowsAr[el.parentNode.idd])!=-1 || this.rowsBuffer[0]._dhx_find(el.parentNode.idd)!=-1)){
                        if (this.checkEvent("onSelectStateChanged")) var initial=this.getSelectedId();
                  var prow=this.row;
                        if(selMethod==0){
                           this.clearSelection();
                        }else if(selMethod==1){
                           var elRowIndex = this.rowsCol._dhx_find(el.parentNode)
                           var lcRowIndex = this.rowsCol._dhx_find(this.lastClicked)
                           if(elRowIndex>lcRowIndex){
                              var strt = lcRowIndex;
                              var end = elRowIndex;
                           }else{
                              var strt = elRowIndex;
                              var end = lcRowIndex;
                           }
                           for(var i=0;i<this.rowsCol.length;i++)
                              if((i>=strt && i<=end)){
                              	if (this.rowsCol[i] && (!this.rowsCol[i]._sRow)){
                              	  
                         		  if (this.rowsCol[i].className.indexOf("rowselected")==-1 && this.callEvent("onBeforeSelect",[this.rowsCol[i].idd,psid])){
                                    this.rowsCol[i].className+=" rowselected";
                                    this.selectedRows[this.selectedRows.length] = this.rowsCol[i]
		                        }}
		                        else{
		                        	this.clearSelection();
		                        	return this.doClick(el,fl,0,show);
		                        }
							  }	                    	
                              
                        }else if(selMethod==2){
                           if(el.parentNode.className.indexOf("rowselected") != -1){
                              el.parentNode.className=el.parentNode.className.replace(/rowselected/g,"");
                              this.selectedRows._dhx_removeAt(this.selectedRows._dhx_find(el.parentNode))
                              var skipRowSelection = true;
                           }
                        }
                        this.editStop()
                        this.cell = el;

                        if ((prow == el.parentNode)&&(this._chRRS))
                     fl=false;

						if (typeof(el.parentNode.idd)=="undefined") return true;
                        this.row = el.parentNode;

                        if((!skipRowSelection)&&(!this.row._sRow)){
                     if (this.callEvent("onBeforeSelect",[this.row.idd,psid])){
                              this.row.className+= " rowselected"
                              if(this.selectedRows._dhx_find(this.row)==-1)
                                 this.selectedRows[this.selectedRows.length] = this.row;
                     }
                     else this.row=prow;

                        }
                        if(this.selBasedOn=="cell"){
                           if (this.cell.parentNode.className.indexOf("rowselected")!=-1)
                               this.cell.className = this.cell.className.replace(/cellselected/g,"")+" cellselected";
                        }

                  if(selMethod!=1)
                  		if (!this.row) return;
                        this.lastClicked = el.parentNode;
						
                        var rid = this.row.idd;
                        var cid = this.cell.cellIndex;
                        if (fl && typeof(rid)!="undefined") self.onRowSelectTime=setTimeout(function(){ self.callEvent("onRowSelect",[rid,cid]); },100)
                        if (this.checkEvent("onSelectStateChanged")) {
                            var afinal=this.getSelectedId();
                            if (initial!=afinal)  this.callEvent("onSelectStateChanged",[afinal]);
                        }
                     }
                     this.isActive = true;
                     if (show!==false)
                     this.moveToVisible(this.cell)
                  }
*/				  
				this.doClick = function(el,fl,selMethod,show){ 

                  var psid=this.row?this.row.idd:0;

                     this.setActive(true);
                     if(!selMethod)
                        selMethod = 0;
                     if(this.cell!=null)
                        this.cell.className = this.cell.className.replace(/cellselected/g,"");
                     if(el.tagName=="TD" && (this.rowsCol._dhx_find(this.rowsAr[el.parentNode.idd])!=-1 || this.rowsBuffer[0]._dhx_find(el.parentNode.idd)!=-1)){
                        if (this.checkEvent("onSelectStateChanged")) var initial=this.getSelectedId();
                  var prow=this.row;
                        if(selMethod==0){
                           this.clearSelection();
                        }else if(selMethod==1){
                           var elRowIndex = this.rowsCol._dhx_find(el.parentNode)
                           var lcRowIndex = this.rowsCol._dhx_find(this.lastClicked)
                           if(elRowIndex>lcRowIndex){
                              var strt = lcRowIndex;
                              var end = elRowIndex;
                           }else{
                              var strt = elRowIndex;
                              var end = lcRowIndex;
                           }
                           for(var i=0;i<this.rowsCol.length;i++)
                              if((i>=strt && i<=end)){
                              	if (this.rowsCol[i] && (!this.rowsCol[i]._sRow)){
                              	  
                         		  if (this.rowsCol[i].className.indexOf("rowselected")==-1 && this.callEvent("onBeforeSelect",[this.rowsCol[i].idd,psid])){
                                    //this.rowsCol[i].className+=" rowselected";
                                    this.selectedRows[this.selectedRows.length] = this.rowsCol[i]
		                        }}
		                        else{
		                        	this.clearSelection();
		                        	return this.doClick(el,fl,0,show);
		                        }
							  }	                    	
                              
                        }else if(selMethod==2){
                           if(el.parentNode.className.indexOf("rowselected") != -1){
                              el.parentNode.className=el.parentNode.className.replace(/rowselected/g,"");
                              this.selectedRows._dhx_removeAt(this.selectedRows._dhx_find(el.parentNode))
                              var skipRowSelection = true;
                           }
                        }
                        this.editStop()
                        this.cell = el;

                        if ((prow == el.parentNode)&&(this._chRRS))
                     fl=false;

						if (typeof(el.parentNode.idd)=="undefined") return true;
                        this.row = el.parentNode;

                        if((!skipRowSelection)&&(!this.row._sRow)){
                     if (this.callEvent("onBeforeSelect",[this.row.idd,psid])){
                             //this.row.className+= " rowselected"
                              if(this.selectedRows._dhx_find(this.row)==-1)
                                 this.selectedRows[this.selectedRows.length] = this.row;
                     }
                     else this.row=prow;

                        }
                        if(this.selBasedOn=="cell"){
                           if (this.cell.parentNode.className.indexOf("rowselected")!=-1)
                               this.cell.className = this.cell.className.replace(/cellselected/g,"")+" cellselected";
                        }

                  if(selMethod!=1)
                  		if (!this.row) return;
                        this.lastClicked = el.parentNode;
						
                        var rid = this.row.idd;
                        var cid = this.cell.cellIndex;

						//if (fl && typeof(rid)!="undefined") alert("hmm"); self.onRowSelectTime=setTimeout(function(){ self.callEvent("onRowSelect",[rid,cid]); },100)
						self.onRowSelectTime=setTimeout(function(){ self.callEvent("onRowSelect",[rid,cid]); },100)
                        if (this.checkEvent("onSelectStateChanged")) {
                            var afinal=this.getSelectedId();
                            if (initial!=afinal)  this.callEvent("onSelectStateChanged",[afinal]);
                        }
                     }
                     this.isActive = true;
                     if (show!==false)
                     this.moveToVisible(this.cell)
                  }
			
      /**
      *   @desc: select all rows in grid, it doesn't fire any events
      *   @param: edit - switch selected cell to edit mode
      *   @type: public
      *   @topic: 1,4
      */
      this.selectAll = function(){
      		this.clearSelection();
      		this.selectedRows=dhtmlxArray([].concat(this.rowsCol));
      		if (this.selectedRows.length){
	      		this.row=this.selectedRows[0];
	      		this.cell=this.row.cells[0];
      		}
      		for (var i=0; i<this.rowsCol.length; i++)
      			//this.rowsCol[i].className+=" rowselected";
      		if ((this._fake)&&(!this._realfake)) this._fake.selectAll();
      		
  	}
      /**
      *   @desc: set selection to specified row-cell
      *   @param: r - row object or row index
      *   @param: cInd - cell index
      *   @param: [fl] - true if to call onRowSelect function
        *   @param: preserve - preserve previously selected rows true/false (false by default)
        *   @param: edit - switch selected cell to edit mode
	  *   @param: show - true/false - scroll row to view, true by defaul         
      *   @type: public
      *   @topic: 1,4
      */
      this.selectCell = function(r,cInd,fl,preserve,edit,show){
                     if(!fl)
                        fl = false;
                     if(typeof(r)!="object")
                        r = this.rowsCol[r]
//#__pro_feature:21092006{
//#colspan:20092006{
                            if (r._childIndexes)
                         var c = r.childNodes[r._childIndexes[cInd]];
                            else
//#}
//#}
                         var c = r.childNodes[cInd];
                         if (!c) c=r.childNodes[0];
                            if (preserve)
                         this.doClick(c,fl,3,show)
                            else
                         this.doClick(c,fl,0,show)
                            if (edit) this.editCell();
                  }
      /**
      *   @desc: moves specified cell to visible area (scrolls)
      *   @param: cell_obj - object of the cell to work with
      *   @param: onlyVScroll - allow only vertical positioning

      *   @type: private
      *   @topic: 2,4,7
      */
      this.moveToVisible = function(cell_obj,onlyVScroll){
                     try{
                        var distance = cell_obj.offsetLeft+cell_obj.offsetWidth+20;

						var scrollLeft=0;
                        if(distance>(this.objBox.offsetWidth+this.objBox.scrollLeft)){
                        	if(cell_obj.offsetLeft>this.objBox.scrollLeft)
                           		scrollLeft = cell_obj.offsetLeft-5
                        }else if(cell_obj.offsetLeft<this.objBox.scrollLeft){
                        	if(distance<this.objBox.scrollLeft)
                           		scrollLeft =  cell_obj.offsetLeft-5
                        }
                        if ((scrollLeft)&&(!onlyVScroll))
                           this.objBox.scrollLeft = scrollLeft;

                        var distance = cell_obj.offsetTop+cell_obj.offsetHeight + 20;
                        if(distance>(this.objBox.offsetHeight+this.objBox.scrollTop)){
                           var scrollTop = distance - this.objBox.offsetHeight;
                        }else if(cell_obj.offsetTop<this.objBox.scrollTop){
                           var scrollTop =  cell_obj.offsetTop-5
                        }
                        if(scrollTop)
                           this.objBox.scrollTop = scrollTop;
                                          }catch(er){
                     }
                  }
      /**
      *   @desc: creates Editor object and switch cell to edit mode if allowed
      *   @type: public
      *   @topic: 4
      */
      this.editCell = function(){ 
                     this.editStop();
                     if ((this.isEditable!=true)||(!this.cell))
                        return false;
                     var c = this.cell;
                            //#locked_row:11052006{
                            if (c.parentNode._locked) return false;
                            //#}

                this.editor = this.cells4(c);

                     //initialize editor
                     if(this.editor!=null){
                           if (this.editor.isDisabled()) { this.editor=null; return false; }
                           if(this.callEvent("onEditCell",[0,this.row.idd,this.cell._cellIndex])!=false && this.editor.edit){
                              this._Opera_stop=(new Date).valueOf();
                              c.className+=" editable";
                              this.editor.edit();
                              this.callEvent("onEditCell",[1,this.row.idd,this.cell._cellIndex])
                           }else{//preserve editing
                              this.editor=null;
                           }
                     }
                  }
      /**
      *   @desc: retuns value from editor(if presents) to cell and closes editor
      *   @mode: if true - current edit value will be reverted to previous one
      *   @type: public
      *   @topic: 4
      */
      this.editStop = function(mode){
                       if (_isOpera)
                                if (this._Opera_stop){
                                    if ((this._Opera_stop*1+50)>(new Date).valueOf()) return;
                                    this._Opera_stop=null;
                                }

                    if(this.editor && this.editor!=null){
                       this.editor.cell.className=this.editor.cell.className.replace("editable","");
                    if (mode){
                    	var t=this.editor.val;
                    	this.editor.detach();
                    	this.editor.setValue(t);
                    	this.editor=null;
                    	return;
                    }
                    if (this.editor.detach()) this.cell.wasChanged = true;

               var g=this.editor;
                    this.editor=null;
                    var z=this.callEvent("onEditCell",[2,this.row.idd,this.cell._cellIndex,g.getValue(),g.val]);

               if ((typeof(z)=="string")||(typeof(z)=="number"))
				  g[g.setImage?"setLabel":"setValue"](z);
               else
				  if (!z) g[g.setImage?"setLabel":"setValue"](g.val);
                     }
                  }
	/**
	*	@desc: 
	*	@type: private
	*/
	this._nextRowCell=function(row,dir,pos){
		row=this._nextRow(this.rowsCol._dhx_find(row),dir);
		if (!row) return null;
			return row.childNodes[row._childIndexes?row._childIndexes[pos]:pos];
		}
	/**
	*	@desc: 
	*	@type: private
	*/
	this._getNextCell=function(acell,dir,i){
		
		
      	acell=acell||this.cell;
		
        var arow=acell.parentNode;
		if (this._tabOrder){
			i=this._tabOrder[acell._cellIndex];
		if (typeof i != "undefined")
		  	if (i < 0)
		  		 acell=this._nextRowCell(arow,dir,Math.abs(i)-1);
		  	else acell=arow.childNodes[i];
  		} else {
  			var i=acell._cellIndex+dir;
  			if (i >= 0 && i < this._cCount ){
  				if (arow._childIndexes) i=arow._childIndexes[acell._cellIndex]+dir;
  				acell=arow.childNodes[i];
  			}
  			else{
				
  				acell=this._nextRowCell(arow,dir,(dir==1?0:(this._cCount-1)));
  				
			}
  		}
		
		
		
		if (!acell){ 
			if( (dir == 1) && this.tabEnd){
				this.tabEnd.focus();
				this.tabEnd.focus();
			}
			if( (dir == -1) && this.tabStart){
				this.tabStart.focus();
				this.tabStart.focus();
			}
			return null;
		}
		//tab out
	
		// tab readonly 
		if (acell.style.display!="none" &&( !this.smartTabOrder || !this.cells(acell.parentNode.idd,acell._cellIndex).isDisabled() ))
        		return acell;
      	return this._getNextCell(acell,dir);
		// tab readonly 
		
   }
	/**
	*	@desc: 
	*	@type: private
	*/
	this._nextRow=function(ind,dir){
		var r=this.rowsCol[ind+dir];
		if (r && r.style.display=="none") return this._nextRow(ind+dir,dir);
		return r;	
	}
	/**
	*	@desc: 
	*	@type: private
	*/
	this.scrollPage = function(dir){                           
                        var new_ind=Math.floor((this.getRowIndex(this.row.idd)||0)+(dir)*this.objBox.offsetHeight/(this._srdh||20));
                        if (new_ind<0) new_ind=0;
                        if (this._dload && (!this.rowsCol[new_ind])){
                        	this._askRealRows(new_ind,function(){
                        			try{ self.selectCell(new_ind,(this.cell?this.cell._cellIndex:0),true); } catch(e){};
                        		});
	                        }
                        else{
                        	if (new_ind>=this.rowsCol.length) new_ind=this.rowsCol.length-1;
                               this.selectCell(new_ind,this.cell._cellIndex,true); 
                        }
	}	
	
      /**
      *   @desc: manages keybord activity in grid
      *   @type: private
      *   @topic: 7
      */
      this.doKey =   function(ev){
                            if (!ev) return true;
                            if ((ev.target||ev.srcElement).value!==window.undefined){
                                 var zx= (ev.target||ev.srcElement);
                                 if ((!zx.parentNode)||(zx.parentNode.className.indexOf("editable")==-1))
                                     return true;
                                 }
                            if ((globalActiveDHTMLGridObject)&&(this!=globalActiveDHTMLGridObject))
                                return globalActiveDHTMLGridObject.doKey(ev);
                     if(this.isActive==false){
                        //document.body.onkeydown = "";
                        return true;
                     }

                     if (this._htkebl) return true;
                     if (!this.callEvent("onKeyPress",[ev.keyCode,ev.ctrlKey,ev.shiftKey,ev])) return false;
					 
                     var code="k"+ev.keyCode+"_"+(ev.ctrlKey?1:0)+"_"+(ev.shiftKey?1:0);
                     if (this.cell){ //if selection exists in grid only
                     	if (this._key_events[code]){
                     		if (false===this._key_events[code].call(this)) return true;
                     		if (ev.preventDefault) ev.preventDefault();		
                     		ev.cancelBubble=true;
                     		return false;
                 		}
                 	
                 		if (this._key_events["k_other"])
                 			this._key_events.k_other.call(this,ev);
				     }
                 		
                 	
                    return true;
                  }
   /**
   *   @desc: selects row (?)for comtatibility with previous version
   *   @param: cell - cell object(or cell's child)
   *   @invoke: click on cell(or cell content)
   *   @type: private
   *   @topic: 1,2
   */
   this.getRow = function(cell){
                  if(!cell)
                     cell = window.event.srcElement;
                  if(cell.tagName!='TD')
                     cell = cell.parentElement;
                  r = cell.parentElement;
                  if(this.cellType[cell._cellIndex]=='lk')
                     eval(this.onLink+"('"+this.getRowId(r.rowIndex)+"',"+cell._cellIndex+")");
                  this.selectCell(r,cell._cellIndex,true)
               }
   /**
   *   @desc: selects row (and first cell of it)
   *   @param: r - row index or row object
   *   @param: fl - if true, then call function on select
    *   @param: preserve - preserve previously selected rows true/false (false by default)
	*   @param: show - true/false - scroll row to view, true by defaul    
   *   @type: public
   *   @topic: 1,2
   */
   this.selectRow = function(r,fl,preserve,show){
                  if(typeof(r)!='object')
                     r = this.rowsCol[r]
                  this.selectCell(r,0,fl,preserve,false,show)
               };
   /**
   *   @desc: sorts rows by specified column
   *   @param: col - column index
   *   @param:   type - str.int.date
   *   @param: order - asc,desc
   *   @type: public
   *   @topic: 2,3,5,9
   */
   this.sortRows = function(col,type,order){
   				  order=(order||"asc").toLowerCase();
   				  type=(type||this.fldSort[col]);
                  while(this.addRowsFromBuffer(true));//nb:paging - before sorting put all rows from buffer to rows collection.
                  //if tree cell exists
                  if(this.cellType._dhx_find("tree")!=-1){ 
                     return this.sortTreeRows(col,type,order)
                  }
                        var self=this;
                        var arrTS=new Array();
                        var atype = this.cellType[col];
                  var amet="getValue";
                  if (atype=="link")  amet="getContent";
                  if (atype=="dhxCalendar" || atype=="dhxCalendarA")  amet="getDate";


                        for (var i=0; i<this.rowsCol.length; i++)
                                 arrTS[this.rowsCol[i].idd]=this.cells3(this.rowsCol[i],col)[amet]();

                        this._sortRows(col,type,order,arrTS);
               }
	/**
	*	@desc: 
	*	@type: private
	*/
	this._sortCore=function(col,type,order,arrTS,s){
				var sort="sort";
                if (this._sst)  {
                	s["stablesort"]=this.rowsCol.stablesort;
                	sort="stablesort";
                }
                
//#__pro_feature:21092006{
//#custom_sort:21092006{
			 if(type.length>4) type=window[type];
			 
             if(type=='cus'){
             s[sort](function(a,b){
				return self._customSorts[col](arrTS[a.idd],arrTS[b.idd],order,a.idd,b.idd);
             });
             }else if(typeof(type)=='function'){
             s[sort](function(a,b){
				return type(arrTS[a.idd],arrTS[b.idd],order,a.idd,b.idd);
             });
         	 } else
//#}
//#}
             if(type=='str'){
             s[sort](function(a,b){
             if(order=="asc")
                  return arrTS[a.idd]>arrTS[b.idd]?1:-1
             else
                  return arrTS[a.idd]<arrTS[b.idd]?1:-1
             });
             }else if(type=='int'){
             s[sort](function(a,b){
             var aVal = parseFloat(arrTS[a.idd]); aVal=isNaN(aVal)?-99999999999999:aVal;
             var bVal = parseFloat(arrTS[b.idd]); bVal=isNaN(bVal)?-99999999999999:bVal;
             if(order=="asc")
                  return aVal-bVal;
             else
                  return bVal-aVal;
             });
             }else if(type=='date'){
             s[sort](function(a,b){
             var aVal = Date.parse(arrTS[a.idd])||(Date.parse("01/01/1900"));
             var bVal = Date.parse(arrTS[b.idd])||(Date.parse("01/01/1900"));
             if(order=="asc")
                return aVal-bVal
             else
                return bVal-aVal
             });
          }
                  
	}
      /**
      *   @desc: inner sorting routine
      *   @type: private
      *   @topic: 7
      */
    this._sortRows = function(col,type,order,arrTS){
                  
				  this._sortCore(col,type,order,arrTS,this.rowsCol);

                  if(this.pagingOn){//nb:paging
                       this.changePage(this.currentPage);
                       this.callEvent("onGridReconstructed",[]);
                  }else{
				    
					var tb = (_isKHTML)?this.obj:this.obj.rows[0].parentNode;
					for(var i=0;i<this.rowsCol.length;i++){
                       if  (this.rowsCol[i]!=this.obj._rows(i))
                           tb.insertBefore(this.rowsCol[i],this.obj._rows(i))
                     }
                  }
                       //this.setSizes()
                       this.callEvent("onGridReconstructed",[]);
}


   /**
   *   @desc: enables the possibility to load content from server when already loaded content was rendered. Using this feature you decrease the grid loading time for extremely big amounts of data ( in case of latest grid version, usage of SmartRendering instead of this mode strongly recommended )
   *   @param: filePath - path which will be used for fetching additional data
   *   @param: bufferSize - size of client size buffer 
   *   @type: public
   *   @topic: 0,7
   */
   this.setXMLAutoLoading = function(filePath,bufferSize){
        if (arguments.length==0) return (this._xmlaR=true);
      this.recordsNoMore = false;
      this.xmlFileUrl = filePath;
      this.rowsBufferOutSize = bufferSize||(this.rowsBufferOutSize==0?40:this.rowsBufferOutSize);
   }

   /**
   *   @desc: enables buffering in content rendering. Using this you decrease the grid loading time.
   *   @type: public
   *   @topic: 0,7
   */
   this.enableBuffering = function(bufferSize){
      this.rowsBufferOutSize = bufferSize||(this.rowsBufferOutSize==0?40:this.rowsBufferOutSize);
   }




   /**
   *   @desc: create rows from another part of buffer
   *   @type: private
   *   @topic: 0,2,7
   */
   this.addRowsFromBuffer = function(stopBeforeServerCall){
      if(this.rowsBuffer[0].length==0){
         if(!this.recordsNoMore && !stopBeforeServerCall){
         	if (this.limit && this.rowsCol.length>=this.limit) return false;
            if ((this.xmlFileUrl!="")&&(!this._startXMLLoading)){
                    this._startXMLLoading=true;
               this.loadXML(this.xmlFileUrl)
            }
         }else
            return false;
      }
      var cnt = Math.min(this.rowsBufferOutSize,this.rowsBuffer[0].length)


      //this.rowsBuffer.length
      for(var i=0;i<cnt;i++){
         //nb:newbuffer

         if(this.rowsBuffer[1][0].tagName == "TR"){//insert already created row
            this._insertRowAt(this.rowsBuffer[1][0],-1,this.pagingOn);
         }else{//create row from xml tag and insert it
            var rowNode = this.rowsBuffer[1][0]
			var r=this.createRowFromXMLTag(rowNode);
            this._insertRowAt(r,-1,this.pagingOn);
			this._postRowProcessing(r,rowNode);
         }
         this.rowsBuffer[0]._dhx_removeAt(0);
         this.rowsBuffer[1]._dhx_removeAt(0);
      }

      return this.rowsBuffer[0].length!=0;
   }
   /**
   *   @desc: creates row object based on xml tag
   *   @param: rowNode - object of xml tag "row"
   *   @type: private
   *   @returns: TR object
   */
   this.createRowFromXMLTag = function(rowNode){
      if(rowNode.tagName=="TR")//not xml tag, but already created TR
         return rowNode;

      var tree=this.cellType._dhx_find("tree");
      var rId = rowNode.getAttribute("id")

      this.rowsAr[rId] = this._prepareRow(rId);
      var r= this._fillRowFromXML(this.rowsAr[rId],rowNode,tree,null);
          
      return r;
   }

   /**
   *   @desc: allow multiselection
   *   @param: fl - false/true
   *   @type: deprecated
   *   @before_init: 1
   *   @topic: 0,2,7
   */
   this.setMultiselect = function(fl){
      this.selMultiRows = convertStringToBoolean(fl);
   }

   /**
   *   @desc: called when row was double clicked
   *   @type: private
   *   @topic: 1,2
   */
   this.wasDblClicked = function(ev){
      var el = this.getFirstParentOfType(_isIE?ev.srcElement:ev.target,"TD");
      if(el){
         var rowId = el.parentNode.idd;
         return this.callEvent("onRowDblClicked",[rowId,el._cellIndex]);
      }
   }

   /**
   *   @desc: called when header was clicked
   *   @type: private
   *   @topic: 1,2
   */
   this._onHeaderClick = function(e,el){
   	    var that=this.grid;
        el = el||that.getFirstParentOfType(_isIE?event.srcElement:e.target,"TD");
		if (this.grid.resized==null){
      		if (!(this.grid.callEvent("onHeaderClick",[el._cellIndexS,(e||window.event)]))) return false;
			that.sortField(el._cellIndexS,false,el)
		}
   }

   /**
   *   @desc: deletes selected row(s)
   *   @type: deprecated
   *   @topic: 2
   */
   this.deleteSelectedItem = function(){
   		this.deleteSelectedRows();
   }
   /**
   *   @desc: deletes selected row(s)
   *   @type: public
   *   @topic: 2
   */
   this.deleteSelectedRows = function(){
                     var num = this.selectedRows.length//this.obj.rows.length
                     if(num==0)
                        return;
                     var tmpAr = this.selectedRows;
                     this.selectedRows = new dhtmlxArray(0)
                     for(var i=num-1;i>=0;i--){
                        var node = tmpAr[i]

                                if(!this.deleteRow(node.idd,node)){
                           this.selectedRows[this.selectedRows.length] = node;
                        }else{
                           if(node==this.row){
                              var ind = i;
                           }
                        }
/*
                           this.rowsAr[node.idd] = null;
                           var posInCol = this.rowsCol._dhx_find(node)
                           this.rowsCol[posInCol].parentNode.removeChild(this.rowsCol[posInCol]);//nb:this.rowsCol[posInCol].removeNode(true);
                           this.rowsCol._dhx_removeAt(posInCol)*/
                     }
                     if(ind){
                        try{
                           if(ind+1>this.rowsCol.length)//this.obj.rows.length)
                              ind--;
                           this.selectCell(ind,0,true)
                        }catch(er){
                           this.row = null
                           this.cell = null
                        }
                     }
                  }

   /**
   *   @desc: gets selected row id
   *   @returns: id of selected row (list of ids with default delimiter) or null if non row selected
   *   @type: public
   *   @topic: 1,2,9
   */
   this.getSelectedRowId = function(){
   	return this.getSelectedId();
   }
   /**
   *   @desc: gets selected row id
   *   @returns: id of selected row (list of ids with default delimiter) or null if non row selected
   *   @type: deprecated
   *   @topic: 1,2,9
   */
   this.getSelectedId = function(){
                     var selAr = new Array(0); var uni={};
                     for(var i=0;i<this.selectedRows.length;i++){
                     	var id=this.selectedRows[i].idd;
                     	if (uni[id]) continue;
                        selAr[selAr.length]=id;
                        uni[id]=true;
                     }

                     //..
                     if(selAr.length==0)
                        return null;
                     else
                        return selAr.join(this.delim);
                  }
   /**
   *   @desc: gets index of selected cell
   *   @returns: index of selected cell or -1 if there is no selected sell
   *   @type: public
   *   @topic: 1,4
   */
   this.getSelectedCellIndex = function(){
                           if(this.cell!=null)
                              return this.cell._cellIndex;
                           else
                              return -1;
                        }
   /**
   *   @desc: gets width of specified column in pixels
   *   @param: ind - column index
   *   @returns: column width in pixels
   *   @type: public
   *   @topic: 3,7
   */
   this.getColWidth = function(ind){
                           return parseInt(this.cellWidthPX[ind])+((_isFF)?2:0);
                        }

   /**
   *   @desc: sets width of specified column in pixels (soen't works with procent based grid)
   *   @param: ind - column index
   *   @param: value - new width value
   *   @type: public
   *   @topic: 3,7
   */
   this.setColWidth = function(ind,value){
                        if (this.cellWidthType=='px')
                               this.cellWidthPX[ind]=parseInt(value);
                     else
                        this.cellWidthPC[ind]=parseInt(value);
                            this.setSizes();
                        }


   /**
   *   @desc: gets row object by id
   *   @param: id - row id
   *   @returns: row object or null if there is no row with specified id
   *   @type: private
   *   @topic: 2,7,9
   */
   this.getRowById = function(id){
                  var row = this.rowsAr[id]
                  if(row)
                     return row;
                  else
                     if (this._dload){
                         var ind = this.rowsBuffer[0]._dhx_find(id);
                         if (ind>=0) {
                               this._askRealRows(ind);
                                return this.getRowById(id);
                         }
                     }
                     else if(this.pagingOn){//use new buffer
                        var ind = this.rowsBuffer[0]._dhx_find(id);
                                if (ind>=0) {
                           var r = this.createRowFromXMLTag(this.rowsBuffer[1][ind]);
                           this._postRowProcessing(r,this.rowsBuffer[1][ind]);
                           this.rowsBuffer[1][ind] = r;
                           return r;
                        }else{
                           return null;
                        }
                     }
                else if (this._slowParse) //smart parsing mode in treegrid
                  return this._seekAndDeploy(id);
                  return null;
               }
   /**
   *   @desc: gets row by index from rowsCola and rowsBuffer
   *   @param: ind - row index
   *   @returns: row object
   *   @type: private
   */
   this.getRowByIndex = function(ind){
      if(this.rowsCol.length<=ind){
         if((this.rowsCol.length+this.rowsBuffer[0].length)<=ind)
            return null;
         else{
            var indInBuf = ind-this.rowsCol.length-1;
            var r = this.createRowFromXMLTag(this.rowsBuffer[1][indInBuf]);
            return r;
         }
      }else{
         return this.rowsCol[ind]
      }
   }

   /**
   *   @desc: gets row index by id (grid only)
   *   @param: row_id - row id
   *   @returns: row index or -1 if there is no row with specified id
   *   @type: public
   *   @topic: 2
   */
   this.getRowIndex = function(row_id){
                        var ind = this.rowsCol._dhx_find(this.getRowById(row_id));
                        if(ind!=-1)
                           return ind;
                        else{
                           ind = this.rowsBuffer[0]._dhx_find(row_id)
                           if(ind!=-1)
                              return ind+this.rowsCol.length;
                           return -1;
                        }
                  }
   /**
   *   @desc: gets row id by index
   *   @param: ind - row index
   *   @returns: row id or null if there is no row with specified index
   *   @type: public
   *   @topic: 2
   */
   this.getRowId = function(ind){
                            var z=this.rowsCol[parseInt(ind)];
                            if (z) return z.idd;
                            return (this.rowsBuffer[0][this._dload?ind:(ind-this.rowsCol.length)]||null);
                  }
   /**
   *   @desc: sets new id for row by its index
   *   @param: ind - row index
   *   @param: row_id - new row id
   *   @type: public
   *   @topic: 2
   */
   this.setRowId = function(ind,row_id){
                     var r = this.rowsCol[ind]
                     this.changeRowId(r.idd,row_id)
                  }
   /**
   *   @desc: changes id of the row to the new one
   *   @param: oldRowId - row id to change
   *   @param: newRowId - row id to set
   *   @type:public
   *   @topic: 2
   */
   this.changeRowId = function(oldRowId,newRowId){
   				  if (oldRowId==newRowId) return;
/*
				  for (var i=0; i<row.childNodes.length; i++)
					  if (row.childNodes[i]._code)
						  this._compileSCL("-",row.childNodes[i]);      */
                  var row = this.rowsAr[oldRowId]
                  row.idd = newRowId;
                  if(this.UserData[oldRowId]){
                     this.UserData[newRowId] = this.UserData[oldRowId]
                     this.UserData[oldRowId] = null;
                  }
	                if (this._h2 && this._h2.get[oldRowId]){
	                	this._h2.get[newRowId]=this._h2.get[oldRowId];
	                	this._h2.get[newRowId].id=newRowId;
	                	delete this._h2.get[oldRowId];
                    }
                  
				  if (this.rowsBuffer[0]){
				  	var ind=this.rowsBuffer[0]._dhx_find(oldRowId);
				  	if (ind!=-1)
				  		this.rowsBuffer[0][ind]==newRowId;
				  }
				  	
                  this.rowsAr[oldRowId] = null;
                  this.rowsAr[newRowId] = row;
				  for (var i=0; i<row.childNodes.length; i++)
					  if (row.childNodes[i]._code)
						  row.childNodes[i]._code=this._compileSCL(row.childNodes[i]._val,row.childNodes[i]);
               }
   /**
   *   @desc: sets ids to every column. Can be used then to retreive the index of the desired colum
   *   @param: [ids] - delimitered list of ids (default delimiter is ","), or empty if to use values set earlier
   *   @type: public
   *   @topic: 3
   */
   this.setColumnIds = function(ids){
                     if(ids)
                        this.columnIds = ids.split(this.delim)
                if (this.hdr.rows.length>0){
                        if(this.hdr.rows[0].cells.length>=this.columnIds.length){
                           for(var i=0;i<this.columnIds.length;i++){
                              this.hdr.rows[0].cells[i].column_id = this.columnIds[i];
                           }
                        }
                }
                  }
   /**
   *   @desc: sets ids to specified column.
   *   @param: ind- index of column
   *   @param: id- id of column
   *   @type: public
   *   @topic: 3
   */
   this.setColumnId = function(ind, id){  this.columnIds[ind]=this.hdr.rows[0].cells[ind].column_id = id; }
   /**
   *   @desc: gets column index by column id
   *   @param: id - column id
   *   @returns: index of the column
   *   @type: public
   *   @topic: 3
   */
   this.getColIndexById = function(id){
                     for(var i=0;i<this.hdr.rows[0].cells.length;i++){
                        if(this.hdr.rows[0].cells[i].column_id==id)
                           return i;
                     }
                  }
   /**
   *   @desc: gets column id of column specified by index
   *   @param: cin - column index
   *   @returns: column id
   *   @type: public
   *   @topic: 3
   */
   this.getColumnId = function(cin){
                     return this.hdr.rows[0].cells[cin].column_id
                  }
	/**
   *   @desc: gets label of column specified by index
   *   @param: cin - column index
   *   @returns: column label
   *   @type: public
   *   @topic: 3
   */
	this.getColumnLabel = function(cin,rin){
		return this.getHeaderCol(cin,rin);
	}
   /**
   *   @desc: gets label of column specified by index
   *   @param: cin - column index
   *   @returns: column label
   *   @type: deprecated
   *	@newmethod: getColumnLabel
   *   @topic: 3
   */
   this.getHeaderCol = function(cin,ind){
   		var z=this.hdr.rows[(ind||0)+1];
   		var n=z.cells[z._childIndexes?z._childIndexes[parseInt(cin)]:cin];
   		return (_isIE?n.innerText:n.textContent);
    }

   /**
   *   @desc: sets row text BOLD
   *   @param: row_id - row id
   *   @type: public
   *   @topic: 2,6
   */
   this.setRowTextBold = function(row_id){
                     this.getRowById(row_id).style.fontWeight = "bold";
                  }
   /**
   *   @desc: sets style to row
   *   @param: row_id - row id
   *   @param: styleString - style string in common format (exmpl: "color:red;border:1px solid gray;")
   *   @type: public
   *   @topic: 2,6
   */
   this.setRowTextStyle = function(row_id,styleString){
                     var r = this.getRowById(row_id)
                     for(var i=0;i<r.childNodes.length;i++){
                                 var pfix="";

//#__pro_feature:21092006{
//#column_hidden:21092006{
                                 if ((this._hrrar)&&(this._hrrar[i]))  pfix="display:none;";
//#}
//#}
                                 if (_isIE)
                                    r.childNodes[i].style.cssText = pfix+"width:"+r.childNodes[i].style.width+";"+styleString;
                                 else
                            r.childNodes[i].style.cssText = pfix+"width:"+r.childNodes[i].style.width+";"+styleString;
                     }

                  }
   /**
   *   @desc: sets background color of row (via bgcolor attribute)
   *   @param: row_id - row id
   *   @param: color - color value
   *   @type: public
   *   @topic: 2,6
   */
   this.setRowColor = function(row_id,color){
		var r = this.getRowById(row_id)
		for(var i=0;i<r.childNodes.length;i++)
			r.childNodes[i].bgColor=color;
	}
   /**
   *   @desc: sets style to cell
   *   @param: row_id - row id
   *   @param: ind - cell index
   *   @param: styleString - style string in common format (exmpl: "color:red;border:1px solid gray;")
   *   @type: public
   *   @topic: 2,6
   */
   this.setCellTextStyle = function(row_id,ind,styleString){
                     var r = this.getRowById(row_id)
                            if (!r) return;
                            var cell=r.childNodes[r._childIndexes?r._childIndexes[ind]:ind];
                            if (!cell) return;
                                 var pfix="";
//#__pro_feature:21092006{
//#column_hidden:21092006{
                                 if ((this._hrrar)&&(this._hrrar[ind]))  pfix="display:none;";
//#}
//#}
                                 if (_isIE)
                                    cell.style.cssText = pfix+"width:"+cell.style.width+";"+styleString;
                                 else
                            cell.style.cssText = pfix+"width:"+cell.style.width+";"+styleString;
                     
                  }

   /**
   *   @desc: sets row text weight to normal
   *   @param: row_id - row id
   *   @type: public
   *   @topic: 2,6
   */
   this.setRowTextNormal = function(row_id){
                     this.getRowById(row_id).style.fontWeight = "normal";
                  }
   /**
   *   @desc: determines if row with specified id exists
   *   @param: row_id - row id
   *   @returns: true if exists, false otherwise
   *   @type: public
   *   @topic: 2,7
   */
   this.doesRowExist = function(row_id){
   	if(this.getRowById(row_id)!=null)
	   return true
	else
	   return false
   	
   }
   
   /**
   *   @desc: determines if row with specified id exists
   *   @param: row_id - row id
   *   @returns: true if exists, false otherwise
   *   @type: deprecated
   *   @topic: 2,7
   */
   this.isItemExists = function(row_id){
                    return this.doesRowExist(row_id) 
                  }

   /**
   *   @desc: gets number of rows in grid
   *   @returns: number of rows in grid
   *   @type: public
   *   @topic: 2,7
   */
   this.getRowsNum = function(){
                     if (this._dload)
                        return  this.limit;
                     return this.rowsCol.length+this.rowsBuffer[0].length;
                  }
   /**
   *   @desc: gets number of columns in grid
   *   @returns: number of columns in grid
   *   @type: public
   *   @topic: 3,7
   */
   this.getColumnsNum = function(){
                     return this.hdr.rows[0].cells.length;
                  }
   /**
   *   @desc: gets number of columns in grid
   *   @returns: number of columns in grid
   *   @type: deprecated
   *   @topic: 3,7
   */
   this.getColumnCount = function(){
                     return this.hdr.rows[0].cells.length;
                  }

   /**
   *   @desc: moves row one position up if possible
   *   @param: row_id -  row id
   *   @type: public
   *   @topic: 2
   */
   this.moveRowUp = function(row_id){
                     var r = this.getRowById(row_id)
                     if (this.isTreeGrid()) return this.moveRowUDTG(row_id,-1);
                     var rInd = this.rowsCol._dhx_find(r)
                                if ((r.previousSibling)&&(rInd!=0)){
        	                        r.parentNode.insertBefore(r,r.previousSibling)
		                            this.rowsCol._dhx_swapItems(rInd,rInd-1)
                                    this.setSizes();
                                    if (this._cssEven)
                                    	this._fixAlterCss(rInd-1);
                                    }
                  }
   /**
   *   @desc: moves row one position down if possible
   *   @param: row_id -  row id
   *   @type: public
   *   @topic: 2
   */
   this.moveRowDown = function(row_id){
                     var r = this.getRowById(row_id)
					 if (this.isTreeGrid()) return this.moveRowUDTG(row_id,1);
					 var rInd = this.rowsCol._dhx_find(r);
					
					 
                            if (r.nextSibling){
	                            this.rowsCol._dhx_swapItems(rInd,rInd+1)							
                                if (r.nextSibling.nextSibling)
                                  r.parentNode.insertBefore(r,r.nextSibling.nextSibling)
                                else
                                    r.parentNode.appendChild(r)
                                this.setSizes();
                                if (this._cssEven)
                                    	this._fixAlterCss(rInd);
                                }
                  }
   /**
   *   @desc: gets dhtmlXGridCellObject object (if no arguments then gets dhtmlXGridCellObject object of currently selected cell)
   *   @param: row_id -  row id
   *   @param: col -  column index
   *   @returns: dhtmlXGridCellObject object (see its methods below)
   *   @type: public
   *   @topic: 4
   */
   this.cellById = function(row_id,col){
   		return this.cells(row_id,col);
   }
   /**
   *   @desc: gets dhtmlXGridCellObject object (if no arguments then gets dhtmlXGridCellObject object of currently selected cell)
   *   @param: row_id -  row id
   *   @param: col -  column index
   *   @returns: dhtmlXGridCellObject object (use it to get/set value to cell etc.)
   *   @type: public
   *   @topic: 4
   */
   this.cells = function(row_id,col){
                     if(arguments.length==0)
                           return this.cells4(this.cell);
                     else
                        var c = this.getRowById(row_id);
                        if (!c && !window.eXcell_math) dhtmlxError.throwError("cell","Row not exists",[row_id,col]);
                        var cell=(c._childIndexes?c.childNodes[c._childIndexes[col]]:c.childNodes[col]);
                  return this.cells4(cell);
                  }
   /**
   *   @desc: gets dhtmlXGridCellObject object
   *   @param: row_index -  row index
   *   @param: col -  column index
   *   @returns: dhtmlXGridCellObject object (see its methods below)
   *   @type: public
   *   @topic: 4
   */
   this.cellByIndex = function(row_index,col){
   		return this.cells2(row_index,col);
   }
   /**
   *   @desc: gets dhtmlXGridCellObject object
   *   @param: row_index -  row index
   *   @param: col -  column index
   *   @returns: dhtmlXGridCellObject object (see its methods below)
   *   @type: public
   *   @topic: 4
   */
   this.cells2 = function(row_index,col){
      var c = this.rowsCol[parseInt(row_index)];
      if (!c && !window.eXcell_math) dhtmlxError.throwError("cell","Row not exists",[row_id,col]);
      var cell=(c._childIndexes?c.childNodes[c._childIndexes[col]]:c.childNodes[col]);
      return this.cells4(cell);
                  }

   /**
   *   @desc: gets exCell editor for row  object and column id
   *   @type: private
   *   @topic: 4
   */
   this.cells3 = function(row,col){
        var cell=(row._childIndexes?row.childNodes[row._childIndexes[col]]:row.childNodes[col]);
      return this.cells4(cell);
                 }
   /**
   *   @desc: gets exCell editor for cell  object
   *   @type: private
   *   @topic: 4
   */
   this.cells4 = function(cell){
   	  var type=window["eXcell_"+(cell._cellType||this.cellType[cell._cellIndex])];
   	  if (type) return new type(cell);
   	  //dhtmlxError.throwError("Incorrect cell type : "+type);
    }
	this.cells5 = function(cell){
		var type=cell._cellType||this.cellType[cell._cellIndex];
		if (!this._ecache[type])
			this._ecache[type]=eval("new eXcell_"+type+"(cell)");
		this._ecache[type].cell=cell;
		return this._ecache[type];
	}    
	this.dma=function(mode){
		if (!this._ecache) this._ecache={};
		if (mode && !this._dma){
			this._dma=this.cells4;
			this.cells4=this.cells5;
		} else if (!mode && this._dma){
			this.cells4=this._dma;
			this._dma=null;
		}
                  }
   /**
   * @desc: gets Combo object of specified column. Use it to change select box value for cell before editor opened
   *   @type: public
   *   @topic: 3,4
   *   @param: col_ind - index of the column to get combo object for
   */
   this.getCombo = function(col_ind){
      if(!this.combos[col_ind]){
         this.combos[col_ind] = new dhtmlXGridComboObject();
      }
      return this.combos[col_ind];
   }
   /**
   *   @desc: sets user data to row
   *   @param: row_id -  row id. if empty then user data is set for grid (not row)
   *   @param: name -  name of user data block
   *   @param: value -  value of user data block
   *   @type: public
   *   @topic: 2,5
   */
   this.setUserData = function(row_id,name,value){
                     try{
                        if(row_id=="")
                           row_id = "gridglobaluserdata";
                        if(!this.UserData[row_id])
                           this.UserData[row_id] = new Hashtable()
                        this.UserData[row_id].put(name,value)
                     }catch(er){
                        alert("UserData Error:"+er.description)
                     }
                  }
   /**
   *   @desc: gets user Data
   *   @param: row_id -  row id. if empty then user data is for grid (not row)
   *   @param: name -  name of user data
   *   @returns: value of user data
   *   @type: public
   *   @topic: 2,5
   */
   this.getUserData = function(row_id,name){
   			this.getRowById(row_id);//parse row if necessary
            if(row_id=="")
               row_id = "gridglobaluserdata";
                var z=this.UserData[row_id];
               return (z?z.get(name):"");
      }

   /**
   *   @desc: manage editibility of the grid
   *   @param: [fl] - set not editable if FALSE, set editable otherwise
   *   @type: public
   *   @topic: 7
   */
   this.setEditable = function(fl){
                     this.isEditable = convertStringToBoolean(fl);
                  }
   /**
   *   @desc: selects row
   *   @param: row_id - row id
   *   @param: multiFL - VOID. select multiple rows
   *   @param: show - true/false - scroll row to view, true by defaul    
   *   @param: call - true to call function on select
   *   @type: deprecated
   *   @topic: 1,2
   */
   this.setSelectedRow = function(row_id, multiFL,show,call){ 
                     if(!call)
                        call = false;
                     this.selectCell(this.getRowById(row_id),0,call,multiFL,false,show);
                  }
   /**
   *   @desc: removes selection from the grid
   *   @type: public
   *   @topic: 1,9
   */
   this.clearSelection = function(){
                     this.editStop()
                     for(var i=0;i<this.selectedRows.length;i++){
                        var r=this.rowsAr[this.selectedRows[i].idd];
                        if (r) r.className=r.className.replace(/rowselected/g,"");
                     }

                     //..
                     this.selectedRows = new dhtmlxArray(0)
                     this.row = null;
                     if(this.cell!=null){
                        this.cell.className = this.cell.className.replace(/cellselected/g,"");
                        this.cell = null;
                     }
                  }
   /**
   *   @desc: copies row content to another existing row
   *   @param: from_row_id - id of the row to copy content from
   *   @param: to_row_id - id of the row to copy content to
   *   @type: public
   *   @topic: 2,5
   */
   this.copyRowContent = function(from_row_id, to_row_id){
                     var frRow = this.getRowById(from_row_id)

                            if (!this.isTreeGrid())
                         for(var i=0;i<frRow.cells.length;i++){
                            this.cells(to_row_id,i).setValue(this.cells(from_row_id,i).getValue())
                         }
                            else
                                this._copyTreeGridRowContent(frRow,from_row_id,to_row_id);

                     //for Mozilla (to avaoid visual glitches)
                     if(!isIE())
                        this.getRowById(from_row_id).cells[0].height = frRow.cells[0].offsetHeight
                  }



	/**
   *   @desc: sets new column header label
   *   @param: col - header column index
   *   @param: label - new label for the cpecified header's column. Can contai img:[imageUrl]Text Label
   *	@param: ind - header row index (default is 0)
   *   @type: public
   *   @topic: 3,6
   */
	this.setColumnLabel = function(c,label,ind){
		this.setHeaderCol(c,label,ind);
	}
   /**
   *   @desc: sets new column header label
   *   @param: col - header column index
   *   @param: label - new label for the cpecified header's column. Can contai img:[imageUrl]Text Label
   *	@param: ind - header row index (default is 0)
   *   @type: public
   *   @topic: 3,6
   */
   this.setHeaderCol = function(c,label,ind){
                     var z=this.hdr.rows[ind||1];
                var col=(z._childIndexes?z._childIndexes[c]:c);
                     if(!this.useImagesInHeader){
                        var hdrHTML = "<div class='hdrcell'>"
                  if(label.indexOf('img:[')!=-1){
                     var imUrl = label.replace(/.*\[([^>]+)\].*/,"$1");
                     label = label.substr(label.indexOf("]")+1,label.length)
                     hdrHTML+="<img width='18px' height='18px' align='absmiddle' src='"+imUrl+"' hspace='2'>"
                  }
                  hdrHTML+=label;
                  hdrHTML+="</div>";
                  z.cells[col].innerHTML = hdrHTML;
                  if (this._hstyles[col]) z.cells[col].style.cssText = this._hstyles[col];

                }else{//if images in header header
                        z.cells[col].style.textAlign = "left";
                        z.cells[col].innerHTML = "<img src='"+this.imgURL+""+label+"' onerror='this.src = \""+this.imgURL+"imageloaderror.gif\"'>";
                        //preload sorting headers (asc/desc)
                        var a = new Image();
                        a.src = this.imgURL+""+label.replace(/(\.[a-z]+)/,".desc$1");
                        this.preloadImagesAr[this.preloadImagesAr.length] = a;
                        var b = new Image();
                        b.src = this.imgURL+""+label.replace(/(\.[a-z]+)/,".asc$1");
                        this.preloadImagesAr[this.preloadImagesAr.length] = b;
                     }
                     
	if ((label||"").indexOf("#")!=-1){
  		var t=label.match(/(^|{)#([^}]+)(}|$)/);
  		if (t){
  			var tn="_in_header_"+t[2];
  			if (this[tn]) this[tn]((this.forceDivInHeader?z.cells[col].firstChild:z.cells[col]),col,label.split(t[0]));
  		}
  	}                     
                  }
   /**
   *   @desc: deletes all rows in grid
   *   @param: header - (boolean) enable/disable cleaning header
   *   @type: public
   *   @topic: 5,7,9
   */
   this.clearAll = function(header){
		if (this._h2){
			this._h2=new dhtmlxHierarchy();
			if (this._fake){
				if (this._realfake)
		  				this._h2=this._fake._h2;
		  			else
		  				this._fake._h2=this._h2;
		 	}
		 }

                  this.limit=this._limitC=0;
                            this.editStop();
                        if (this._dLoadTimer)  window.clearTimeout(this._dLoadTimer);
                            if (this._dload){
                               this.objBox.scrollTop=0;
                               this.limit=this._limitC||0;
                               this._initDrF=true;
                               }

                     var len = this.rowsCol.length;
                     //treegrid
                     if(this.loadedKidsHash!=null){
                        this.loadedKidsHash.clear();
                                this.loadedKidsHash.put("hashOfParents",new Hashtable());
                     }
                     //for some case
                     len = this.obj._rowslength();

                     for(var i=len-1;i>=0;i--){
                       var t_r=this.obj._rows(i);
                        t_r.parentNode.removeChild(t_r);
                     }
                if (header && this.obj.rows[0]){
                    this.obj.rows[0].parentNode.removeChild(this.obj.rows[0]);
                        for(var i=this.hdr.rows.length-1;i>=0;i--){
                          var t_r=this.hdr.rows[i];
                           t_r.parentNode.removeChild(t_r);
                        }
                        if (this.ftr){
                        	this.ftr.parentNode.removeChild(this.ftr);
                        	this.ftr=null;
                        }               
               		this._aHead=this.ftr=this._aFoot=null;
               		this._hrrar=[];
                }

                     //..
                     this.row = null;
                     this.cell = null;


                     this.rowsCol = new dhtmlxArray(0)
                     this.rowsAr = new Array(0);//array of rows by idd
                     this.rowsBuffer = new Array(new dhtmlxArray(0),new dhtmlxArray(0));//buffer of rows loaded, but not rendered (array of ids, array of cell values arrays)
                     this.UserData = new Array(0)
					 this.selectedRows = new dhtmlxArray(0);

                     if(this.pagingOn){
                     	this.xmlFileUrl="";
                     	this.recordsNoMore=null;
                        this.changePage(1);
                        //this.createPagingBlock();
                     }

             //  if (!this._fake){
               	/*
                   if ((this._hideShowColumn)&&(this.hdr.rows[0]))
                      for (var i=0; i<this.hdr.rows[0].cells.length; i++)
                          this._hideShowColumn(i,"");
               this._hrrar=new Array();*/
            //}
            if (this._contextCallTimer) window.clearTimeout(this._contextCallTimer);


            if (this._sst)
               this.enableStableSorting(true);

               this.setSortImgState(false); 
                    this.setSizes();
                    //this.obj.scrollTop = 0;
            
            this.callEvent("onClearAll",[]);
   }


   /**
   *   @desc: sorts grid by specified field
   *    @invoke: header click
   *   @param: [ind] - index of the field
   *   @param: [repeatFl] - if to repeat last sorting
   *   @type: private
   *   @topic: 3
   */
   this.sortField = function(ind,repeatFl,r_el){
                  if(this.getRowsNum()==0)
                     return false;
                  var el = this.hdr.rows[0].cells[ind];
                        if (!el) return; //somehow
             // if (this._dload  && !this.callEvent("onBeforeSorting",[ind,this]) ) return true;

                  if(el.tagName == "TH" && (this.fldSort.length-1)>=el._cellIndex && this.fldSort[el._cellIndex]!='na'){//this.entBox.fieldstosort!="" &&
                     if((((this.sortImg.src.indexOf("_desc.gif")==-1) && (!repeatFl)) || ((this.sortImg.style.filter!="") && (repeatFl))) && (this.fldSorted==el))
                        var sortType = "des";
                     else
                        var sortType = "asc";

				  if (!this.callEvent("onBeforeSorting",[ind,this,sortType])) return;
				  this.sortImg.src = this.imgURL+"sort_"+(sortType=="asc"?"asc":"desc")+".gif";

                     //for header images
                     if(this.useImagesInHeader){
                   var cel=this.hdr.rows[1].cells[el._cellIndex].firstChild;
                        if(this.fldSorted!=null){
                     var celT=this.hdr.rows[1].cells[this.fldSorted._cellIndex].firstChild;
                           celT.src = celT.src.replace(/\.[ascde]+\./,".");
                        }
                        cel.src = cel.src.replace(/(\.[a-z]+)/,"."+sortType+"$1")
                     }
                     //.
                     this.sortRows(el._cellIndex,this.fldSort[el._cellIndex],sortType)
                     this.fldSorted = el;this.r_fldSorted = r_el;
                var c=this.hdr.rows[1];
                var c=r_el.parentNode;
                     var real_el=c._childIndexes?c._childIndexes[el._cellIndex]:el._cellIndex;
                     this.setSortImgPos(false,false,false,r_el);
                     this.callEvent("onAfterSorting",[]);
                  }
               }

//#__pro_feature:21092006{
//#custom_sort:21092006{
    /**
    *   @desc: set custom sorting (custom sort has three params - valueA,valueB,order; where order can be asc or des)
    *   @param: func - function to use for comparison
    *   @param:   col - index of column to apply custom sorting to
    *   @type: public
    *   @edition: Professional
    *   @topic: 3
    */
    this.setCustomSorting = function(func,col){
       if (!this._customSorts) this._customSorts=new Array();
       this._customSorts[col] = (typeof(func)=="string")?eval(func):func;
       this.fldSort[col]="cus";
    }
//#}
//#}

   /**
   *   @desc: specify if values passed to Header are images file names
   *   @param: fl - true to treat column header values as image names
   *   @type: public
   *   @before_init: 1
   *   @topic: 0,3
   */
   this.enableHeaderImages = function(fl){
      this.useImagesInHeader = fl;
   }

   /**
   *   @desc: set header label and default params for new headers
   *   @param: hdrStr - header string with delimiters
   *   @param: splitSign - string used as a split marker, optional. Default is "#cspan"
   *   @param: styles - array of header styles
   *   @type: public
   *   @before_init: 1
   *   @topic: 0,3
   */
   this.setHeader = function(hdrStr,splitSign,styles){
   	if (typeof(hdrStr)!="object")
      var arLab = this._eSplit(hdrStr);
    else arLab=[].concat(hdrStr);
    
      var arWdth = new Array(0);
      var arTyp = new dhtmlxArray(0);
      var arAlg = new Array(0);
      var arVAlg = new Array(0);
      var arSrt = new Array(0);
      for(var i=0;i<arLab.length;i++){
         arWdth[arWdth.length] = Math.round(100/arLab.length);
         arTyp[arTyp.length] = "ed";
         arAlg[arAlg.length] = "left";
         arVAlg[arVAlg.length] = "";//top
         arSrt[arSrt.length] = "na";
      }

      this.splitSign = splitSign||"#cspan";
      this.hdrLabels = arLab;
      this.cellWidth = arWdth;
      this.cellType =  arTyp;
      this.cellAlign =  arAlg;
      this.cellVAlign =  arVAlg;
      this.fldSort = arSrt;
      this._hstyles = styles||[];
   }
   	/**
   *   @desc: 
   *   @param: str - ...
   *   @type: private
   */
	this._eSplit=function(str){
		if (![].push) return str.split(this.delim);
	 	var a="r"+(new Date()).valueOf();
	 	var z=this.delim.replace(/([\|\+\*\^])/g,"\\$1")
   		return (str||"").replace(RegExp(z,"g"),a).replace(RegExp("\\\\"+a,"g"),this.delim).split(a); 
   		
	}

   /**
   *   @desc: get column type by column index
   *   @param: cInd - column index
   *   @returns:  type code
   *   @type: public
   *   @topic: 0,3,4
   */
    this.getColType = function(cInd) {
       return this.cellType[cInd];
    }

   /**
   *   @desc: get column type by column ID
   *   @param: cID - column id
   *   @returns:  type code
   *   @type: public
   *   @topic: 0,3,4
   */
    this.getColTypeById = function(cID) {
       return this.cellType[this.getColIndexById(cID)];
    }

   /**
   *   @desc: set column types
   *   @param: typeStr - type codes list (default delimiter is ",")
   *   @before_init: 2
   *   @type: public
   *   @topic: 0,3,4
   */
   this.setColTypes = function(typeStr){
      this.cellType = dhtmlxArray(typeStr.split(this.delim));
          this._strangeParams=new Array();
        for (var i=0; i<this.cellType.length; i++)
        if ((this.cellType[i].indexOf("[")!=-1))
            {
                var z=this.cellType[i].split(/[\[\]]+/g);
                this.cellType[i]=z[0];
                this.defVal[i]=z[1];
                if (z[1].indexOf("=")==0){
                    this.cellType[i]="math";
                    this._strangeParams[i]=z[0];
                    }
            }
   }
   /**
   *   @desc: set column sort types (avaialble: str, int, date, na or function object for custom sorting)
   *   @param: sortStr - sort codes list with default delimiter
   *   @before_init: 1
   *   @type: public
   *   @topic: 0,3,4
   */
   this.setColSorting = function(sortStr){
      this.fldSort = sortStr.split(this.delim)
//#__pro_feature:21092006{
//#custom_sort:21092006{
        for (var i=0; i<this.fldSort.length; i++)
            if (((this.fldSort[i]).length>4)&&(typeof(window[this.fldSort[i]])=="function"))
                {
                   if (!this._customSorts) this._customSorts=new Array();
                   this._customSorts[i]=window[this.fldSort[i]];
                   this.fldSort[i]="cus";
                }
//#}
//#}
   }
   /**
   *   @desc: set align of values in columns
   *   @param: alStr - list of align values (possible values are: right,left,center,justify). Default delimiter is ","
   *   @before_init: 1
   *   @type: public
   *   @topic: 0,3
   */
   this.setColAlign = function(alStr){
      this.cellAlign = alStr.split(this.delim)
   }
   /**
   *   @desc: set vertical align of columns
   *   @param: valStr - vertical align values list for columns (possible values are: baseline,sub,super,top,text-top,middle,bottom,text-bottom)
   *   @before_init: 1
   *   @type: public
   *   @topic: 0,3
   */
   this.setColVAlign = function(valStr){
      this.cellVAlign = valStr.split(this.delim)
   }

   /**
   *   @desc: sets grid to multiline row support (call before init)
   *   @param:   fl - true to set multiline support
   *   @type: deprecated
   *   @before_init: 1
   *   @topic: 0,2
   */
   this.setMultiLine = function(fl){
      if(fl==true)
         this.multiLine = -1;
   }
   /**
   * 	@desc: create grid with no header. Call before initialization, but after setHeader. setHeader have to be called in any way as it defines number of columns
   *   @param: fl - true to use no header in the grid
   *   @type: public
   *   @before_init: 1
   *   @topic: 0,7
   */
   this.setNoHeader = function(fl){
      if(convertStringToBoolean(fl)==true)
         this.noHeader = true;
   }
   /**
   *   @desc: scrolls row to the visible area
   *   @param: rowID - row id
   *   @type: public
   *   @topic: 2,7
   */
   this.showRow = function(rowID){
      if(this.pagingOn){
         if (this.rowsAr[rowID])
            this.changePage(Math.floor(this.getRowIndex(rowID)/this.rowsBufferOutSize)+1);
         else
            while((!this.rowsAr[rowID]) && ( this.rowsBuffer[0].length>0 || !this.recordsNoMore ))
               this.changePage(this.currentPage+1);
      }
	  var c=this.getRowById(rowID).cells[0]; while (c && c.style.display=="none") c=c.nextSibling;
      if (c) this.moveToVisible(c,true)
   }

   /**
   *   @desc: modify default style of grid and its elements. Call before or after Init
   *   @param: ss_header - style def. expression for header
   *   @param: ss_grid - style def. expression for grid cells
   *   @param: ss_selCell - style def. expression for selected cell
   *   @param: ss_selRow - style def. expression for selected Row
   *   @type: public
   *   @before_init: 2
   *   @topic: 0,6
   */
   this.setStyle = function(ss_header,ss_grid,ss_selCell,ss_selRow){
      this.ssModifier = [ss_header, ss_grid , ss_selCell,ss_selCell, ss_selRow];
     var prefs=["#"+this.entBox.id+" table.hdr td","#"+this.entBox.id+" table.obj td","#"+this.entBox.id+" table.obj tr.rowselected td.cellselected","#"+this.entBox.id+" table.obj td.cellselected","#"+this.entBox.id+" table.obj tr.rowselected td"];

     for (var i=0; i<prefs.length; i++)
      if (this.ssModifier[i]){
         if (_isIE)
            this.styleSheet[0].addRule(prefs[i],this.ssModifier[i]);
             else
            this.styleSheet[0].insertRule(prefs[i]+" { "+this.ssModifier[i]+" } ",0);
      }
   }
   /**
   *   @desc: colorize columns  background.
   *   @param: clr - colors list
   *   @type: public
   *   @before_init: 1
   *   @topic: 3,6
   */
   this.setColumnColor = function(clr){
      this.columnColor = clr.split(this.delim)
   }

   /**
   *   @desc: set even/odd css styles
   *   @param: cssE - name of css class for even rows
   *   @param: cssU - name of css class for odd rows
   *   @param: perLevel - true/false - mark rows not by order, but by level in treegrid
   *   @param: levelUnique - true/false - creates additional unique css class based on row level
   *   @type: public
   *   @before_init: 1
   *   @topic: 3,6
   */
   this.enableAlterCss = function(cssE,cssU,perLevel,levelUnique){
        if (cssE||cssU)
            this.setOnGridReconstructedHandler(function(){ 
            	if (!this._cssSP)
                	this._fixAlterCss();
            });


	  this._cssSP=perLevel;
	  this._cssSU=levelUnique;
	  this._cssEven = cssE;
      this._cssUnEven = cssU;
   }

   /**
   *   @desc: recolor grid from defined point
   *   @type: private
   *   @before_init: 1
   *   @topic: 3,6
   */
   this._fixAlterCss = function(ind){
 		if (this._cssSP && this.isTreeGrid()) return this._fixAlterCssTG(ind);
        ind=ind||0;
        var j=ind;
        for (var i=ind; i<this.rowsCol.length; i++){
            if (!this.rowsCol[i]) continue;
            if (this.rowsCol[i].style.display!="none"){
            if (this.rowsCol[i].className.indexOf("rowselected")!=-1){
                if (j%2==1)
                    this.rowsCol[i].className=this._cssUnEven+" rowselected"+(this.rowsCol[i]._css||"");
                else
                    this.rowsCol[i].className=this._cssEven+" rowselected"+(this.rowsCol[i]._css||"");
            }
            else{
                if (j%2==1)
                    this.rowsCol[i].className=this._cssUnEven+(this.rowsCol[i]._css||"");
                else
                    this.rowsCol[i].className=this._cssEven+(this.rowsCol[i]._css||"");
            }
                j++;
            }
        }
   }

//#__pro_feature:21092006{
/**
*     @desc: clear wasChanged state for all cells in grid
*     @type: public
*     @edition: Professional
*     @topic: 7
*/
this.clearChangedState = function(){
   for (var i=0; i<this.rowsCol.length; i++){
      var row=this.rowsCol[i];
      var cols=row.childNodes.length;
      for (var j=0; j<cols; j++)
         row.childNodes[j].wasChanged=false;
   }
};

/**
*     @desc: get list of IDs of changed rows
*     @type: public
*     @edition: Professional
*     @return: list of ID of changed rows
*     @topic: 7
*/
this.getChangedRows = function(){
   var res=new Array();
   this.forEachRow(function(id){
      var row=this.rowsAr[id];
      var cols=row.childNodes.length;
      for (var j=0; j<cols; j++)
        if (row.childNodes[j].wasChanged) {
         res[res.length]=row.idd;
         break;
         }
	   })
   return res.join(this.delim);
};


//#serialization:21092006{

this._sUDa = false;
this._sAll = false;

/**
*     @desc: configure XML serialization
*     @type: public
*     @edition: Professional
*     @param: userData - enable/disable user data serialization
*     @param: fullXML - enable/disable full XML serialization (selection state)
*     @param: config - serialize grid configuration
*     @param: changedAttr - include changed attribute
*     @param: onlyChanged - include only Changed  rows in result XML
*     @param: asCDATA - output cell values as CDATA sections (prevent invalid XML)
*     @topic: 0,5,7
*/
this.setSerializationLevel = function(userData,fullXML,config,changedAttr,onlyChanged,asCDATA){
   this._sUDa = userData;
   this._sAll = fullXML;
   this._sConfig = config;
   this._chAttr = changedAttr;
   this._onlChAttr = onlyChanged;
   this._asCDATA = asCDATA;
}



/**
*     @desc: configure which column must be serialized (if you do not use this method, then all columns will be serialized)
*     @type: public
*     @edition: Professional
*     @param: list - list of true/false values separated by comma, if list empty then all fields will be serialized
*     @topic: 0,5,7
*/
this.setSerializableColumns=function(list){
    if (!list) {
        this._srClmn=null;
        return;
        }
    this._srClmn=(list||"").split(",");
    for (var i=0; i<this._srClmn.length; i++)
        this._srClmn[i]=convertStringToBoolean(this._srClmn[i]);
}

/**
*     @desc: serialize a collection of rows
*     @type: private
*     @topic: 0,5,7
*/
this._serialise = function(rCol,inner,closed){
     this.editStop()
    var out=[];
    //rows collection
    var i=0;
    var j=0;
    var leni=(this._dload)?this.rowsBuffer[0].length:rCol.length;

	if (this.isTreeGrid()){
		var f=function(id,f){
			var str=[];
			var z=self._h2.get[id];
			if (0!=id)
				str.push(self._serializeRow(self.rowsAr[id],i));
			if (z.childs.length==0 && self.rowsAr[id]._xml){
			  var xar=self.rowsAr[id]._xml;
			  for(var i=0; i<xar.length; i++)
				if (self.xmlSerializer)
                   	str.push(self.xmlSerializer.serializeToString(xar[i]));
               	else
                   str.push(xar[i].xml);
			} else
			for(var i=0; i<z.childs.length; i++)
				str.push(f(z.childs[i].id,f));
			if (0!=id)				
				str.push("</row>\n");
			return str.join("");
		}
		out.push(f(0,f));
	}else
    for(i; i<leni; i++){
		var r = rCol[i];
		var temp=this._serializeRow(r,i);
		out.push(temp);
        if ((temp!="") && r && (!r._sRow) && (!r._rLoad))
			out.push("</row>");
   }

    return [out.join(""),j+i];
}

/**
*   @desc: serialize xml node to XML string
*   @param: r - TR or xml node (row)
*   @retruns: string - xml representation of passed row
*   @type: private
*/
this._manualXMLSerialize = function(r){
   var out = "<row id='"+r.getAttribute("id")+"'>";
   var i=0;
    for(var jj=0;jj<r.childNodes.length;jj++){
      var z=r.childNodes[jj];
      if (z.tagName!="cell") continue;
        if ((!this._srClmn)||(this._srClmn[i]))
         out += "<cell>"+(z.firstChild?z.firstChild.data:"")+"</cell>";
      i++;
      }
   out+="</row>";
    return out;
}


/**
*   @desc: serialize TR or xml node to grid formated xml (row tag)
*   @param: r - TR or xml node (row)
*   @retruns: string - xml representation of passed row
*   @type: private
*/
this._serializeRow = function(r,i){
    var out = [];
    if ((!r)||(r._sRow)||(r._rLoad)) {
		if (this._onlChAttr) return "";
         if (this.rowsBuffer[1][i]){
               if (this.xmlSerializer)
                   out=this.xmlSerializer.serializeToString(this.rowsBuffer[1][i]);
               else
                   out=this.rowsBuffer[1][i].xml;
         }
         return out;
     }


      var selStr = "";

      //serialize selection
      if(this._sAll && this.selectedRows._dhx_find(r)!=-1)
         selStr = " selected='1'";
      out.push("<row id='"+r.idd+"'"+selStr+" "+((this._h2 && this._h2.get[r.idd].state=="minus")?"open='1'":"")+">");
      //userdata
      if(this._sUDa && this.UserData[r.idd]){
         keysAr = this.UserData[r.idd].getKeys()
           for(var ii=0;ii<keysAr.length;ii++){
            out.push("<userdata name='"+keysAr[ii]+"'>"+this.UserData[r.idd].get(keysAr[ii])+"</userdata>");
         }
      }


      //cells
     var changeFl=false;
      for(var jj=0;jj<r.childNodes.length;jj++){
            if ((!this._srClmn)||(this._srClmn[jj]))
                {
                var cvx=r.childNodes[jj];
                out.push("<cell");

                var zx=this.cells(r.idd,cvx._cellIndex);
                if (zx.cell)
                    zxVal=zx[this._agetm]();
                else zxVal="";

            if (zxVal===null) zxVal="";
            if (this._asCDATA)
               zxVal="<![CDATA["+zxVal+"]]>";

//#colspan:20092006{
                if ((this._ecspn)&&(cvx.colSpan)&&cvx.colSpan>1)
                    out.push(" colspan=\""+cvx.colSpan+"\" ");
//#}

			if( zx.getSerializeAttributes )
     			out.push(" "+zx.getSerializeAttributes());


            if (this._chAttr){
               if (zx.wasChanged()){
                  out.push(" changed=\"1\"");
                  changeFl=true;
                  }
               }
            else
               if ((this._onlChAttr)&&(zx.wasChanged())) changeFl=true;

                if (this._sAll)
                  out.push((this._h2?(" image='"+this._h2.get[r.idd].image+"'"):"")+">"+zxVal+"</cell>");
                else
                 out.push(">"+zxVal+"</cell>");
//#colspan:20092006{
                if ((this._ecspn)&&(cvx.colSpan)){
                    cvx=cvx.colSpan-1;
                    for (var u=0; u<cvx; u++)
                        out.push("<cell/>");
                        }
//#}
                }
      }
     if ((this._onlChAttr)&&(!changeFl)&&(!r._added)) return "";
      return out.join("");
}

/**
*     @desc: serialize grid configuration
*     @type: private
*     @topic: 0,5,7
*/
this._serialiseConfig=function(){
    var out="<head>";
        for (var i=0; i<this.hdr.rows[0].cells.length; i++){
            out+="<column width='"+this.cellWidthPX[i]+"' align='"+this.cellAlign[i]+"' type='"+this.cellType[i]+"' sort='"+this.fldSort[i]+"' color='"+this.columnColor[i]+"'"+(this.columnIds[i]?(" id='"+this.columnIds[i]+"'"):"")+">";
            out+=this.getHeaderCol(i);
            var z=this.getCombo(i);
            if (z)
                for (var j=0; j<z.keys.length; j++)
                    out+="<option value='"+z.keys[j]+"'>"+z.values[j]+"</option>";
            out+="</column>"
            }
    return out+="</head>";
}
/**
*     @desc: get actual xml of grid. The depth of serialization can be set with setSerializationLevel method
*     @type: public
*     @edition: Professional
*     @topic: 5,7
*/
this.serialize = function(){
    if(_isFF)
      this.xmlSerializer = new XMLSerializer();

   var out='<?xml version="1.0"?><rows>';
        if (this._mathSerialization)
             this._agetm="getMathValue";
        else this._agetm="getValue";

   if(this._sUDa && this.UserData["gridglobaluserdata"]){
      var keysAr = this.UserData["gridglobaluserdata"].getKeys()
      for(var i=0;i<keysAr.length;i++){
         out += "<userdata name='"+keysAr[i]+"'>"+this.UserData["gridglobaluserdata"].get(keysAr[i])+"</userdata>";
      }

   }

    if (this._sConfig)
        out+=this._serialiseConfig();
    out+=this._serialise(this.rowsCol)[0];


    if (!this._dload){
       //rows buffer
       for(var i=0;i<this.rowsBuffer[1].length;i++){
          if(this.rowsBuffer[1][i].tagName=="TR"){

         }else{
         if (!this._onlChAttr){
            if (this._srClmn)
               out += this._manualXMLSerialize(this.rowsBuffer[1][i]);
            else
                  if(!this.xmlSerializer)//ie
                      out += this.rowsBuffer[1][i].xml;
                   else{//mozilla
                      out += this.xmlSerializer.serializeToString(this.rowsBuffer[1][i]);
                   }
         }
         }
       }
    }
   out+='</rows>';
   return out;
}
//#}
//#}

/*SET EVENT HANDLERS*/
//#events_basic:21092006{
   /**
   *     @desc: set function called when row selected  ( event is async )
   *     @param: func - event handling function (or its name)
   *     @param: anyClick - call handler on any click event, react only on changed row by default
   *     @type: deprecated
   *     @topic: 10
   *     @event: onRowSelect
   *     @eventdesc: Event raised immideatly after row was clicked.
   *     @eventparam:  ID of clicked row
   *     @eventparam:  index of clicked cell
   */
   this.setOnRowSelectHandler = function(func,anyClick){
        this.attachEvent("onRowSelect",func);
      this._chRRS=(!convertStringToBoolean(anyClick));
   }


   /**
   *     @desc: set function called on grid scrolling
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *     @event: onScroll
   *     @eventdesc: Event raised immideatly after scrolling occured
   *     @eventparam:  scroll left
   *     @eventparam:  scroll top
   */
   this.setOnScrollHandler = function(func){
        this.attachEvent("onScroll",func);
   }

   /**
   *     @desc: set function called when cell editted
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *   @event: onEditCell
   *     @eventdesc: Event raises 1-3 times depending on cell type and settings
   *     @eventparam:  stage of editting (0-before start[can be canceled if returns false],1-editor opened,2-editor closed)
   *     @eventparam:  ID or row
   *     @eventparam:  index of cell
   *     @eventparam:  new value ( only for stage 2 )
   *     @eventparam:  old value ( only for stage 2 )
   *     @returns:   for stage (0) - false - deny editing; for stag (2) - false - revert to old value, (string) - set (string) instead of new value
   */
   this.setOnEditCellHandler = function(func){
        this.attachEvent("onEditCell",func);
   }
   /**
   *     @desc: set function called when checkbox or radiobutton was clicked
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *     @event: onCheckbox
   *     @eventdesc: Event raises after state was changed.
   *     @eventparam:  ID or row
   *     @eventparam:  index of cell
   *     @eventparam:  state of checkbox/radiobutton
   */
   this.setOnCheckHandler = function(func){
        this.attachEvent("onCheckbox",func);
   }

   /**
   *     @desc: set function called when user press Enter
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *   @event: onEnter
   *     @eventdesc: Event raised immideatly after Enter pressed.
   *     @eventparam:  ID or row
   *     @eventparam:  index of cell
   */
   this.setOnEnterPressedHandler = function(func){
        this.attachEvent("onEnter",func);
   }

   /**
   *     @desc: set function called before row removed from grid
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *      @event: onBeforeRowDeleted
   *     @eventdesc: Event raised right before row deleted (if returns false, deletion canceled)
   *     @eventparam:  ID or row
   */
   this.setOnBeforeRowDeletedHandler = function(func){
        this.attachEvent("onBeforeRowDeleted",func);
   }
   /**
   *     @desc: set function called after row added to grid
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *      @event: onRowAdded
   *     @eventdesc: Event raised right after row was added to grid
   *     @eventparam:  ID or row
   */
   this.setOnRowAddedHandler = function(func){
        this.attachEvent("onRowAdded",func);
   }

   /**
   *     @desc: set function called when row added/deleted or grid reordered
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *     @event: onGridReconstructed
   *     @eventdesc: Event raised immideatly after row was clicked.
   *     @eventparam:  grid object
   */
   this.setOnGridReconstructedHandler = function(func){
        this.attachEvent("onGridReconstructed",func);
   }
/**
*     @desc: set function called on each resizing itteration
*     @param: func - event handling function
*     @type: deprecated

*     @topic: 10
*     @event:  onResize
*     @eventdesc: event fired on each resize itteration
*     @eventparam: cell index
*     @eventparam: cell width
*     @eventparam: grid object
*     @eventreturns: if event returns false - the resizig denied
*/
	dhtmlXGridObject.prototype.setOnResize=function(func){
                this.attachEvent("onResize",func);
    };
       
//#}

//#__pro_feature:21092006{
//#events_adv:21092006{
/**
*     @desc: set function called moment before row selected in grid
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event:  onBeforeSelect
*     @eventdesc: event fired moment before row in grid get selection
*     @eventparam: new selected row
*     @eventparam: old selected row
*     @eventreturns: false - block selection
*/
   dhtmlXGridObject.prototype.setOnBeforeSelect=function(func){
                this.attachEvent("onBeforeSelect",func);
    };
/**
*     @desc: set function called after row created
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event:  onRowCreated
*     @eventdesc: event fired after row created in grid, and filled with data
*     @eventparam: row id
*     @eventparam: row object
*     @eventparam: related xml ( if available )
*/
   dhtmlXGridObject.prototype.setOnRowCreated=function(func){
                this.attachEvent("onRowCreated",func);
    };

/**
*     @desc: set function called after xml loading/parsing ended
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event:  onXLE
*     @eventdesc: event fired simultaneously with ending XML parsing, new items already available in tree
*     @eventparam: grid object
*     @eventparam: count of nodes added
*/
   dhtmlXGridObject.prototype.setOnLoadingEnd=function(func){
                this.attachEvent("onXLE",func);
    };

/**
*     @desc: set function called after value of cell changed by user actions
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event:  onCellChanged
*     @eventdesc: event fired after value was changed
*     @eventparam: row ID
*     @eventparam: cell index
*     @eventparam: new value
*/
   dhtmlXGridObject.prototype.setOnCellChanged=function(func){
                this.attachEvent("onCellChanged",func);
		    }; 
/**
*     @desc: set function called before xml loading started
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event: onXLS
*     @eventdesc: event fired before request for new XML sent to server
*     @eventparam: grid object
*/
   dhtmlXGridObject.prototype.setOnLoadingStart=function(func){
                this.attachEvent("onXLS",func);
    };




/**
*     @desc: set function called before sorting of data started, didn't occur while calling grid.sortRows
*     @param: func - event handling function
*     @type: deprecated
*     @edition: Professional
*     @topic: 10
*     @event:  onBeforeSorting
*     @eventdesc: event called before sorting of data started
*     @eventparam: index of sorted column
*     @eventparam: grid object
*     @eventparam: direction of sorting asc/desc
*     @eventreturns: if event returns false - the sorting denied
*/
   dhtmlXGridObject.prototype.setOnColumnSort=function(func){
            this.attachEvent("onBeforeSorting",func);
        };

   /**
   *     @desc: set function called when row selection changed
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *      @edition: Professional
   *     @event: onSelectStateChanged
   *     @eventdesc: Event raised immideatly after selection state was changed
   *     @eventparam:  ID or list of IDs of selected row(s)
   */
   this.setOnSelectStateChanged = function(func){
        this.attachEvent("onSelectStateChanged",func);
   }

   /**
   *     @desc: set function called when row was dbl clicked
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *      @edition: Professional
   *      @event: onRowDblClicked
   *     @eventdesc: Event raised right after row was double clicked, before cell editor opened by dbl click. If retuns false, event canceled;
   *     @eventparam:  ID or row
   *     @eventparam:  index of column
   */
   this.setOnRowDblClickedHandler = function(func){
        this.attachEvent("onRowDblClicked",func);
   }

   /**
   *     @desc: set function called when header was clicked
   *     @param: func - event handling function (or its name)
   *     @type: deprecated
   *     @topic: 10
   *     @edition: Professional
   *     @event: onHeaderClick
   *     @eventdesc: Event raised right after header was clicked, before sorting or any other actions;
   *     @eventparam:  index of column
   *     @eventparam:  related javascript event object   
   *     @eventreturns: if event returns false - defaul action denied
   */
   this.setOnHeaderClickHandler = function(func){
        this.attachEvent("onHeaderClick",func);
   }



//#}
//#}


/**
*     @desc: set function called after resizing finished
*     @param: func - event handling function
*     @type: deprecated
*     @topic: 10
*     @event:  onResizeEnd
*     @eventdesc: event fired after resizing of column finished
*     @eventparam: grid object
*/
   dhtmlXGridObject.prototype.setOnResizeEnd=function(func){
                this.attachEvent("onResizeEnd",func);
    };

   /**
   *    @desc: returns absolute left and top position of specified element
   *    @returns: array of two values: absolute Left and absolute Top positions
   *    @param: oNode - element to get position of
   *   @type: private
   *   @topic: 8
   */
   this.getPosition = function(oNode,pNode){

                  if(!pNode)
                        var pNode = document.body

                  var oCurrentNode=oNode;
                  var iLeft=0;
                  var iTop=0;
                  while ((oCurrentNode)&&(oCurrentNode!=pNode)){//.tagName!="BODY"){
               iLeft+=oCurrentNode.offsetLeft-oCurrentNode.scrollLeft;
               iTop+=oCurrentNode.offsetTop-oCurrentNode.scrollTop;
               oCurrentNode=oCurrentNode.offsetParent;//isIE()?:oCurrentNode.parentNode;
                  }
              if (pNode == document.body ){
                 if (_isIE){
                 if (document.documentElement.scrollTop)
                  iTop+=document.documentElement.scrollTop;
                 if (document.documentElement.scrollLeft)
                  iLeft+=document.documentElement.scrollLeft;
                  }
                  else
                       if (!_isFF){
                             iLeft+=document.body.offsetLeft;
                           iTop+=document.body.offsetTop;
                  }
                 }
                     return new Array(iLeft,iTop);
               }
   /**
   *   @desc: gets nearest parent of specified type
   *   @param: obj - input object
   *   @param: tag - string. tag to find as parent
   *   @returns: object. nearest paraent object (including spec. obj) of specified type.
   *   @type: private
   *   @topic: 8
   */
   this.getFirstParentOfType = function(obj,tag){
      while(obj && obj.tagName!=tag && obj.tagName!="BODY"){
         obj = obj.parentNode;
      }
      return obj;
   }

/*METHODS deprecated*/
   /**
   *   @desc: deprecated. sets number of columns
   *   @param: cnt - number of columns
   *   @type: void
   *   @topic: 3,7
   */
   this.setColumnCount = function(cnt){alert('setColumnCount method deprecated')}
   /**
   *   @desc: deprecated. repaint of the grid
   *   @topic: 7
   *   @type: void
   */
   this.showContent = function(){alert('showContent method deprecated')}

/*INTERNAL EVENT HANDLERS*/
      this.objBox.onscroll = new Function("","this.grid._doOnScroll()")
    if ((!_isOpera)||(_OperaRv>8.5))
    {
   this.hdr.onmousemove = new Function("e","this.grid.changeCursorState(e||window.event)");
      this.hdr.onmousedown = new Function("e","return this.grid.startColResize(e||window.event)");
    }
   this.obj.onmousemove = this._drawTooltip;
   this.obj.onclick = new Function("e","this.grid._doClick(e||window.event); if (this.grid._sclE) this.grid.editCell(e||window.event);  (e||event).cancelBubble=true; ");
   
   if (_isMacOS) {
	this.entBox.oncontextmenu = new Function("e","return this.grid._doContClick(e||window.event);");   
	}
   this.entBox.onmousedown = new Function("e","return this.grid._doContClick(e||window.event);");
   this.obj.ondblclick = new Function("e","if(!this.grid.wasDblClicked(e||window.event)){return false}; if (this.grid._dclE) this.grid.editCell(e||window.event);  (e||event).cancelBubble=true;");
   this.hdr.onclick = this._onHeaderClick;
   this.sortImg.onclick= function(){ self._onHeaderClick.apply({grid:self},[null,self.r_fldSorted]); };
   this.hdr.ondblclick = this._onHeaderDblClick;
   

   //VOID this.grid.ondblclick = this.onDoubleClick;
    if (!document.body._dhtmlxgrid_onkeydown){
      dhtmlxEvent(document,"keydown",new Function("e","if (globalActiveDHTMLGridObject) return globalActiveDHTMLGridObject.doKey(e||window.event);  return true;"));
      document.body._dhtmlxgrid_onkeydown=true;
    }

   dhtmlxEvent(document.body,"click",function(){ if (self.editStop) self.editStop();});

//nb:document.body.attachEvent("onclick",new Function("","if(this.document.getElementById('"+this.entBox.id+"').grid.isActive==-1)this.document.getElementById('"+this.entBox.id+"').grid.setActive(false)"))
   //activity management
    this.entBox.onbeforeactivate = new Function("","this._still_active=null; this.grid.setActive(); event.cancelBubble=true;");
   this.entBox.onbeforedeactivate = new Function("","if (this.grid._still_active) this.grid._still_active=null; else this.grid.isActive=false; event.cancelBubble=true;");
   //postprocessing events (method can be redeclared to react on some events during processing)
   this.doOnRowAdded = function(row){};
	if (this.entBox.style.height.toString().indexOf("%")!=-1)
    	this._setAutoResize();
  return this;
}


   /**
   *   @desc: detect is current grid is a treeGrid
   *   @type: private
   *   @topic: 2
   */
   dhtmlXGridObject.prototype.isTreeGrid=    function(){
        return (this.cellType._dhx_find("tree")!=-1);
    }

   /**
   *   @desc: adds row to the specified position (by default to the beginning of the grid)
   *   @param: new_id - id for new row
   *   @param: text - Array of values or String(with delimiter as in delimiter parameter)
   *   @param: [ind] - index of row (0 by default)
   *   @returns: new row dom object
   *   @type: public
   *   @topic: 2
   */
   dhtmlXGridObject.prototype.addRow=function(new_id,text,ind){
      var r =  this._addRow(new_id,text,ind);
      if (!this.dragContext)
      	this.callEvent("onRowAdded",[new_id]);
	  this.callEvent("onRowCreated",[r.idd,r,null]);
      if(this.pagingOn)
         this.changePage(this.currentPage)

      this.setSizes();
      r._added=true;

                       this.callEvent("onGridReconstructed",[]);

      return r;
   }


      /**
      *   @desc: first step of add row,  created a TR
      *   @type: private
      */
    dhtmlXGridObject.prototype._prepareRow=function(new_id){
                                var r=document.createElement("TR");
                        r.idd = new_id;
                        r.grid = this;

                        for(var i=0;i<this.hdr.rows[0].cells.length;i++){
                            var c = document.createElement("TD");
                                    //#cell_id:11052006{
                                    if (this._enbCid) c.id="c_"+r.idd+"_"+i;
                                    //#}
                                    c._cellIndex = i;
                                    if (this.dragAndDropOff) this.dragger.addDraggableItem(c,this);
                           if (this.cellAlign[i]) c.align = this.cellAlign[i];
                           c.style.verticalAlign = this.cellVAlign[i];
                           //add color to column
                           c.bgColor = this.columnColor[i] || "";

//#__pro_feature:21092006{
//#column_hidden:21092006{
                                    if ((this._hrrar)&&(this._hrrar[i]))
                                c.style.display="none";
//#}
//#}


                                    r.appendChild(c);
                                }
                                return r;
    }
      /**
      *   @desc: second step of add row,  fill tr with data
      *   @type: private
      */
    dhtmlXGridObject.prototype._fillRow=function(r,text){ 
        if (!this._parsing_) this.editStop();

        this.math_off=true;
        this.math_req=false;

        if(typeof(text)!='object')
           text = (text||"").split(this.delim);
        for(var i=0; i<r.childNodes.length; i++){
         if((i<text.length)||(this.defVal[i])){
	         var val = text[i]

         if (( this._dload && this.rowsAr[r.idd] ) || ( this._refresh_mode && (!this._fake || this._fake.rowsAr[r.idd] )))
                  var aeditor = this.cells3(r,r.childNodes[i]._cellIndex);
         else
                  var aeditor = this.cells4(r.childNodes[i]);

				if (aeditor.cell._cellIndex) val=text[aeditor.cell._cellIndex]
				if ((this.defVal[i])&&((val=="")||(typeof(val)=="undefined")))
                  val = this.defVal[i];

              aeditor.setValue(val)
              aeditor = aeditor.destructor();
           }else{
              var val = "&nbsp;";
              r.childNodes[i].innerHTML = val;
                       r.childNodes[i]._clearCell=true;
           }
        }
        this.math_off=false;
        if ((this.math_req)&&(!this._parsing_)){
               for(var i=0;i<this.hdr.rows[0].cells.length;i++)
                    this._checkSCL(r.childNodes[i]);
            this.math_req=false;
        }
        return r;
    }

      /**
      *   @desc: third step of add row,  attach TR to DOM
      *   @type: private
      */
    dhtmlXGridObject.prototype._insertRowAt=function(r,ind,skip){ 
             if ((ind<0)||((!ind)&&(parseInt(ind)!==0)))
                ind = this.rowsCol.length;
             else{
                if(ind>this.rowsCol.length)
                   ind = this.rowsCol.length;
             }    	
             
    		
    			
				if (this._cssEven){
                    if ((this._cssSP?this.getLevel(r.idd):ind)%2==1) r.className+=" "+this._cssUnEven+(this._cssSU?(this._cssUnEven+"_"+this.getLevel(r.idd)):"");
                    else r.className+=" "+this._cssEven+(this._cssSU?(" "+this._cssEven+"_"+this.getLevel(r.idd)):"");
                }                
			if (r._skipInsert) {                
				this.rowsAr[r.idd] = r;
    			return r;
    		}
                            if (!skip)
                            if ((ind==(this.obj.rows.length-1))||(!this.rowsCol[ind]))
                                if (_isKHTML)
                                    this.obj.appendChild(r);
                                else{
                                    this.obj.firstChild.appendChild(r);
                                    }
                            else
                                {
                                this.rowsCol[ind].parentNode.insertBefore(r,this.rowsCol[ind]);
                                }


                     this.rowsAr[r.idd] = r;
                     this.rowsCol._dhx_insertAt(ind,r);
                            if (this._cssEven){

                                if (!this._cssSP && (ind!=(this.rowsCol.length-1)))
                                    this._fixAlterCss(ind+1);
                            }

                     //this.chngCellWidth(ind)
                        this.doOnRowAdded(r);

                            //bad code, need to be rethinked
                            if ((this.math_req)&&(!this._parsing_)){
                                for(var i=0;i<this.hdr.rows[0].cells.length;i++)
                                   this._checkSCL(r.childNodes[i]);
                                this.math_req=false;
                            }

                            return r;
    }

   /**
   *   @desc: adds row to the specified position
   *   @param: new_id - id for new row
   *   @param: text - Array of values or String(with delimiter as in delimiter parameter)
   *   @param: [ind] - index of row (0 by default)
   *   @returns: new row dom object
   *   @type: private
   *   @topic: 2
   */
   dhtmlXGridObject.prototype._addRow   =    function(new_id,text,ind){ 
                       var row = this._fillRow(this._prepareRow(new_id),text);
                  if (!this._dload)                       
                  if ((ind>this.rowsCol.length && ind<(this.rowsCol.length+this.rowsBuffer[0].length)) || (typeof ind =="undefined" && this.rowsBuffer[0].length)){
                  	 if (typeof ind =="undefined") var inBufInd=this.rowsBuffer[0].length;
                  	 else var inBufInd = ind - this.rowsCol.length;
                     this.rowsBuffer[0]._dhx_insertAt(inBufInd,new_id);
                     this.rowsBuffer[1]._dhx_insertAt(inBufInd,row);
                     return row;
                  }
                  return this._insertRowAt(row,ind);
               }

/**
*   @desc: hide/show row (warning! - this command doesn't affect row indexes, only visual appearance)
*   @param: ind - column index
*   @param: state - true/false - hide/show row
*   @type:  public
*/
dhtmlXGridObject.prototype.setRowHidden=function(id,state){
    var f=convertStringToBoolean(state);
    //var ind=this.getRowIndex(id);
    //if (id<0)
   //   return;
    var row= this.getRowById(id)//this.rowsCol[ind];
   if(!row)
      return;

    if (row.expand==="")
        this.collapseKids(row);

    if ((state)&&(row.style.display!="none")){
        row.style.display="none";
        var z=this.selectedRows._dhx_find(row);
        if (z!=-1){
          row.className=row.className.replace("rowselected","");
            for (var i=0; i<row.childNodes.length; i++)
                row.childNodes[i].className=row.childNodes[i].className.replace(/cellselected/g,"");
             this.selectedRows._dhx_removeAt(z);
            }
                       this.callEvent("onGridReconstructed",[]);
        }

    if ((!state)&&(row.style.display=="none")){
        row.style.display="";
                       this.callEvent("onGridReconstructed",[]);
        }
   this.setSizes();
}

//#__pro_feature:21092006{
//#column_hidden:21092006{
/**
*   @desc: hide/show column
*   @param: ind - column index
*   @param: state - true/false - hide/show column
*   @type:  public
*   @edition: Professional
*/
   dhtmlXGridObject.prototype.setColumnHidden=function(ind,state){
   if (!this.hdr.rows.length){
   		if (!this._ivizcol) this._ivizcol=[];
   		return this._ivizcol[ind]=state;
   }
   
   
    if ((this.fldSorted)&&(this.fldSorted.cellIndex==ind)&&(state))
            this.sortImg.style.display = "none";

    var f=convertStringToBoolean(state);
    if (f){
        if (!this._hrrar) this._hrrar=new Array();
        else if (this._hrrar[ind]) return;
            this._hrrar[ind]="display:none;";
            this._hideShowColumn(ind,"none");
    }
    else
    {
        if ((!this._hrrar)||(!this._hrrar[ind])) return;
        this._hrrar[ind]="";
        this._hideShowColumn(ind,"");
    }

    if ((this.fldSorted)&&(this.fldSorted.cellIndex==ind)&&(!state))
            this.sortImg.style.display = "inline";
}



/**
*   @desc: get show/hidden status of column
*   @param: ind - column index
*   @type:  public
*   @edition: Professional
*   @returns:  if column hidden then true else false
*/
dhtmlXGridObject.prototype.isColumnHidden=function(ind){
     if ((this._hrrar)&&(this._hrrar[ind])) return true;
     return false;
}


/**
*   @desc: set list of visible/hidden columns
*   @param: list - list of true/false separated by comma
*   @type:  public
*   @edition: Professional
*   @topic:0
*/
dhtmlXGridObject.prototype.setColumnsVisibility=function(list){
	this.setColHidden(list)
}
/**
*   @desc: set list of visible/hidden columns
*   @param: list - list of true/false separated by comma
*   @type:  deprecated
*	@newmethod: setColumnsVisibility
*   @edition: Professional
*   @topic:0
*/
dhtmlXGridObject.prototype.setColHidden=function(list){
    if (list) this._ivizcol=list.split(",");
    
   if (this.hdr.rows.length && this._ivizcol)
   for (var i=0; i<this._ivizcol.length; i++)
       this.setColumnHidden(i,this._ivizcol[i]);

}

      /**
      *   @desc: fix hidden state for column in all rows
      *   @type: private
      */
dhtmlXGridObject.prototype._fixHiddenRowsAll=function(pb,ind,prop,state){
 var z=pb.rows.length;
 for(var i=0;i<z;i++){
	var x=pb.rows[i].cells;
	if (x.length!=this._cCount){
		for (var j=0; j<x.length; j++)
			if (x[j]._cellIndex==ind){
            	x[j].style[prop]=state;
            	break;
			}
	}
    else 
    	x[ind].style[prop]=state;
 }
}
/**
*   @desc: hide column
*   @param: ind - column index
*   @param: state - hide/show
*   @edition: Professional
*   @type:  private
*/
dhtmlXGridObject.prototype._hideShowColumn=function(ind,state){
   var hind=ind;
   if ((this.hdr.rows[1]._childIndexes)&&(this.hdr.rows[1]._childIndexes[ind]!=ind))
      hind=this.hdr.rows[1]._childIndexes[ind];

    if (state=="none"){
        this.hdr.rows[0].cells[ind]._oldWidth = this.hdr.rows[0].cells[ind].style.width;
        this.hdr.rows[0].cells[ind]._oldWidthP = this.cellWidthPC[ind];
        this.obj.rows[0].cells[ind].style.width = "0px";
		
        this._fixHiddenRowsAll(this.obj,ind,"display","none");
      if (this._fixHiddenRowsAllTG) this._fixHiddenRowsAllTG(ind,"none");
      
        if ((_isOpera&&_OperaRv<9)||_isKHTML||(_isFF)){
         this._fixHiddenRowsAll(this.hdr,ind,"display","none");
        if (this.ftr)
            this._fixHiddenRowsAll(this.ftr.childNodes[0],ind,"display","none");
	    }

			this._fixHiddenRowsAll(this.hdr,ind,"whiteSpace","nowrap");

			if (!this.cellWidthPX.length && !this.cellWidthPC.length)
				this.cellWidthPX=[].concat(this.initCellWidth);
			
            if (this.cellWidthPX[ind]) this.cellWidthPX[ind]=0;
            if (this.cellWidthPC[ind]) this.cellWidthPC[ind]=0;
        }
    else {
        if (this.hdr.rows[0].cells[ind]._oldWidth){
        var zrow=this.hdr.rows[0].cells[ind];
          if (_isOpera||_isKHTML||(_isFF))
             this._fixHiddenRowsAll(this.hdr,ind,"display","");

         if (this.ftr)
            this._fixHiddenRowsAll(this.ftr.childNodes[0],ind,"display","");

             this.obj.rows[0].cells[ind].style.width = this.hdr.rows[0].cells[ind]._oldWidth;
             this._fixHiddenRowsAll(this.obj,ind,"display","");
         if (this._fixHiddenRowsAllTG) this._fixHiddenRowsAllTG(ind,"");          

             zrow.style.width = zrow._oldWidth;

			this._fixHiddenRowsAll(this.hdr,ind,"whiteSpace","normal");
             
             if (zrow._oldWidthP) this.cellWidthPC[ind]=zrow._oldWidthP;
             if (zrow._oldWidth) this.cellWidthPX[ind]=parseInt(zrow._oldWidth);
        }
    }
    this.setSizes();
	
    if ((!_isIE)&&(!_isFF))
    {
    //dummy Opera/Safari fix
    this.obj.border=1;
    this.obj.border=0;
    }

}

//#}

//#colspan:20092006{
/**
*   @desc: enable/disable colspan support
*   @param: mode - true/false
*   @type:  public
*   @edition: Professional
*/
 dhtmlXGridObject.prototype.enableColSpan=function(mode){
    this._ecspn=convertStringToBoolean(mode);
 }
 /**
*   @desc: enable/disable colspan support
*   @param: mode - true/false
*   @type:  deprecated
*	@newmethod: enableColSpan
*   @edition: Professional
*/
 dhtmlXGridObject.prototype.enableCollSpan=function(mode){
    this._ecspn=convertStringToBoolean(mode);
 }
 //#}

 //#}
/**
*   @desc: enable/disable hovering row on mouse over
*   @param: mode - true/false
*   @param: cssClass - css class for hovering row
*   @type:  public
*/
dhtmlXGridObject.prototype.enableRowsHover = function(mode,cssClass){
    this._hvrCss=cssClass;
    if (convertStringToBoolean(mode)){
        if (!this._elmnh){
             this.obj._honmousemove=this.obj.onmousemove;
             this.obj.onmousemove=this._setRowHover;
             if (_isIE)
                this.obj.onmouseleave=this._unsetRowHover;
             else
                 this.obj.onmouseout=this._unsetRowHover ;

             this._elmnh=true;
        }
    } else {
        if (this._elmnh){
             this.obj.onmousemove=this.obj._honmousemove;
             if (_isIE)
                this.obj.onmouseleave=null;
             else
                 this.obj.onmouseout=null;

             this._elmnh=false;
        }
    }
};

/**
*   @desc: enable/disable events which fire excell editing, mutual exclusive with enableLightMouseNavigation
*   @param: click - true/false - enable/disable editing by single click
*   @param: dblclick - true/false - enable/disable editing by double click
*   @param: f2Key - enable/disable editing by pressing F2 key
*   @type:  public
*/
dhtmlXGridObject.prototype.enableEditEvents = function(click, dblclick, f2Key){
         this._sclE = convertStringToBoolean(click);
         this._dclE = convertStringToBoolean(dblclick);
         this._f2kE = convertStringToBoolean(f2Key);
}


/**
*   @desc: enable/disable light mouse navigation mode (row selection with mouse over, editing with single click), mutual exclusive with enableEditEvents
*   @param: mode - true/false
*   @type:  public
*/
dhtmlXGridObject.prototype.enableLightMouseNavigation = function(mode){
    if (convertStringToBoolean(mode)){
        if (!this._elmn){
             this.entBox._onclick=this.entBox.onclick;
             this.entBox.onclick = function () {return true; };
			 
			 this.obj._onclick=this.obj.onclick;
             this.obj.onclick=function (e){
                 var c = this.grid.getFirstParentOfType(e?e.target:event.srcElement,'TD');
                 this.grid.editStop();
                 this.grid.doClick(c);
                this.grid.editCell();
             (e||event).cancelBubble=true;
                 }

             this.obj._onmousemove=this.obj.onmousemove;
             this.obj.onmousemove=this._autoMoveSelect;
             this._elmn=true;
        }
    } else {
        if (this._elmn){
             this.entBox.onclick = this.entBox._onclick;
             this.obj.onclick=this.obj._onclick;
             this.obj.onmousemove=this.obj._onmousemove;
             this._elmn=false;
        }
    }
}


/**
*   @desc: remove hover state on row
*   @type:  private
*/
dhtmlXGridObject.prototype._unsetRowHover = function(e,c){
        if (c) that=this; else that=this.grid;

        if ((that._lahRw)&&(that._lahRw!=c)){
         for(var i=0;i<that._lahRw.childNodes.length;i++)
                that._lahRw.childNodes[i].className=that._lahRw.childNodes[i].className.replace(that._hvrCss,"");
            that._lahRw=null;
        }
}

/**
*   @desc: set hover state on row
*   @type:  private
*/
dhtmlXGridObject.prototype._setRowHover = function(e){
        var c = this.grid.getFirstParentOfType(e?e.target:event.srcElement,'TD');
        if (c) {
            this.grid._unsetRowHover(0,c);
            c=c.parentNode;
         for(var i=0;i<c.childNodes.length;i++)
                c.childNodes[i].className+=" "+this.grid._hvrCss;
            this.grid._lahRw=c;
        }
        this._honmousemove(e);
}

/**
*   @desc: onmousemove, used in light mouse navigaion mode
*   @type:  private
*/
dhtmlXGridObject.prototype._autoMoveSelect = function(e){
    //this - grid.obj
    if(!this.grid.editor)
    {
        var c = this.grid.getFirstParentOfType(e?e.target:event.srcElement,'TD');
      if (c.parentNode.idd)
           this.grid.doClick(c,true,0);
    }
    this._onmousemove(e);
}

//#__pro_feature:21092006{
//#distrb_parsing:21092006{
/**
*   @desc: enable/disable distributed parsing (rows paresed portion by portion with some timeout)
*   @param: mode - true/false
*   @param: count - count of nodes parsed by one step (the 10 by default)
*   @param: time - time between parsing counts in milli seconds (the 250 by default)
*   @type:  public
*   @edition: Professional
*/
dhtmlXGridObject.prototype.enableDistributedParsing = function(mode,count,time){
    count=count||10;
    time=time||250;
    if (convertStringToBoolean(mode)){
        this._ads_count=count;
        this._ads_time=time;
    }
    else  this._ads_count=0;
}


/**
*   @desc:  call function from saved context, used in distributed parsing
*   @type:  private
*/
function _contextCall(obj,name,rowsCol,startIndex,tree,pId,i,n){
    obj._contextCallTimer=window.setTimeout(function(){
        var res=obj[name](rowsCol,startIndex,tree,pId,i);
        if (obj._ahgr) obj.setSizes();
        if (res!=-1)
            obj.callEvent("onXLE",[obj,obj.rowsCol.length]);
    },n);
    return this;
}
//#}
//#}
      /**
          *     @desc: destructor, removes grid and cleans used memory
          *     @type: public
        *     @topic: 0
          */
dhtmlXGridObject.prototype.destructor=function(){
    if (this._sizeTime)
        this._sizeTime=window.clearTimeout(this._sizeTime);
		
    var a;
    this.xmlLoader=this.xmlLoader.destructor();
    for (var i=0; i<this.rowsCol.length; i++)
        if (this.rowsCol[i]) this.rowsCol[i].grid=null;
    for (i in this.rowsAr)
        if (this.rowsAr[i])     	
        	this.rowsAr[i]=null;

    this.rowsCol=new dhtmlxArray();
    this.rowsAr=new Array();
    this.entBox.innerHTML="";
   this.entBox.onclick = function(){};
   this.entBox.onmousedown = function(){};
   this.entBox.onbeforeactivate = function(){};
   this.entBox.onbeforedeactivate = function(){};
   this.entBox.onbeforedeactivate = function(){};

   this.entBox.onselectstart = function(){};
   this.entBox.grid = null;


    for (a in this){
        if ((this[a])&&(this[a].m_obj))
            this[a].m_obj=null;
        this[a]=null;
        }


    if (this==globalActiveDHTMLGridObject)
        globalActiveDHTMLGridObject=null;
//   self=null;
    return null;
}





/**
*     @desc: get sorting state of grid
*     @type: public
*     @returns: array, first element is index of sortef column, second - direction of sorting ("asc" or "des").
*     @topic: 0
*/
   dhtmlXGridObject.prototype.getSortingState=function(){
                var z=new Array();
                if (this.fldSorted){
                    z[0]=this.fldSorted._cellIndex;
                    z[1]=(this.sortImg.src.indexOf("sort_desc.gif")!=-1)?"des":"asc";
                }
                return z;
    };

/**
*     @desc: enable autoheight of grid
*     @param: mode - true/false
*     @param: maxHeight - maximum height before scrolling appears (no limit by default)
*     @param: countFullHeight - control the usage of maxHeight parameter - when set to true all grid height included in max height calculation, if false then only data part (no header) of grid included in calcualation (false by default)
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableAutoHeight=function(mode,maxHeight,countFullHeight){
        this._ahgr=convertStringToBoolean(mode);
        this._ahgrF=convertStringToBoolean(countFullHeight);
        this._ahgrM=maxHeight||null;
      if (maxHeight=="auto")
         {
         this._ahgrM=null;
         this._ahgrMA=true;
         this._setAutoResize();
      //   this._activeResize();
         }
    };
   dhtmlXGridObject.prototype.enableAutoHeigth=dhtmlXGridObject.prototype.enableAutoHeight;

/**
*     @desc: enable stable sorting (slow). Stable sorting algorithms maintain the relative order of records with equal keys
*     @param: mode - true/false
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableStableSorting=function(mode){
		this._sst=convertStringToBoolean(mode);
		this.rowsCol.stablesort=function(cmp){
      	 var size=this.length-1;
         for (var i=0; i<this.length-1; i++){
            for (var j=0; j<size; j++)
               if (cmp(this[j],this[j+1])>0){
                  var temp=this[j];
                  this[j]=this[j+1];
                  this[j+1]=temp;
               }
               size--;
           }
        }
    };

/**
*     @desc: enable/disable hot keys in grid
*     @param: mode - true/false
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableKeyboardSupport=function(mode){
        this._htkebl=!convertStringToBoolean(mode);
    };


/**
*     @desc: enable/disable context menu
*     @param: dhtmlxMenu object, if null - context menu will be disabled
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableContextMenu=function(menu){
        this._ctmndx=menu;
    };
/**
*     @desc: set event handler, which will be called immideatly before showing context menu
*     @param: func - user defined function
*     @type: deprecated
*     @event: onBeforeContextMenu
*     @eventdesc: Event raised immideatly before showing context menu
*     @eventparam:  ID of clicked row
*     @eventparam:  index of cell column
*     @eventparam:  grid object
*     @eventreturns: if event returns false, then context menu is not shown
*     @topic: 0,10
*/
   dhtmlXGridObject.prototype.setOnBeforeContextMenu=function(func){
            this.attachEvent("onBeforeContextMenu",func);
    };

/**
*     @desc: set event handler, which will be called immideatly after right mouse button click on grid row
*     @param: func - user defined function
*     @type: deprecated
*     @event: onRightClick
*     @eventdesc: Event raised immideatly after right mouse button clicked on grid row
*     @eventparam:  ID of clicked row
*     @eventparam:  index of cell column
*     @eventparam:  event object
*     @eventreturns: if event returns false, then dhtmlxMenu integration disabled
*     @topic: 0,10
*/
dhtmlXGridObject.prototype.setOnRightClick=function(func){
   this.attachEvent("onRightClick",func);
};



/**
*     @desc: set width of browser scrollbars, will be used to correct autoWidth calculations (by default grid uses 16 for IE and 19 pixels for FF)
*     @param: width - scrollbar width
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.setScrollbarWidthCorrection=function(width){
        this._scrFix=parseInt(width);
    };

/**
*     @desc: enable/disable tooltips for specified colums
*     @param: list - list of true/false values, tooltips enabled for all columns by default
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableTooltips=function(list){
        this._enbTts=list.split(",");
        for (var i=0; i<this._enbTts.length; i++)
            this._enbTts[i]=convertStringToBoolean(this._enbTts[i]);
    };


/**
*     @desc: enable/disable resizing for specified colums
*     @param: list - list of true/false values, resizing enabled for all columns by default
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableResizing=function(list){
        this._drsclmn=list.split(",");
        for (var i=0; i<this._drsclmn.length; i++)
            this._drsclmn[i]=convertStringToBoolean(this._drsclmn[i]);
    };

/**
*     @desc: set minimum column width ( works only for manual resizing )
*     @param: width - minimum column width, can be set for specified column, or as comma separated list for all columns
*     @param: ind - column index
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.setColumnMinWidth=function(width,ind){
        if (arguments.length==2){
            if (!this._drsclmW) this._drsclmW=new Array();
            this._drsclmW[ind]=width;
            }
        else
            this._drsclmW=width.split(",");
    };


//#cell_id:11052006{
/**
*     @desc: enable/disable unique id for cells (id will be automaticaly created using the following template: "c_[RowId]_[colIndex]")
*     @param: mode - true/false - enable/disable
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.enableCellIds=function(mode){
        this._enbCid=convertStringToBoolean(mode);
    };
//#}



//#locked_row:11052006{
/**
*     @desc: lock/unlock row for editing
*     @param: rowId - id of row
*     @param: mode - true/false - lock/unlock
*     @type: public
*     @topic: 0
*/
   dhtmlXGridObject.prototype.lockRow=function(rowId,mode){
        var z=this.getRowById(rowId);
        if (z) {
            z._locked=convertStringToBoolean(mode);
            if ((this.cell)&&(this.cell.parentNode.idd==rowId))
                this.editStop();
            }
    };
//#}

/**
*   @desc:  get values of all cells in row
*   @type:  private
*/
   dhtmlXGridObject.prototype._getRowArray=function(row){
        var text=new Array();
        for (var ii=0; ii<row.childNodes.length; ii++){
			var a=this.cells3(row,ii);
			if (a.cell._code) text[ii]=a.cell._val;
			else text[ii]=a.getValue();
		}
        return text;
        }

//#__pro_feature:21092006{
//#data_format:12052006{
/**
*     @desc: set mask for date formatting in cell
*     @param: mask - date mask, d,m,y will mean day,month,year; for example "d/m/y" - 22/05/1985
*     @type: public
*     @edition: Professional
*     @topic: 0
*/
   dhtmlXGridObject.prototype.setDateFormat=function(mask){
        this._dtmask=mask;
    }

/**
*     @desc: set mask for formatting numeric data ( works for [ed/ro]n excell only or oher cell types with suport for this method)
*     @param: mask - numeric mask; for example 0,000.00 - 1,234.56
*     @param: cInd - column index
*     @param: p_sep - char used as groups separator ( comma by default )
*     @param: d_sep - char used as decimal part separator ( point by default )
*     @type: public
*     @edition: Professional
*     @topic: 0
*/
   dhtmlXGridObject.prototype.setNumberFormat=function(mask,cInd,p_sep,d_sep){
            var nmask=mask.replace(/[^0\,\.]*/g,"");
            var pfix=nmask.indexOf(".");
            if (pfix>-1) pfix=nmask.length-pfix-1;
            var dfix=nmask.indexOf(",");
            if (dfix>-1) dfix=nmask.length-pfix-2-dfix;

            p_sep=p_sep||".";
            d_sep=d_sep||",";
            var pref=mask.split(nmask)[0];
            var postf=mask.split(nmask)[1];
            this._maskArr[cInd]=[pfix,dfix,pref,postf,p_sep,d_sep];
    }
/**
*   @desc:  convert formated value to original
*   @type:  private
*/
    dhtmlXGridObject.prototype._aplNFb=function(data,ind){
            var a=this._maskArr[ind];
            if (!a) return data;

            var ndata=parseFloat(data.toString().replace(/[^0-9]*/g,""));
            if (data.toString().substr(0,1)=="-") ndata=ndata*-1;
            if (a[0]>0) ndata=ndata/Math.pow(10,a[0]);
            return ndata;
    }

/**
*   @desc:  format data with mask
*   @type:  private
*/
   dhtmlXGridObject.prototype._aplNF=function(data,ind){
            var a=this._maskArr[ind];
            if (!a) return data;

            var c=(parseFloat(data)<0?"-":"")+a[2];
            data = Math.abs(Math.round(parseFloat(data)*Math.pow(10,a[0]>0?a[0]:0))).toString();
            data=(data.length<a[0]?Math.pow(10,a[0]+1-data.length).toString().substr(1,a[0]+1)+data.toString():data).split("").reverse();
            data[a[0]]=(data[a[0]]||"0")+a[4];
            if (a[1]>0)  for (var j=(a[0]>0?0:1)+a[0]+a[1]; j<data.length; j+=a[1]) data[j]+=a[5];
            return c+data.reverse().join("")+a[3];
    }
//#} 

//#}

//#config_from_xml:20092006{

/**
*   @desc:  configure grid structure from XML
*   @type:  private
*/
      dhtmlXGridObject.prototype._launchCommands = function(arr){
         for (var i=0; i<arr.length; i++){
            var args=new Array();
            for (var j=0; j<arr[i].childNodes.length; j++)
               if (arr[i].childNodes[j].nodeType==1)
                  args[args.length]=arr[i].childNodes[j].firstChild.data;
            this[arr[i].getAttribute("command")].apply(this,args);
         }
     }


/**
*   @desc:  configure grid structure from XML
*   @type:  private
*/
      dhtmlXGridObject.prototype._parseHead = function(xmlDoc){
                    var hheadCol = this.xmlLoader.doXPath("//rows/head",xmlDoc);
                    if (hheadCol.length){
                        
                          var headCol = this.xmlLoader.doXPath("//rows/head/column",hheadCol[0]);
                                var asettings = this.xmlLoader.doXPath("//rows/head/settings",hheadCol[0]);
                                var awidthmet="setInitWidths";
                                var split=false;

                                if (asettings[0]){
                                    for (var s=0; s<asettings[0].childNodes.length; s++)
                                        switch (asettings[0].childNodes[s].tagName){
                                            case "colwidth":
                                                if (asettings[0].childNodes[s].firstChild && asettings[0].childNodes[s].firstChild.data=="%")
                                                    awidthmet="setInitWidthsP";
                                                break;
                                            case "splitat":
                                                split=(asettings[0].childNodes[s].firstChild?asettings[0].childNodes[s].firstChild.data:false);
                                                break;
                                        }
                                }
                    this._launchCommands(this.xmlLoader.doXPath("//rows/head/beforeInit/call",hheadCol[0]));
                   
                          if(headCol.length>0){
                            var sets=[[],[],[],[],[],[],[],[],[]];
                            var attrs=["","width","type","align","sort","color","format","hidden","id"];
                            var calls=["setHeader",awidthmet,"setColTypes","setColAlign","setColSorting","setColumnColor","","","setColumnIds"];
                            for (var i=0; i < headCol.length; i++) {
                            	for (var j=1; j < attrs.length; j++) 
                            		sets[j].push(headCol[i].getAttribute(attrs[j]));
                            	sets[0].push((headCol[i].firstChild?headCol[i].firstChild.data:"").replace(/^\s*((.|\n)*.+)\s*$/gi,"$1"));
                            };

                            for (var i=0; i < calls.length; i++) 
                            	if (calls[i]) this[calls[i]](sets[i].join(this.delim))
                            
                            for (var i=0; i<headCol.length; i++){
                            	if ((this.cellType[i].indexOf('co')==0)||(this.cellType[i]=="clist")){
                                	var optCol = this.xmlLoader.doXPath("./option",headCol[i]);
                                    if (optCol.length){
                                    	var resAr=new Array();
                                        if (this.cellType[i]=="clist"){
                                        	for (var j=0;j<optCol.length; j++)
                                            	resAr[resAr.length]=optCol[j].firstChild?optCol[j].firstChild.data:"";
                                            this.registerCList(i,resAr);
                                        }
                                        else{
                                        	var combo=this.getCombo(i);
                                            for (var j=0;j<optCol.length; j++)
                                            	combo.put(optCol[j].getAttribute("value"),optCol[j].firstChild?optCol[j].firstChild.data:"");
                                        }
                                    }
                                }
								else
                                	if (sets[6][i])
                                    	if ((this.cellType[i]=="calendar")||(this.fldSort[i]=="date"))
                                     		this.setDateFormat(sets[6][i],i);
                                  		else
                                     		this.setNumberFormat(sets[6][i],i);
                            }

							this.init();
							if (this.setColHidden)
                            	this.setColHidden(sets[7].join(this.delim));
                            if ((split)&&(this.splitAt)) this.splitAt(split);
            	}
            	this._launchCommands(this.xmlLoader.doXPath("//rows/head/afterInit/call",hheadCol[0]));
			}
                        //global(grid) user data
                        var gudCol = this.xmlLoader.doXPath("//rows/userdata",xmlDoc);
                        if(gudCol.length>0){
						   if (!this.UserData["gridglobaluserdata"])
                           		this.UserData["gridglobaluserdata"] = new Hashtable();
                           for(var j=0;j<gudCol.length;j++){
                              this.UserData["gridglobaluserdata"].put(gudCol[j].getAttribute("name"),gudCol[j].firstChild?gudCol[j].firstChild.data:"");
                           }
                        }            
        }
    
    
//#}


   /**
      *    @desc: populate grid with data from xml file (if passed parameter contains '.') or data island
      *    @param: [xml] - xml island will be found by grid id ([id]_xml) if no file or island id is specified. This also can be ready-to-use XML object
      *   @param: [startIndex] - index of row in grid to start insertion from
      *   @type: public
      *   @topic: 2,5
      */
      dhtmlXGridObject.prototype.parseXML = function(xml,startIndex){
                   this._xml_ready=true;
                   var pid=null;
                   var zpid=null;
                        if(!xml)
                           try{
                              var xmlDoc = eval(this.entBox.id+"_xml").XMLDocument;
                           }catch(er){
                              var xmlDoc = this.loadXML(this.xmlFileUrl)
                           }
                        else{
                           if(typeof(xml)=="object"){
                              var xmlDoc = xml;
                           }else{
                              if(xml.indexOf(".")!=-1){
                                 if(this.xmlFileUrl=="")
                                    this.xmlFileUrl = xml
                                 var xmlDoc = this.loadXML(xml)
                                            return;
                              }else
                                 var xmlDoc = (_isIE?eval(xml).XMLDocument:document.getElementById(xml));
                           }
                        }





                        var ar = new Array();
                        var idAr = new Array();

						var a_top=this.xmlLoader.doXPath("//rows",xmlDoc);
						
						if (a_top[0] && a_top[0].getAttribute("total_count"))
							this.limit=a_top[0].getAttribute("total_count");
							
//#config_from_xml:20092006{
						this._parseHead(xmlDoc);
//#}


                        

                        var tree=this.cellType._dhx_find("tree");
                        var rowsCol = this.xmlLoader.doXPath("//rows/row",xmlDoc);
                        if(rowsCol.length==0){
                           this.recordsNoMore = true;
                     var top=this.xmlLoader.doXPath("//rows",xmlDoc);
                     		if (!top) return;
                            var pid=(top[0].getAttribute("parent")||0);
                          if ((tree!=-1)&&(this.rowsAr[pid])){
                        var tree_r=this.rowsAr[pid].childNodes[tree];
                     }
                        }
                                else{
                                    pid=(rowsCol[0].parentNode.getAttribute("parent")||null);
                                    zpid=this.getRowById(pid);
                                    if (zpid) zpid._xml_await=false;
                                    else pid=null;
                                    startIndex=this.getRowIndex(pid)+1;
                                }



                        //rows
                                if (tree==-1) tree=this.cellType._dhx_find("3d");
                                if (this._innerParse(rowsCol,startIndex,tree,pid)==-1) return (this._ahgr?this.setSizes():"");
                                if (zpid) this.expandKids(zpid);

                        

                                if (tree!=-1){
                                  var oCol = this.xmlLoader.doXPath("//row[@open]",xmlDoc);
                                for (var i=0; i<oCol.length; i++)
                                    this.openItem(oCol[i].getAttribute("id"));
                                    }

                              this.setSizes();
                            if (_isOpera){
                                this.obj.style.border=1;
                                this.obj.style.border=0;
                                }
                            this._startXMLLoading=false;

                            this.callEvent("onXLE",[this,rowsCol.length,pid,xmlDoc]);
                            
                  }
/**
*   @desc:  add additional attributes to row, based on XML attributes
*   @type:  private
*/
         dhtmlXGridObject.prototype._postRowProcessing=function(r,xml){
               var rId = xml.getAttribute("id");
               var xstyle = xml.getAttribute("style");

                //user data
              var udCol = this.xmlLoader.doXPath("./userdata",xml);
              if(udCol.length>0){
			  	 if (!this.UserData[rId])
	                 this.UserData[rId] = new Hashtable();
                 for(var j=0;j<udCol.length;j++){
                    this.UserData[rId].put(udCol[j].getAttribute("name"),udCol[j].firstChild?udCol[j].firstChild.data:"");
                 }
              }

                //#td_tr_classes:06062006{
                var css1=xml.getAttribute("class");
                if (css1) r.className+=(r._css=" "+css1);
                //#}
                var css1=xml.getAttribute("bgColor");
            if (css1)
               for (var i=0; i<r.childNodes.length; i++)
                  r.childNodes[i].bgColor=css1;
                  
                //#locked_row:11052006{
                if (xml.getAttribute("locked"))
                {
                    r._locked=true;
                }
                //#}


                //select row
                if(xml.getAttribute("selected")==true){
                    this.setSelectedRow(rId,this.selMultiRows,false,xml.getAttribute("call")==true)
                }
                /*
                //expand row
                if(xml.getAttribute("expand")=="1"){
                    r.expand = "";
                }
                */

                if (xstyle) this.setRowTextStyle(rId,xstyle);

			this.callEvent("onRowCreated",[r.idd,r,xml]);
         }
/**
*   @desc:  fill row with data from XML
*   @type:  private
*/
         dhtmlXGridObject.prototype._fillRowFromXML=function(r,xml,tree,pId){
            var cellsCol = this.xmlLoader.doXPath("./cell",xml);
            var strAr = new Array(0);

            for(var j=0;j<cellsCol.length;j++){
            var cellVal=cellsCol[j];
            var exc=cellVal.getAttribute("type");
//#__pro_feature:21092006{
//#xml_content:23102006{
               if (cellVal.getAttribute("xmlcontent"))
                  cellVal=cellsCol[j];
               else
//#}
//#}
               if (cellVal.firstChild)
                  cellVal=cellVal.firstChild.data;
            else cellVal="";
                if (j!=tree)
                    strAr[strAr.length] = cellVal;
                else
                    strAr[strAr.length] = [pId,cellVal,((xml.getAttribute("xmlkids")||r._xml)?"1":"0"),(cellsCol[j].getAttribute("image")||"leaf.gif")];

            if (exc)
               r.childNodes[j]._cellType=exc;

           }
         if (this._c_order) strAr=this._swapColumns(strAr);
            for(var j=0;j<cellsCol.length;j++){
            	var ji=r._childIndexes?r._childIndexes[j]:j;
               	 var css1=cellsCol[j].getAttribute("class");
                 if (css1) r.childNodes[ji].className=css1;
				 css1=cellsCol[j].getAttribute("style");
                 if (css1) r.childNodes[ji].style.cssText+=";"+css1;
                 css1=cellsCol[j].getAttribute("title");
                 if (css1) r.childNodes[ji].title=css1;
            }
            this._fillRow(r,strAr);
//#__pro_feature:21092006{
//#colspan:20092006{
            if (this._ecspn && !this._refresh_mode)
            {
                r._childIndexes=new Array();
                var col_ex=0;
                var l=this.obj.rows[0].childNodes.length
                for(var j=0;j<l;j++){
                    r._childIndexes[j]=j-col_ex;
                    if (!cellsCol[j]) continue;
                    var col=cellsCol[j].getAttribute("colspan");
                    if (col){
                    r.childNodes[j-col_ex].colSpan=col;
                    for (var z=1; z<col; z++){
                        r.removeChild(r.childNodes[j-col_ex+1]);
                        r._childIndexes[j+z]=j-col_ex;
                        }
                    col_ex+=(col-1);
                    j+=(col-1);
                  }
                }
                if (!col_ex)
                   r._childIndexes=null;

            }
//#}
//#}
         if ((r.parentNode)&&(r.parentNode.tagName))
            this._postRowProcessing(r,xml);
		 if (this._customAttrs) this._customAttrs(r.idd,cellsCol)
            return r;
         }


/**
*   @desc:  inner recursive part of XML parsing routine, parses xml for one branch of treegrid or for whole grid
*   @type:  private
*/
         dhtmlXGridObject.prototype._innerParse=function(rowsCol,startIndex,tree,pId,i){
                            i=i||0;    var imax=i+this._ads_count*1;
                            var r=null;
                     var rowsCol2;
                            for(var i;i<rowsCol.length;i++){
//#__pro_feature:21092006{
//#distrb_parsing:21092006{
                                    if (this._ads_count && i==imax) {
                                        new _contextCall(this,"_innerParse",rowsCol,startIndex,tree,pId,i,this._ads_time);
                                        return -1;
                                        }
//#}
//#}
                     if ((pId)||(i<this.rowsBufferOutSize || this.rowsBufferOutSize==0)){

                               this._parsing_=true;
                                var rId = (rowsCol[i].getAttribute("id")||(this.rowsCol.length+2));
                        r=this._prepareRow(rId);

                        if (tree!=-1){
                           rowsCol2 = this.xmlLoader.doXPath("./row",rowsCol[i]);
                           if ((rowsCol2.length!=0)&&(this._slowParse))
                                    r._xml=rowsCol2;
                        }

                                r=this._fillRowFromXML(r,rowsCol[i],tree,pId);

							
                               if(startIndex){
                                 r = this._insertRowAt(r,startIndex);
                                   startIndex++;
                               }else{
                                  r = this._insertRowAt(r);
                               }
                          this._postRowProcessing(r,rowsCol[i]);
                                 this._parsing_=false;
                            }
                     else{
                               var len = this.rowsBuffer[0].length
                               this.rowsBuffer[1][len] = rowsCol[i];
                               this.rowsBuffer[0][len] = rowsCol[i].getAttribute("id")
                               if (!this.rowsBuffer[0][len]){
                               		this.rowsBuffer[0][len]=this.rowsCol.length+2+len;
                               		rowsCol[i].setAttribute("id",this.rowsBuffer[0][len])
                           	   }
                            }

                            if ((tree!=-1)&&(rowsCol2.length!=0)&&(!this._slowParse))
                                startIndex=this._innerParse(rowsCol2,startIndex,tree,rId);

               }

                //nb:paging
                if(this.pagingOn && this.rowsBuffer[0].length>0){
                    this.changePage(this.currentPage)
                }

                if ((r)&&(this._checkSCL))
                    for(var i=0;i<this.hdr.rows[0].cells.length;i++)
                        this._checkSCL(r.childNodes[i]);
                return startIndex;
            }


      /**
      *   @desc: get list of Ids of all rows with checked exCell in specified column
      *   @type: public
      *   @param: col_ind - column index
      *   @topic: 5
      */
 dhtmlXGridObject.prototype.getCheckedRows=function(col_ind){
    var d=new Array();
    this.forEachRow(function(id){
    	if (this.cells(id,col_ind).getValue()!=0)
    		d.push(id);
    })
    return d.join(",");
 }
/**
*   @desc:  grid body onmouseover function
*   @type:  private
*/
 dhtmlXGridObject.prototype._drawTooltip=function(e){
    var c = this.grid.getFirstParentOfType(e?e.target:event.srcElement,'TD');
    if((this.grid.editor)&&(this.grid.editor.cell==c)) return true;

    var r = c.parentNode;
    if (!r.idd) return;
    var el=(e?e.target:event.srcElement);
    if (r.idd==window.unknown) return true;
	if (!this.grid.callEvent("onMouseOver",[r.idd,c._cellIndex])) return true;
    if ((this.grid._enbTts)&&(!this.grid._enbTts[c._cellIndex])) {
    	if (el.title) el.title='';
        return true; }
    var ced = this.grid.cells(r.idd,c._cellIndex);
	if (el._title) ced.cell.title="";
	if (!ced.cell.title) el._title=true;
    if (ced)
 //KC: Changed to stop the alt tags in datagrids
 //el.title=ced.cell.title||(ced.getTitle?ced.getTitle():(ced.getValue()||"").toString().replace(/<[^>]*>/gi,""));

 return true;
    };

/**
*   @desc:  can be used for setting correction for cell padding, while calculation setSizes
*   @type:  private
*/
 dhtmlXGridObject.prototype.enableCellWidthCorrection=function(size){
    if (_isFF) this._wcorr=parseInt(size);
 }


 	/**
	*	@desc: gets a list of all row ids in grid
	*	@param: separator - delimiter to use in list
	*	@returns: list of all row ids in grid
	*	@type: public
	*	@topic: 2,7
	*/
dhtmlXGridObject.prototype.getAllRowIds = function(separator){
							var ar = new Array(0)
                            var z=this.getRowsNum();
							for(i=0;i<z;i++)
                                if ((this.rowsCol[i])&&(!this.rowsCol[i]._sRow)&&(!this.rowsCol[i]._rLoad))
                                   ar[ar.length]=this.rowsCol[i].idd;
                                else if (this.rowsBuffer[1][i])
                                   ar[ar.length]=this.rowsBuffer[0][i];

							return ar.join(separator||",")
						}
dhtmlXGridObject.prototype.getAllItemIds = function(){
	return this.getAllRowIds();
}
   /**
   *   @desc: deletes row specified by ID
   *   @param: row_id - id of row to delete
   *   @param: node - reserved   
   *   @type: public
   *   @topic: 2,9
   */
dhtmlXGridObject.prototype.deleteRow = function(row_id,node){
                                
                        if (!node)node = this.getRowById(row_id)
                        if (!node) return;
                                
                        this.editStop();
                        if (this.callEvent("onBeforeRowDeleted",[row_id])==false)
                           return false;

                    
						if (this.cellType._dhx_find("tree")!=-1)
                        	this._removeTrGrRow(node);
                        else {
                        	if (node.parentNode)
                            	node.parentNode.removeChild(node);
                                   
                           var ind=this.rowsCol._dhx_find(node);
                           if (ind!=-1)
                           	this.rowsCol._dhx_removeAt(ind);
                                else{
                                	ind = this.rowsBuffer[0]._dhx_find(row_id)
                                 	if(ind>=0){
                                    	this.rowsBuffer[0]._dhx_removeAt(ind)
                                    	this.rowsBuffer[1]._dhx_removeAt(ind)
	                                 	}
                				}
                    		this.rowsAr[row_id] = null;
    					}
                    
                    

                            for (var i=0; i<this.selectedRows.length; i++)
                                if (this.selectedRows[i].idd==row_id)
                                    this.selectedRows._dhx_removeAt(i);

                       
                            if(this.pagingOn){
                        this.changePage();
                     }
                     this.setSizes();
                     this.callEvent("onGridReconstructed",[]);
                     if (this._dload) this._askRealRows();
                     return true;
                  }

//#__pro_feature:21092006{
//#colspan:20092006{

/**
*   @desc: dynamicaly set colspan in row starting from specified column index
*   @param: row_id - row id
*   @param: col_id - index of column
*   @param: colspan - size of colspan
*   @type: public
*   @edition: Professional
*   @topic: 2,9
*/
dhtmlXGridObject.prototype.setColspan = function(row_id,col_ind,colspan){
    if (!this._ecspn) return;

    var r=this.getRowById(row_id);
    if ((r._childIndexes)&&(r.childNodes[r._childIndexes[col_ind]])){
        var j=r._childIndexes[col_ind];
        var n=r.childNodes[j];
        var m=n.colSpan;        n.colSpan=1;
        if ((m)&&(m!=1))
            for (var i=1; i<m; i++){
                var c=document.createElement("TD");
                if (n.nextSibling) r.insertBefore(c,n.nextSibling);
                else r.appendChild(c);
                r._childIndexes[col_ind+i]=j+i;
                c._cellIndex=col_ind+i;
                c.align = this.cellAlign[i];
            c.style.verticalAlign = this.cellVAlign[i];
                n=c;
                this.cells3(r,j+i-1).setValue("");
            }

        for (var z=col_ind*1+1*m; z<r._childIndexes.length; z++){
            r._childIndexes[z]+=(m-1)*1;                 }

    }

    if ((colspan)&&(colspan>1)){
        if (r._childIndexes)
            var j=r._childIndexes[col_ind];
        else{
            var j=col_ind;
            r._childIndexes=new Array();
            for (var z=0; z<r.childNodes.length; z++)
                r._childIndexes[z]=z;
            }

        r.childNodes[j].colSpan=colspan;
        for (var z=1; z<colspan; z++){
            r._childIndexes[r.childNodes[j+1]._cellIndex]=j;
            r.removeChild(r.childNodes[j+1]);
        }

        var c1=r.childNodes[r._childIndexes[col_ind]]._cellIndex;
        for (var z=c1*1+1*colspan; z<r._childIndexes.length; z++)
            r._childIndexes[z]-=(colspan-1);

    }
}

//#}
//#}

/**
*   @desc: prevent caching in IE  by adding random values to URL string
*   @param: mode - enable/disable random values in URLs ( disabled by default )
*   @type: public
*   @topic: 2,9
*/
dhtmlXGridObject.prototype.preventIECaching=function(mode){
   this.no_cashe = convertStringToBoolean(mode);
   this.xmlLoader.rSeed=this.no_cashe;
}
dhtmlXGridObject.prototype.preventIECashing=dhtmlXGridObject.prototype.preventIECaching;


/**
*   @desc: enable/disable autosize of column (column size set depending on content size) on doubleclick
*   @param: mode - true/false
*   @type:  public
*/
dhtmlXGridObject.prototype.enableColumnAutoSize = function(mode){
   this._eCAS=convertStringToBoolean(mode);
}
/**
*   @desc: called when header was dbllicked
*   @type: private
*   @topic: 1,2
*/
dhtmlXGridObject.prototype._onHeaderDblClick = function(e){
    var that=this.grid;
     var el = that.getFirstParentOfType(_isIE?event.srcElement:e.target,"TD");

   if (!that._eCAS) return false;
   that.adjustColumnSize(el._cellIndexS)
}

/**
*   @desc: autosize column  to max content size
*   @param: cInd - index of column
*   @type:  public
*/
dhtmlXGridObject.prototype.adjustColumnSize = function(cInd,complex){
   this._notresize=true;
   var m=0;
   this._setColumnSizeR(cInd,20);
   
   for (var j=1; j<this.hdr.rows.length; j++){
         var a=this.hdr.rows[j];
      a=a.childNodes[(a._childIndexes)?a._childIndexes[cInd]:cInd];

      if ((a)&&((!a.colSpan)||(a.colSpan<2))){
         if ((a.childNodes[0])&&(a.childNodes[0].className=="hdrcell"))   a=a.childNodes[0];
         m=Math.max(m,((_isFF||_isOpera)?(a.textContent.length*7):a.scrollWidth));
	  }
   }
	

   var l=this.obj._rowslength();
   for (var i=0; i<l; i++){
	   var z=this.obj._rows(i);
	   if (z._childIndexes && z._childIndexes[cInd]!=cInd) continue;
      if (_isFF||_isOpera||complex)
         z=z.childNodes[cInd].textContent.length*7;
      else
         z=z.childNodes[cInd].scrollWidth;
      if (z>m) m=z;
   }
   m+=20+(complex||0);

   
   this._setColumnSizeR(cInd,m);
   this._notresize=false;
   this.setSizes();
}



/**
*   @desc: remove header line from grid (opposite to attachHeader)
*   @param: index - index of row to be removed ( zero based )
*	@param: hdr - header object (optional)
*   @type:  public
*/
dhtmlXGridObject.prototype.detachHeader = function(index,hdr){
	hdr=hdr||this.hdr;
	var row=hdr.rows[index+1];
	if (row) row.parentNode.removeChild(row);
	this.setSizes();
}

/**
*   @desc: remove footer line from grid (opposite to attachFooter)
*   @param: values - array of header titles
*   @type:  public
*/
dhtmlXGridObject.prototype.detachFooter = function(index){
	this.detachHeader(index,this.ftr);
}

/**
*   @desc: attach additional line to header
*   @param: values - array of header titles
*   @param: style - array of styles, optional
*	@param: _type - reserved
*   @type:  public
*/
dhtmlXGridObject.prototype.attachHeader = function(values,style,_type){
   if (typeof(values)=="string") values=this._eSplit(values);
   if (typeof(style)=="string") style=style.split(this.delim);
   _type=_type||"_aHead";
   if (this.hdr.rows.length){
      if (values)
         this._createHRow([values,style],this[(_type=="_aHead")?"hdr":"ftr"]);
       else if (this[_type])
         for (var i=0; i<this[_type].length; i++)
            this.attachHeader.apply(this,this[_type][i]);
   }
   else{
      if (!this[_type]) this[_type]=new Array();
      this[_type][this[_type].length]=[values,style,_type];
   }
}
/**
*	@desc:
*	@type: private
*/
dhtmlXGridObject.prototype._createHRow = function(data,parent){
   if (!parent){
      //create footer zone
      this.entBox.style.position = "relative";
      var z=document.createElement("DIV");
         z.className="c_ftr".substr(2);
      this.entBox.appendChild(z);
      var t=document.createElement("TABLE");
      t.cellPadding=t.cellSpacing=0;
      if (!_isIE){
            t.width="100%";
         t.style.paddingRight="20px";
        }
        t.style.tableLayout = "fixed";

      z.appendChild(t);
      t.appendChild(document.createElement("TBODY"));
      this.ftr=parent=t;

        var hdrRow =t.insertRow(0);
      var thl=((this.hdrLabels.length<=1)?data[0].length:this.hdrLabels.length);
        for(var i=0;i<thl;i++){
           hdrRow.appendChild(document.createElement("TH"));
           hdrRow.childNodes[i]._cellIndex=i;
        }

        if (_isIE) hdrRow.style.position="absolute";
        else hdrRow.style.height='auto';

   }
   var st1=data[1];
   var z=document.createElement("TR");
    parent.rows[0].parentNode.appendChild(z);
   for (var i=0; i<data[0].length; i++){
      if (data[0][i]=="#cspan"){
         var pz=z.cells[z.cells.length-1];
         pz.colSpan=(pz.colSpan||1)+1;
       continue;
      }
      if ((data[0][i]=="#rspan")&&(parent.rows.length>1)){
         var pind=parent.rows.length-2;
         var found=false;
         var pz=null;
         while(!found){
            var pz=parent.rows[pind];
            for (var j=0; j<pz.cells.length; j++)
               if (pz.cells[j]._cellIndex==i) {
                  found=j+1;
                  break;
            }
            pind--;
         }

         pz=pz.cells[found-1];
         pz.rowSpan=(pz.rowSpan||1)+1;
         continue;
//            data[0][i]="";
      }

      var w=document.createElement("TD");
      w._cellIndex=w._cellIndexS=i;
     if (this.forceDivInHeader)
          w.innerHTML="<div class='hdrcell'>"+data[0][i]+"</div>";
     else
         w.innerHTML=data[0][i];
	if ((data[0][i]||"").indexOf("#")!=-1){
  		var t=data[0][i].match(/(^|{)#([^}]+)(}|$)/);
  		if (t){
  			var tn="_in_header_"+t[2];
  			if (this[tn]) this[tn]((this.forceDivInHeader?w.firstChild:w),i,data[0][i].split(t[0]));
  		}
  	}


	  	
      if (st1) w.style.cssText = st1[i];

      z.appendChild(w);
   }
   var self=parent;
   if (_isKHTML){
         if (parent._kTimer) window.clearTimeout(parent._kTimer);
         parent._kTimer=window.setTimeout(function(){
         parent.rows[1].style.display='none';
         window.setTimeout(function(){ parent.rows[1].style.display=''; },1);
      },500);
   }
}

//#__pro_feature:21092006{
/**
*   @desc: attach additional line to footer
*   @param: values - array of header titles
*   @param: style - array of styles, optional
*   @edition: Professional
*   @type:  public
*/
dhtmlXGridObject.prototype.attachFooter = function(values,style){
   this.attachHeader(values,style,"_aFoot");
}


/**
*   @desc: set excell type for cell in question
*   @param: rowId - row ID
*   @param: cellIndex - cell index
*   @param: type - type of excell (code like "ed", "txt", "ch" etc.)
*   @edition: Professional
*   @type:  public
*/
dhtmlXGridObject.prototype.setCellExcellType = function(rowId,cellIndex,type){
   this.changeCellType(this.rowsAr[rowId],cellIndex,type);
}
/**
*	@desc: 
*	@type: private
*/
dhtmlXGridObject.prototype.changeCellType=function(r,ind,type){
   type=type||this.cellType[ind];
   var z=this.cells3(r,ind);
   var v=z.getValue();
   z.cell._cellType=type;
   var z=this.cells3(r,ind);
   z.setValue(v);
}
/**
*   @desc: set excell type for all cells in specified row
*   @param: rowId - row ID
*   @param: type - type of excell
*   @edition: Professional
*   @type:  public
*/
dhtmlXGridObject.prototype.setRowExcellType = function(rowId,type){
   var z=this.rowsAr[rowId];
   for (var i=0; i<z.childNodes.length; i++)
      this.changeCellType(z,i,type);
}
/**
*   @desc: set excell type for all cells in specified column
*   @param: colIndex - column index
*   @param: type - type of excell
*   @edition: Professional
*   @type:  public
*/
dhtmlXGridObject.prototype.setColumnExcellType = function(colIndex,type){
   for (var i=0; i<this.rowsCol.length; i++)
      this.changeCellType(this.rowsCol[i],colIndex,type);
}

/**
*   @desc: find cell in grid by value
*   @param: value - search string
*   @param: c_ind - index of column to search in (optional. if not specified, then search everywhere)
*   @param: incBufferFl - make search in all rows including buffer (this will parse entire grid and can take time)
*   @edition: Professional
*   @returns: array each member of which contains array with row index and cell index
*   @type:  public
*/
dhtmlXGridObject.prototype.findCell = function(value,c_ind,incBufferFl){
   var res=new Array();
   value=value.toString().toLowerCase();

   if (!this.rowsCol.length) return res;
    for (var i=(c_ind||0); i<this.cellType.length; i++){
       var z=this.cells2(0,i);
      
      if (this.isTreeGrid())
      this.forEachRow(function(id){
         z.cell=this.rowsAr[id].childNodes[i];
         if (!z.cell) return;
         var val=z.getValue();
         if ((val||"").toString().indexOf(value)!=-1) res[res.length]=[id,i];
      });      
      else
  	  for (var j=0; j<this.rowsCol.length; j++){
         z.cell=this.rowsCol[j].childNodes[i];
         if (!z.cell) continue;
         var val=z.getValue();

         if ((val||"").toString().toLowerCase().indexOf(value)!=-1) res[res.length]=[j,i];
		 if(incBufferFl && this.rowsCol.length==j+1) this.addRowsFromBuffer()
      }
      
      if (typeof(c_ind)!="undefined")
         return res;
   }

   return res;
}


//#}
/**
      *   @desc: 
      *   @type: private
      */
dhtmlXGridObject.prototype.dhx_Event=function()
{
   this.dhx_SeverCatcherPath="";

   this.attachEvent = function(original, catcher, CallObj)
   {
      CallObj = CallObj||this;
      original = 'ev_'+original;
       if ( ( !this[original] ) || ( !this[original].addEvent ) ) {
           var z = new this.eventCatcher(CallObj);
           z.addEvent( this[original] );
           this[original] = z;
       }
       return ( original + ':' + this[original].addEvent(catcher) );   //return ID (event name & event ID)
   }
   this.callEvent=function(name,arg0){
         if (this["ev_"+name]) return this["ev_"+name].apply(this,arg0);
       return true;
   }
   this.checkEvent=function(name){
         if (this["ev_"+name]) return true;
       return false;
   }

   this.eventCatcher = function(obj)
   {
       var dhx_catch = new Array();
       var m_obj = obj;
       var func_server = function(catcher,rpc)
                         {
                           catcher = catcher.split(":");
                     var postVar="";
                     var postVar2="";
                           var target=catcher[1];
                     if (catcher[1]=="rpc"){
                           postVar='<?xml version="1.0"?><methodCall><methodName>'+catcher[2]+'</methodName><params>';
                        postVar2="</params></methodCall>";
                        target=rpc;
                     }
                           var z = function() {
                                   }
                           return z;
                         }
       var z = function()
             {
                   if (dhx_catch)
                      var res=true;
                   for (var i=0; i<dhx_catch.length; i++) {
                      if (dhx_catch[i] != null) {
                           var zr = dhx_catch[i].apply( m_obj, arguments );
                           res = res && zr;
                      }
                   }
                   return res;
                }
       z.addEvent = function(ev)
                {
                       if ( typeof(ev) != "function" )
                           if (ev && ev.indexOf && ev.indexOf("server:") == 0)
                               ev = new func_server(ev,m_obj.rpcServer);
                           else
                               ev = eval(ev);
                       if (ev)
                           return dhx_catch.push( ev ) - 1;
                       return false;
                }
       z.removeEvent = function(id)
                   {
                     dhx_catch[id] = null;
                   }
       return z;
   }

   this.detachEvent = function(id)
   {
      if (id != false) {
         var list = id.split(':');            //get EventName and ID
         this[ list[0] ].removeEvent( list[1] );   //remove event
      }
   }
}
/**
*   @desc: execute code for each row in a grid
*   @param: custom_code - function which get row id as incomming argument
*   @type:  public
*/
dhtmlXGridObject.prototype.forEachRow=function(custom_code)
{
   for (a in this.rowsAr)
      if (this.rowsAr[a] && this.rowsAr[a].tagName) custom_code.apply(this,[this.rowsAr[a].idd]);
}
/**
*   @desc: execute code for each cell in a row
*   @param: rowId - id of row where cell must be itterated
*   @param: custom_code - function which get eXcell object as incomming argument
*   @type:  public
*/
dhtmlXGridObject.prototype.forEachCell=function(rowId,custom_code)
{
   var z=this.rowsAr[rowId];
   if (!z) return;
   for (var i=0; i<this._cCount; i++)
        custom_code(this.cells3(z,i));
}
/**
*   @desc: changes grid's container size on the fly to fit total width of grid columns
*   @param: mode  - truse/false - enable / disable
*   @param: max_limit  - max allowed width, not limited by default
*   @param: min_limit  - min allowed width, not limited by default
*   @type:  public
*/
dhtmlXGridObject.prototype.enableAutoWidth = function (mode, max_limit, min_limit){
	this._awdth=[convertStringToBoolean(mode),(max_limit||99999),(min_limit||0)];
 }
 
/**
*   @desc: return unique for current grid id
*   @type:  public
*/
dhtmlXGridObject.prototype.getUID = function (){
	var z=this.getRowsNum()+1;
	while(this.rowsAr[z]) z++;
	return z;
 }
 
 /**
*     @desc: set function called after key pressed in grid
*     @param: func - event handling function
*     @type: depricated
*     @edition: Professional
*     @topic: 10
*     @event:  onKeyPress
*     @eventdesc: event fired after key pressed but before default key processing started
*     @eventparam: key code
*     @eventparam: control key flag
*     @eventparam: shift key flag
*     @eventreturns: false - block defaul key processing
*/
   dhtmlXGridObject.prototype.setOnKeyPressed=function(func){
                this.attachEvent("onKeyPress",func);
    };
 
/**
*   @desc: refresh grid from XML ( doesnt work for buffering, tree grid or rows in smart rendering mode )
*   @param: insert_new - insert new items
*   @param: del_missed - delete missed rows
*   @param: afterCall - function, will be executed after refresh completted
*   @type:  public
*/

dhtmlXGridObject.prototype.updateFromXML=function(url,insert_new,del_missed,afterCall)
{
	if (typeof insert_new == "undefined")
		insert_new=true;
	this._refresh_mode=[true,insert_new,del_missed];
	if (afterCall) this.xmlLoader.waitCall=afterCall;
	this.callEvent("onXLS",[this]);
	this.xmlLoader.loadXML(url);
}
dhtmlXGridObject.prototype.updateFromXMl=dhtmlXGridObject.prototype.updateFromXML;
/**
      *   @desc: 
      *   @type: private
      */
dhtmlXGridObject.prototype._refreshFromXML=function(xml)
{
      if (window.eXcell_tree){
	  	eXcell_tree.prototype.setValueX=eXcell_tree.prototype.setValue;
	  	eXcell_tree.prototype.setValue=function(content){ 
    		if (this.grid._h2.get[this.cell.parentNode.idd]){
	    		this.setLabel(content[1]);
	    		if (content[3]) this.setImage(content[3]);
	    	}
	    	else
    			this.setValueX(content);
			};
	  }

	var tree=this.cellType._dhx_find("tree");
    var pid=xml.doXPath("//rows")[0].getAttribute("parent")||0;
    
	var del={};
	if (this._refresh_mode[2]){
		this.forEachRow(function(id){
			del[id]=true;				
		});}
		
	var rows=xml.doXPath("//row");
	for (var i=0; i<rows.length; i++){
		var row=rows[i];
		var id=row.getAttribute("id");
		del[id]=false;
		
		if (this._dload || this.rowsBuffer[0].length){
			if (this.rowsAr[id])
				this._fillRowFromXML(this.rowsAr[id],row,-1);
			else{
					var z=this.rowsBuffer[0]._dhx_find(id);
					if (z!=-1) 
						this.rowsBuffer[1][z]=row;
				}
		}
		else
		if (this.rowsAr[id])
			this._fillRowFromXML(this.rowsAr[id],row,tree,pid);
		else if(this._refresh_mode[1]){
			var r=this._prepareRow(id);
                r=this._fillRowFromXML(r,row,tree,pid);
                if (tree!=-1 && this._h2.get[pid].state=="minus")
                	r = this._insertRowAt(r,this.getRowIndex(pid)+this._getOpenLenght(pid,0));
                else
                	r = this._insertRowAt(r);
                this._postRowProcessing(r,row)
		}
	}
	
	if (this._refresh_mode[2])
		for (id in del){
			if (del[id] && this.rowsAr[id])
				this.deleteRow(id);
		}

      if (window.eXcell_tree)
	  	eXcell_tree.prototype.setValue=eXcell_tree.prototype.setValueX;
	this.callEvent("onXLE",[this,rows.length]);
}


/**
*   @desc: get combobox specific for cell in question
*   @param: id - row id
*   @param: ind  - row index
*   @type:  public
*/
dhtmlXGridObject.prototype.getCustomCombo=function(id,ind){
	var cell= this.cells(id,ind).cell;
	if (!cell._combo)
		cell._combo = new dhtmlXGridComboObject();
	return cell._combo;
}
/**
*   @desc: set tab order of columns
*   @param: order - list of tab indexes (default delimiter is ",")
*   @type:  public
*/
dhtmlXGridObject.prototype.setTabOrder=function(order){
	var t=order.split(this.delim);
	this._tabOrder=[];
	for (var i=0; i < this._cCount; i++) 
		t[i]={c:parseInt(t[i]), ind:i};
	t.sort(function(a,b){  return (a.c>b.c?1:-1); });
	for (var i=0; i < this._cCount; i++) 
		if (!t[i+1] || (typeof t[i].c == "undefined"))
			this._tabOrder[t[i].ind]=(t[0].ind+1)*-1;
		else
			this._tabOrder[t[i].ind]=t[i+1].ind;
}
dhtmlXGridObject.prototype.i18n={
	loading:"Loading"
}
//key_ctrl_shift
dhtmlXGridObject.prototype._key_events={
			k13_1_0:function(){
				var rowInd = this.rowsCol._dhx_find(this.row)
                this.selectCell(this.rowsCol[rowInd+1],this.cell._cellIndex,true);
			},
			k13_0_1:function(){
				var rowInd = this.rowsCol._dhx_find(this.row)
                this.selectCell(this.rowsCol[rowInd-1],this.cell._cellIndex,true);
			},
			k13_0_0:function(){
				this.editStop();
            	this.callEvent("onEnter",[(this.row?this.row.idd:null),(this.cell?this.cell._cellIndex:null)]);
            },
            k9_0_0:function(){
				this.editStop();
				var z=this._getNextCell(null,1);
				if (z) {
					this.selectCell(z.parentNode,z._cellIndex,(this.row!=z.parentNode),false,true);
					this._still_active=true;
				}
            },
			k9_0_1:function(){
				this.editStop();
				var z=this._getNextCell(null,-1);
				if (z) {
					this.selectCell(z.parentNode,z._cellIndex,(this.row!=z.parentNode),false,true);
					this._still_active=true;
				}
            },
            k113_0_0:function(){
            	if (this._f2kE) this.editCell();
            },
            k32_0_0:function(){
            	var c=this.cells4(this.cell);
            	if (!c.changeState || (c.changeState()===false)) return false;
            },
            k27_0_0:function(){
            	this.editStop(true);
            },
            k33_0_0:function(){
            	if(this.pagingOn)
            		this.changePage(this.currentPage-1);
            	else this.scrollPage(-1);            		
	        },
			k34_0_0:function(){
            	if(this.pagingOn)
            		this.changePage(this.currentPage+1);
            	else this.scrollPage(1);
	        },
			k37_0_0:function(){
            	if(!this.editor && this.isTreeGrid())
            		this.collapseKids(this.row)
            	else return false;
	        },
			k39_0_0:function(){
				if(!this.editor && this.isTreeGrid())
            		this.expandKids(this.row)
            	else return false;
            },
			k40_0_0:function(){
				if (this.editor && this.editor.combo)
					this.editor.shiftNext();
				else{
					var rowInd = this.rowsCol._dhx_find(this.row)+1;
					if (rowInd!=this.rowsCol.length && rowInd!=this.obj.rows.length-1){
						var nrow=this._nextRow(rowInd-1,1);
						if (nrow._sRow || nrow._rLoad) return false;
                        this.selectCell(nrow,this.cell._cellIndex,true);
                    }
                    else {
                    	this._key_events.k34_0_0.apply(this,[]);
                    	if (this.pagingOn && this.rowsCol[(this.currentPage-1)*this.rowsBufferOutSize])
                    		this.selectCell((this.currentPage-1)*this.rowsBufferOutSize,0,true);
                    }
				}
            },
			k38_0_0:function(){		
				if (this.editor && this.editor.combo)
					this.editor.shiftPrev();
				else{
					var rowInd = this.rowsCol._dhx_find(this.row)+1;
					if (rowInd!=-1 && (!this.pagingOn || (this.currentPage-1)*this.rowsBufferOutSize+1 < rowInd )){
						var nrow=this._nextRow(rowInd-1,-1);
						if (!nrow || nrow._sRow || nrow._rLoad) return false;
                    	this.selectCell(nrow,this.cell._cellIndex,true);
					}
					else {
						this._key_events.k33_0_0.apply(this,[]);
						if (this.pagingOn && this.rowsCol[this.currentPage*this.rowsBufferOutSize-1])
                    		this.selectCell(this.currentPage*this.rowsBufferOutSize-1,0,true);
	                }
				}
            }
		};
/**
*   @desc: enables/disables mode when readonly cell is not available with tab 
*   @param: mode - (boolean) true/false
*   @type:  public
*/
dhtmlXGridObject.prototype.enableSmartTabOrder = function(mode){
	if(arguments.length > 0) this.smartTabOrder = convertStringToBoolean(mode);
	else this.smartTabOrder = true;
}
/**
*   @desc: sets elements which get focus when tab is pressed in the last or first (tab+shift) cell 
*   @param: start - html object or its id - gets focus when tab+shift are pressed in the first cell  
*   @param: end - html object or its id - gets focus when tab is pressed in the last cell  
*   @type:  public
*/
dhtmlXGridObject.prototype.setExternalTabOrder = function(start,end){
	var grid = this;
	this.tabStart = (typeof(start)=="object")?start: document.getElementById(start);
	this.tabStart.onkeydown = function(e){
		var ev = (e||window.event);
		ev.cancelBubble=true;
		if(ev.keyCode==9){
			grid.selectCell(0,0,0,0,1); 
			if(grid.cells2(0,0).isDisabled()){
				grid._key_events["k9_0_0"].call(grid);
			}
			return false;
		}
		
	}
	
	this.tabEnd = (typeof(end)=="object")?end:document.getElementById(end);
	this.tabEnd.onkeydown = function(e){
		var ev = (e||window.event);
		ev.cancelBubble=true;  
		if((ev.keyCode == 9) && ev.shiftKey){
			grid.selectCell((grid.getRowsNum()-1),(grid.getColumnCount()-1),0,0,1); 
			if(grid.cells2((grid.getRowsNum()-1),(grid.getColumnCount()-1)).isDisabled()){
				grid._key_events["k9_0_1"].call(grid);
			}
			return false;
		}
		
	}
	
}

//(c)dhtmlx ltd. www.dhtmlx.com


/**
*	@desc: dhtmlxGrid cell object constructor (shouldn't be accesed directly. Use cells and cells2 methods of the grid instead)
*	@type: cell
*	@returns: dhtmlxGrid cell
*/
function dhtmlXGridCellObject(obj){
	/**
	*	@desc: desctructor, clean used memory
	*	@type: public
	*/
    this.destructor=function(){
        this.cell.obj=null;
        this.cell=null;
        this.grid=null;
        this.base=null;
        return null;
    }
	this.cell = obj;
	/**
	*	@desc: gets Value of cell
	*	@type: public
	*/
	this.getValue = function(){
                        if ((this.cell.firstChild)&&(this.cell.firstChild.tagName=="TEXTAREA"))
                        return this.cell.firstChild.value;
                        else
						return this.cell.innerHTML._dhx_trim();//innerText;
					}

	/**
	*	@desc: gets math formula of cell if any
	*	@type: public
	*/
	this.getMathValue = function(){
                    if (this.cell._val)
						return this.cell._val;//innerText;
                    else return  this.getValue();
					}
	/**
	*	@desc: determ. font style if it was set
	*	@returns: font name only if it was set for the cell
	*	@type: public
	*/
	this.getFont = function(){
						arOut = new Array(3);
						if(this.cell.style.fontFamily)
							arOut[0] = this.cell.style.fontFamily
						if(this.cell.style.fontWeight=='bold' || this.cell.parentNode.style.fontWeight=='bold')
							arOut[1] = 'bold';
						if(this.cell.style.fontStyle=='italic' || this.cell.parentNode.style.fontWeight=='italic')
							arOut[1] += 'italic';
						if(this.cell.style.fontSize)
							arOut[2] = this.cell.style.fontSize
						else
							arOut[2] = "";
						return arOut.join("-")
					}
	/**
	*	@desc: determ. cell's text color
	*	@returns: cell's text color
	*	@type: public
	*/
	this.getTextColor = function(){
							if(this.cell.style.color)
								return this.cell.style.color
							else
								return "#000000";
						}
	/**
	*	@desc: determ. cell's background color
	*	@returns: cell's background color
	*	@type: public
	*/
	this.getBgColor = function(){
							if(this.cell.bgColor)
								return this.cell.bgColor
							else
								return "#FFFFFF";
						}
	/**
	*	@desc: determines horisontal align od the cell
	*	@returns: horisontal align of cell content
	*	@type: public
	*/
	this.getHorAlign = function(){
							if(this.cell.style.textAlign)
								return this.cell.style.textAlign;
							else if(this.cell.align)
								return this.cell.align
							else
								return "left";
						}
	/**
	*	@desc: gets width of the cell in pixel
	*	@returns: width of the cell in pixels
	*	@type: public
	*/
	this.getWidth = function(){
		return this.cell.scrollWidth;
	}

	/**
	*	@desc: sets font family to the cell
	*	@param: val - string in format: Arial-bold(italic,bolditalic,underline)-12px
	*	@type: public
	*/
	this.setFont = function(val){
						fntAr = val.split("-");
						this.cell.style.fontFamily = fntAr[0];
						this.cell.style.fontSize = fntAr[fntAr.length-1]
						if(fntAr.length==3){
							if(/bold/.test(fntAr[1]))
								this.cell.style.fontWeight = "bold";
							if(/italic/.test(fntAr[1]))
								this.cell.style.fontStyle = "italic";
							if(/underline/.test(fntAr[1]))
								this.cell.style.textDecoration = "underline";

						}

					}
	/**
	*	@desc: sets text color to the cell
	*	@param: val - color value (name or hex)
	*	@type: public
	*/
	this.setTextColor = function(val){
							this.cell.style.color = val;
						}
	/**
	*	@desc: sets background color to the cell
	*	@param: val - color value (name or hex)
	*	@type: public
	*/
	this.setBgColor = function(val){
							if(val=="")
								val = null;
							this.cell.bgColor = val;
						}
	/**
	*	@desc: sets horisontal align to the cell
	*	@param: val - value in single-letter or full format(exmp: r or right)
	*	@type: public
	*/
	this.setHorAlign = function(val){
							if(val.length==1){
								if(val=='c')
									this.cell.style.textAlign = 'center'
								else if(val=='l')
									this.cell.style.textAlign = 'left';
								else
									this.cell.style.textAlign = 'right';
							}else
								this.cell.style.textAlign = val
						}
	/**
	*	@desc: determines whether cell value was changed
	*	@returns: true if cell value was changed, otherwise - false
	*	@type: public
	*/
	this.wasChanged = function(){
							if(this.cell.wasChanged)
								return true;
							else
								return false;
						}
	/**
	*	@desc: determines whether first child of the cell is checkbox or radio
	*	@returns: true if first child of the cell is input element of type radio or checkbox
	*	@type: deprecated
	*/
	this.isCheckbox = function(){
							var ch = this.cell.firstChild;
							if(ch && ch.tagName=='INPUT'){
								type = ch.type;
								if(type=='radio' || type=='checkbox')
									return true;
								else
									return false;
							}else
								return false;
						}
	/**
	*	@desc: determines whether radio or checkbox inside is checked
	*	@returns: true if first child of the cell is checked
	*	@type: public
	*/
	this.isChecked = function(){
							if(this.isCheckbox()){
								return this.cell.firstChild.checked;
							}
						}
	/**
	*	@desc: determines whether cell content (radio,checkbox) is disabled
	*	@returns: true if first child of the cell is disabled
	*	@type: public
	*/
	this.isDisabled = function(){
						return this.cell._disabled;
						}
	/**
	*	@desc: checks checkbox or radion
	*	@param: fl - true or false
	*	@type: public
	*/
	this.setChecked = function(fl){
							if(this.isCheckbox()){
								if(fl!='true' && fl!=1)
									fl = false;
								this.cell.firstChild.checked = fl;
							}
						}
	/**
	*	@desc: disables radio or checkbox
	*	@param: fl - true or false
	*	@type: public
	*/
	this.setDisabled = function(fl){
							if(fl!='true' && fl!=1)
								fl = false;
							if(this.isCheckbox()){
								this.cell.firstChild.disabled = fl;
                                if (this.disabledF) this.disabledF(fl);
							}
							this.cell._disabled = fl;
						}
}


/**
*	@desc: sets value to the cell
*	@param: val - new value
*	@type: public
*/
dhtmlXGridCellObject.prototype.setValue = function(val){
	if((typeof(val)!="number") && (!val || val.toString()._dhx_trim()=="")){
		val="&nbsp;"
 		this.cell._clearCell=true;
	} else this.cell._clearCell=false;
	this.setCValue(val);
}
/**
*	@desc: sets value to the cell
*	@param: val - new value
*	@param: val2
*	@type: private
*/
dhtmlXGridCellObject.prototype.getTitle=function(){ return (_isIE?this.cell.innerText:this.cell.textContent); }

dhtmlXGridCellObject.prototype.setCValue = function(val,val2){
						this.cell.innerHTML = val;
//#__pro_feature:21092006{
//#on_cell_changed:23102006{
                        this.grid.callEvent("onCellChanged",[this.cell.parentNode.idd,this.cell._cellIndex, (arguments.length>1?val2:val)]);
//#}
//#}
}
dhtmlXGridCellObject.prototype.setCTxtValue = function(val){
						this.cell.innerHTML="";
						this.cell.appendChild(document.createTextNode(val));
//#__pro_feature:21092006{
//#on_cell_changed:23102006{
                        this.grid.callEvent("onCellChanged",[this.cell.parentNode.idd,this.cell._cellIndex, val]);
//#}
//#}
}

/**
*	@desc: sets text representation of cell which contains math formula ( setLabel doesn't triger math calculations as setValue do)
*	@param: val - new value
*	@type: public
*/
dhtmlXGridCellObject.prototype.setLabel = function(val){
						this.cell.innerHTML = val;
				}

/**
*	@desc: get formula of ExCell ( actual only for math based exCells )
*	@type: public
*/
dhtmlXGridCellObject.prototype.getMath = function(){
                    if (this._val) return this.val;
                    else
                        return this.getValue();
				}

/**
*	@desc: dhtmlxGrid cell editor constructor (base for all eXcells). Shouldn't be accessed directly
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell(){
	this.obj = null;//editor
	//this.cell = null//cell to get value from
	this.val = null;//current value (before edit)
	/**
	*	@desc: occures on space for example 
	*	@type: private
	*/
	this.changeState = function(){return false}
	/**
	*	@desc: opens editor
	*	@type: private
	*/
	this.edit = function(){this.val = this.getValue()}//
	/**
	*	@desc: return value to cell, closes editor
	*	@returns: if cell's value was changed (true) or not
	*	@type: private
	*/
	this.detach = function(){return false}//
	/**
	*	@desc: gets position (left-right) of element
	*	@param: oNode - element to get position of
	*	@type: private
	*	@topic: 8
	*/
	this.getPosition = function(oNode){
			   var oCurrentNode=oNode;
			   var iLeft=0;
			   var iTop=0;
			   while(oCurrentNode.tagName!="BODY"){
			      iLeft+=oCurrentNode.offsetLeft;
				  iTop+=oCurrentNode.offsetTop;
			      oCurrentNode=oCurrentNode.offsetParent;
			   }
			   return new Array(iLeft,iTop);
			}
}
eXcell.prototype = new dhtmlXGridCellObject;


/**
*	@desc: simple text editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_ed(cell){
	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}
	this.edit = function(){
					this.cell.atag=((!this.grid.multiLine)&&(_isKHTML||_isMacOS||_isFF))?"INPUT":"TEXTAREA";
					this.val = this.getValue();
					this.obj = document.createElement(this.cell.atag);
					this.obj.setAttribute("autocomplete","off");
					this.obj.style.height = (this.cell.offsetHeight-(_isIE?4:2))+"px";
                    this.obj.className="dhx_combo_edit";
				   	this.obj.wrap = "soft";
					this.obj.style.textAlign = this.cell.align;
					this.obj.onclick = function(e){(e||event).cancelBubble = true}
					this.obj.onmousedown = function(e){(e||event).cancelBubble = true}
					this.obj.value = this.val
					this.cell.innerHTML = "";
					this.cell.appendChild(this.obj);
				  	if (_isFF) {
						this.obj.style.overflow="visible";
						if ((this.grid.multiLine)&&(this.obj.offsetHeight>=18)&&(this.obj.offsetHeight<40)){
							this.obj.style.height="36px";
							this.obj.style.overflow="scroll";
						}
					}
                    this.obj.onselectstart=function(e){  if (!e) e=event; e.cancelBubble=true; return true;  };
					this.obj.focus()
  					this.obj.focus()
				}
	this.getValue = function(){
        if ((this.cell.firstChild)&&((this.cell.atag)&&(this.cell.firstChild.tagName==this.cell.atag)))
            return this.cell.firstChild.value;
    	if (this.cell._clearCell)
    		return "";
    	return this.cell.innerHTML.toString()._dhx_trim();
	}

	this.detach = function(){
					this.setValue(this.obj.value);
					return this.val!=this.getValue();
				}

}
eXcell_ed.prototype = new eXcell;

/**
*	@desc: pure text editor ( HTML not supported )
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_edtxt(cell){
	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}
	this.getValue = function(){
        if ((this.cell.firstChild)&&((this.cell.atag)&&(this.cell.firstChild.tagName==this.cell.atag)))
            return this.cell.firstChild.value;
    	if (this.cell._clearCell)
    		return "";            
		return (_isIE?this.cell.innerText:this.cell.textContent);
	}
	this.setValue = function(val){
						if(!val || val.toString()._dhx_trim()==""){
							val=" ";
							this.cell._clearCell=true;
						}						
						else this.cell._clearCell=false;
						this.setCTxtValue(val);
	}
}
eXcell_edtxt.prototype = new eXcell_ed;

//#__pro_feature:21092006{
//#data_format:12052006{
/**
*	@desc: simple numeric text editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*	@edition: professional
*/
function eXcell_edn(cell){
	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}
	this.getValue = function(){
		//this.grid.editStop();
        if ((this.cell.firstChild)&&(this.cell.firstChild.tagName=="TEXTAREA"))
            return this.cell.firstChild.value;
        if (this.cell._clearCell)
    		return "";
		return this.grid._aplNFb(this.cell.innerHTML.toString()._dhx_trim(),this.cell._cellIndex);
	}

	this.detach = function(){
                    var tv=this.obj.value;
					this.setValue(tv);
					return this.val!=this.getValue();
				}
}
eXcell_edn.prototype = new eXcell_ed;
eXcell_edn.prototype.setValue = function(val){
						if(!val || val.toString()._dhx_trim()==""){
							val="0"
							this.cell._clearCell=true;
						} else this.cell._clearCell=false;
						this.setCValue(this.grid._aplNF(val,this.cell._cellIndex));
				}

//#}
//#}

/**
*	@desc: checkbox editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_ch(cell){
	if(cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
		this.cell.obj = this;
	}

    this.disabledF=function(fl){
        if ((fl==true)||(fl==1))
            this.cell.innerHTML=this.cell.innerHTML.replace("item_chk0.","item_chk0_dis.").replace("item_chk1.","item_chk1_dis.");
        else
            this.cell.innerHTML=this.cell.innerHTML.replace("item_chk0_dis.","item_chk0.").replace("item_chk1_dis.","item_chk1.");
    }

	this.changeState = function(){ 
					//nb:
                    if ((!this.grid.isEditable)||(this.cell.parentNode._locked)||(this.isDisabled())) return;
						if(this.grid.callEvent("onEditCell",[0,this.cell.parentNode.idd,this.cell._cellIndex])){
							this.val = this.getValue()
							if(this.val=="1")
								this.setValue("0")
							else
								this.setValue("1")

							this.cell.wasChanged=true;
							//nb:
							this.grid.callEvent("onEditCell",[1,this.cell.parentNode.idd,this.cell._cellIndex]);
							this.grid.callEvent("onCheckbox",[this.cell.parentNode.idd,this.cell._cellIndex,(this.val!='1')]);
							this.grid.callEvent("onCheck",[this.cell.parentNode.idd,this.cell._cellIndex,(this.val!='1')]);
						}else{//preserve editing (not tested thoroughly for this editor)
							this.editor=null;
						}
				}
	this.getValue = function(){
						return this.cell.chstate?this.cell.chstate.toString():"0";
					}

	this.isCheckbox = function(){
						return true;
					}
	this.isChecked = function(){
						if(this.getValue()=="1")
							return true;
						else
							return false;
					}
	this.setChecked = function(fl){
	this.setValue(fl.toString())
	}
	this.detach = function(){
						return this.val!=this.getValue();
					}
	this.edit=null;
}
eXcell_ch.prototype = new eXcell;
eXcell_ch.prototype.setValue = function(val){
                        this.cell.style.verticalAlign = "middle";//nb:to center checkbox in line
						//val can be int
						if (val){
	                        val=val.toString()._dhx_trim();
							if ((val=="false")||(val=="0")) val="";
							}
						if(val){
							val = "1";
							this.cell.chstate = "1";
						}else{
							val = "0";
							this.cell.chstate = "0"
						}
						var obj = this;
						this.setCValue("<img src='"+this.grid.imgURL+"item_chk"+val+".gif' onclick='new eXcell_ch(this.parentNode).changeState(); (arguments[0]||event).cancelBubble=true; '>",this.cell.chstate);
					}

/**
*	@desc: radio editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_ra(cell){
	this.base = eXcell_ch;
	this.base(cell)
    this.grid = cell.parentNode.grid;

    this.disabledF=function(fl){
        if ((fl==true)||(fl==1))
            this.cell.innerHTML=this.cell.innerHTML.replace("radio_chk0.","radio_chk0_dis.").replace("radio_chk1.","radio_chk1_dis.");
        else
            this.cell.innerHTML=this.cell.innerHTML.replace("radio_chk0_dis.","radio_chk0.").replace("radio_chk1_dis.","radio_chk1.");
    }

	this.changeState = function(){
                        if ((!this.grid.isEditable)||(this.cell.parentNode._locked)) return;
						if(this.grid.callEvent("onEditCell",[0,this.cell.parentNode.idd,this.cell._cellIndex])!=false){
							this.val = this.getValue()
							if(this.val=="1")
								this.setValue("0")
							else
								this.setValue("1")
							this.cell.wasChanged=true;
							//nb:
							this.grid.callEvent("onEditCell",[1,this.cell.parentNode.idd,this.cell._cellIndex]);
							this.grid.callEvent("onCheckbox",[this.cell.parentNode.idd,this.cell._cellIndex,(this.val!='1')]);
							this.grid.callEvent("onCheck",[this.cell.parentNode.idd,this.cell._cellIndex,(this.val!='1')]);
						}else{//preserve editing (not tested thoroughly for this editor)
							this.editor=null;
						}
				}
	this.edit=null;
}
eXcell_ra.prototype = new eXcell_ch;
eXcell_ra.prototype.setValue = function(val){
						this.cell.style.verticalAlign = "middle";//nb:to center checkbox in line
						if (val){
	                        val=val.toString()._dhx_trim();
							if ((val=="false")||(val=="0")) val="";
							}
						if(val){
							if (!this.grid._RaSeCol) this.grid._RaSeCol=[];
							if (this.grid._RaSeCol[this.cell._cellIndex]){
								var z=this.grid.cells4(this.grid._RaSeCol[this.cell._cellIndex]);
								z.setValue("0")
                                this.grid.callEvent("onEditCell",[1,z.cell.parentNode.idd,z.cell._cellIndex]);
							}										
							this.grid._RaSeCol[this.cell._cellIndex]=this.cell;
							
							val = "1";
							this.cell.chstate = "1";
						}else{
							val = "0";
							this.cell.chstate = "0"
						}
						var obj = this;
						this.setCValue("<img src='"+this.grid.imgURL+"radio_chk"+val+".gif' onclick='this.parentNode.obj.changeState()'>",this.cell.chstate);
					}
/**
*	@desc: multilene popup editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_txt(cell){
	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}
	this.edit = function(){
					this.val = this.getValue()
					this.obj = document.createElement("TEXTAREA");
                    this.obj.className="dhx_textarea";
	
					this.obj.onclick = function(e){(e||event).cancelBubble = true}
					var arPos = this.grid.getPosition(this.cell);//,this.grid.objBox
                    if (!this.cell._clearCell)
    					this.obj.value = this.val;

					this.obj.style.display = "";
					this.obj.style.textAlign = this.cell.align;
					if (_isFF){
						var z_ff=document.createElement("DIV");
						z_ff.appendChild(this.obj);
						z_ff.style.overflow="auto";
						z_ff.className="dhx_textarea";
						this.obj.style.margin="0px 0px 0px 0px";
					    this.obj.style.border="0px";
	  					this.obj=z_ff;
						}
					document.body.appendChild(this.obj);//nb:
					this.obj.onkeydown=function(e){
						var ev=(e||event);
						if (ev.keyCode==9) {
							globalActiveDHTMLGridObject.entBox.focus();
							globalActiveDHTMLGridObject.doKey({keyCode:ev.keyCode,shiftKey:ev.shiftKey,srcElement:"0"});
							return false;
							}
					}

					this.obj.style.left = arPos[0]+"px";
					this.obj.style.top = arPos[1]+this.cell.offsetHeight+"px";
					if(this.cell.scrollWidth<200)
						var pw=200;
					else
						var pw=this.cell.scrollWidth;
					this.obj.style.width = pw+(_isFF?18:16)+"px"
					if (_isFF){
						 this.obj.firstChild.style.width = parseInt(this.obj.style.width)+"px";
						 this.obj.firstChild.style.height = this.obj.offsetHeight-3+"px";
						 }

					   
					   if (_isFF) this.obj.firstChild.focus();
					   else{
					   	this.obj.focus();
					   	this.obj.focus()
					   	}
					   

				}
	this.detach = function(){
					var a_val="";
					if (_isFF) 
							a_val=this.obj.firstChild.value;
					else a_val=this.obj.value;
                    if (a_val=="") {
                        this.cell._clearCell=true;
                        }
                    else this.cell._clearCell=false;
					this.setValue(a_val);
					//isIE()?this.obj.removeNode(true):this.grid.objBox.removeChild(this.obj);
					document.body.removeChild(this.obj);

					return this.val!=this.getValue();
				}
	this.getValue = function(){
                        if (this.cell.firstChild){
							if (this.cell.firstChild.tagName=="TEXTAREA")
		                        return this.obj.firstChild.value;
							else
							if (this.cell.firstChild.tagName=="DIV")
		                        return this.obj.firstChild.firstChild.value;
						}
						
						if (this.cell._clearCell) return "";
                        if ((!this.grid.multiLine))
							return this.cell._brval||this.cell.innerHTML;
						else 
							return this.cell.innerHTML.replace(/<br[^>]*>/gi,"\n")._dhx_trim();//innerText;
					}
}
eXcell_txt.prototype = new eXcell;

/**
*	@desc: multiline text editor without HTML support
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_txttxt(cell){
	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}
	this.getValue = function(){
        if ((this.cell.firstChild)&&(this.cell.firstChild.tagName=="TEXTAREA"))
            return this.cell.firstChild.value;
            
		if (this.cell._clearCell) return "";
            if ((!this.grid.multiLine) && this.cell._brval)
				return this.cell._brval;
				
		return (_isIE?this.cell.innerText:this.cell.textContent);
	}
	this.setValue = function(val){
						this.cell._brval=val;
						if(!val || val.toString()._dhx_trim()=="")
							val=" ";
						this.setCTxtValue(val);
	}
}

eXcell_txttxt.prototype = new eXcell_txt;



eXcell_txt.prototype.setValue = function(val){
						if(!val || val.toString()._dhx_trim()==""){
							val="&nbsp;"
                            this.cell._clearCell=true;
                            }
						else this.cell._clearCell=false;

						this.cell._brval=val;
  						if ((!this.grid.multiLine))
							this.setCValue(val,val);
						else
							this.setCValue(val.replace(/\n/g,"<br/>"),val);
				}


/**
*	@desc: combobox editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_co(cell){
     	if (cell){
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
		this.combo = (this.cell._combo||this.grid.getCombo(this.cell._cellIndex));
		this.editable = true
   }
    this.shiftNext=function(){
        var z=this.list.options[this.list.selectedIndex+1];
        if (z) z.selected=true;
        this.obj.value=this.list.options[this.list.selectedIndex].text;

        return true;
    }
    this.shiftPrev=function(){
		if (this.list.selectedIndex!=0){
        	var z=this.list.options[this.list.selectedIndex-1];
    	    if (z) z.selected=true;
	        this.obj.value=this.list.options[this.list.selectedIndex].text;
	    }

       return true;
    }

	this.edit = function(){
						this.val = this.getValue();
						this.text = this.getText()._dhx_trim();
						var arPos = this.grid.getPosition(this.cell)//,this.grid.objBox)

						this.obj = document.createElement("TEXTAREA");
                        this.obj.className="dhx_combo_edit";
						this.obj.style.height=(this.cell.offsetHeight-4)+"px";

							this.obj.wrap = "soft";
							this.obj.style.textAlign = this.cell.align;
							this.obj.onclick = function(e){(e||event).cancelBubble = true}
							this.obj.value = this.text
							this.obj.onselectstart=function(e){if(!e)e=event;e.cancelBubble=true;return true;};							
							var editor_obj = this;
							this.obj.onkeyup=function(e){
								var val=this.readonly?String.fromCharCode((e||event).keyCode):this.value;
								var c=editor_obj.list.options;
								for (var i=0; i<c.length; i++)
									if (c[i].text.indexOf(val)==0)
										return c[i].selected=true;
							}
                            this.list =  document.createElement("SELECT");
                        	
                            this.list.className='dhx_combo_select';
                            this.list.style.width=this.cell.offsetWidth+"px";
							this.list.style.left = arPos[0]+"px";//arPos[0]
							this.list.style.top = arPos[1]+this.cell.offsetHeight+"px";//arPos[1]+this.cell.offsetHeight;
							this.list.onclick = function(e){
														var ev = e||window.event;
														var cell = ev.target||ev.srcElement
                                                        //tbl.editor_obj.val=cell.combo_val;
                                                        if (cell.tagName=="OPTION") cell=cell.parentNode;
														editor_obj.setValue(cell.value);
                                                        editor_obj.editable=false;
														editor_obj.grid.editStop();
													}
							var comboKeys = this.combo.getKeys();
							var fl=false
                            var selOptId=0;
							for(var i=0;i<comboKeys.length;i++){
									var val = this.combo.get(comboKeys[i])
                                    this.list.options[this.list.options.length]=new Option(val,comboKeys[i]);
									if(comboKeys[i]==this.val){
                                        selOptId=this.list.options.length-1;
                                        fl=true;
                                        }
                                    }

							if(fl==false){//if no such value in combo list
      						   this.list.options[this.list.options.length]=new Option(this.text,this.val===null?"":this.val);
                               selOptId=this.list.options.length-1;
							}
						document.body.appendChild(this.list)//nb:this.grid.objBox.appendChild(this.listBox);
                        this.list.size="6";						
                        this.cstate=1;
						if(this.editable){
							this.cell.innerHTML = "";
						}
                        else {
                            this.obj.style.width="1px";
                            this.obj.style.height="1px";
                            }
							this.cell.appendChild(this.obj);
                            this.list.options[selOptId].selected=true;
							//fix for coro - FF scrolls grid in incorrect position
							if ((!_isFF)||(this.editable)){
   								this.obj.focus();
	   						   	this.obj.focus();
							}
                            if (!this.editable){
                                this.obj.style.visibility="hidden";
                                this.list.focus();
                                this.list.onkeydown=function(e){
                                	editor_obj.grid.setActive(true)
                                	if ((e||window.event).keyCode<30)
                                		return editor_obj.grid.doKey({target:editor_obj.cell, keyCode:(e||window.event).keyCode})
                                }
                            }
					}

	this.getValue = function(){
						return ((this.cell.combo_value==window.undefined)?"":this.cell.combo_value);
					}
	this.detach = function(){
						if(this.val!=this.getValue()){
							this.cell.wasChanged = true;
			   }
						if(this.list.parentNode!=null){
                        if (this.editable)
							if(this.obj.value._dhx_trim()!=this.text){
								if (this.list.selectedIndex && this.list.options[this.list.selectedIndex].text==this.obj.value)
									this.setValue(this.list.value)
								else
									this.setValue(this.obj.value)
							}else{
								this.setValue(this.val)
							}
                        else
                            this.setValue(this.list.value)
						}
							if(this.list.parentNode)
								this.list.parentNode.removeChild(this.list);
							if(this.obj.parentNode)
								this.obj.parentNode.removeChild(this.obj);

						return this.val!=this.getValue();
					}
}
eXcell_co.prototype = new eXcell;
eXcell_co.prototype.getText = function(){ return this.cell.innerHTML; }
eXcell_co.prototype.setValue = function(val){
						if (typeof(val)=="object"){
							var optCol=this.grid.xmlLoader.doXPath("./option",val);
                            if (optCol.length)
                            	this.cell._combo=new dhtmlXGridComboObject();
                                for (var j=0;j<optCol.length; j++)
									this.cell._combo.put(optCol[j].getAttribute("value"),optCol[j].firstChild?optCol[j].firstChild.data:"");
							val=val.firstChild.data;	
						}
						if ((val||"").toString()._dhx_trim()=="")
							val=null

                        if (val!==null)
  						    this.setCValue((this.cell._combo||this.grid.getCombo(this.cell._cellIndex)).get(val) || val,val);
                        else
                            this.setCValue("&nbsp;",val);

						this.cell.combo_value = val;
					}
/**
*	@desc: selectbox editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_coro(cell){
	this.base = eXcell_co;
	this.base(cell)
	this.editable = false;
}
eXcell_coro.prototype = new eXcell_co;
function eXcell_cotxt(cell){
	this.base = eXcell_co;
	this.base(cell)
}   
eXcell_cotxt.prototype = new eXcell_co;   
eXcell_cotxt.prototype.getText = function(){ return (_isIE?this.cell.innerText:this.cell.textContent); }
eXcell_cotxt.prototype.setValue = function(val){
						if (typeof(val)=="object"){
							var optCol=this.grid.xmlLoader.doXPath("./option",val);
                            if (optCol.length)
                            	this.cell._combo=new dhtmlXGridComboObject();
                                for (var j=0;j<optCol.length; j++)
									this.cell._combo.put(optCol[j].getAttribute("value"),optCol[j].firstChild?optCol[j].firstChild.data:"");
							val=val.firstChild.data;	
						}
						if ((val||"").toString()._dhx_trim()=="")
							val=null

                        if (val!==null)
  						    this.setCTxtValue((this.cell._combo||this.grid.getCombo(this.cell._cellIndex)).get(val) || val,val);
                        else
                            this.setCTxtValue(" ",val);

						this.cell.combo_value = val;
}

function eXcell_corotxt(cell){
	this.base = eXcell_co;
	this.base(cell)
	this.editable = false;
}
eXcell_corotxt.prototype = new eXcell_cotxt;

/**
*	@desc: color picker editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_cp(cell){
	try{
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}catch(er){}
	this.edit = function(){
						this.val = this.getValue()
						this.obj = document.createElement("SPAN");
						this.obj.style.border = "1px solid black";
						this.obj.style.position = "absolute";
						var arPos = this.grid.getPosition(this.cell);//,this.grid.objBox
						this.colorPanel(4,this.obj)
						document.body.appendChild(this.obj);//this.grid.objBox.appendChild(this.obj);
						this.obj.style.left = arPos[0]+"px";
						this.obj.style.top = arPos[1]+this.cell.offsetHeight+"px";
					}
	this.toolDNum = function(value){
						if(value.length==1)
							value = '0'+value;
						return value;
					}
	this.colorPanel = function(index,parent){
						var tbl = document.createElement("TABLE");
						parent.appendChild(tbl)
						tbl.cellSpacing = 0;
						tbl.editor_obj = this;
						tbl.style.cursor = "default";
						tbl.style.cursor = "table-layout:fixed";
						tbl.onclick = function(e){
											var ev = e||window.event
											var cell = ev.target||ev.srcElement;
											var ed = cell.parentNode.parentNode.parentNode.editor_obj
											ed.setValue(cell.style.backgroundColor)
											ed.grid.editStop();
										}
						var cnt = 256/index;
						for(var j=0;j<=(256/cnt);j++){
							var r = tbl.insertRow(j);
							for(var i=0;i<=(256/cnt);i++){
								for(var n=0;n<=(256/cnt);n++){
									R = new Number(cnt*j)-(j==0?0:1)
									G = new Number(cnt*i)-(i==0?0:1)
									B = new Number(cnt*n)-(n==0?0:1)
									var rgb = this.toolDNum(R.toString(16))+""+this.toolDNum(G.toString(16))+""+this.toolDNum(B.toString(16));
									var c = r.insertCell(i);
										c.width = "10px";
										c.innerHTML = "&nbsp;";//R+":"+G+":"+B;//
										c.title = rgb.toUpperCase()
										c.style.backgroundColor = "#"+rgb
										if(this.val!=null && "#"+rgb.toUpperCase()==this.val.toUpperCase()){
											c.style.border = "2px solid white"
										}
								}
							}
						}
					}
	this.getValue = function(){
						return this.cell.firstChild.style?this.cell.firstChild.style.backgroundColor:"";//this.getBgColor()
					}
	this.getRed	  =	function(){
						return Number(parseInt(this.getValue().substr(1,2),16))
					}
	this.getGreen	= function(){
						return Number(parseInt(this.getValue().substr(3,2),16))
					}
	this.getBlue	= function(){
						return Number(parseInt(this.getValue().substr(5,2),16))
					}				
	this.detach = function(){
					if(this.obj.offsetParent!=null)
						document.body.removeChild(this.obj);
					//this.obj.removeNode(true)
					return this.val!=this.getValue();
				}
}
eXcell_cp.prototype = new eXcell;
eXcell_cp.prototype.setValue = function(val){
	this.setCValue("<div style='width:100%;height:"+(this.cell.offsetHeight-2)+";background-color:"+(val||"")+";border:0px;'>&nbsp;</div>",val);
					}



/**
*	@desc: image editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
/*
	The corresponding  cell value in XML should be a "^" delimited list of following values:
	1st - image src
	2nd - image alt text (optional)
	3rd - link (optional)
	4rd - target (optional, default is _self)
*/
function eXcell_img(cell){
	try{
		this.cell = cell;
		this.grid = this.cell.parentNode.grid;
	}catch(er){}
	this.getValue = function(){
		 if(this.cell.firstChild.tagName=="IMG")
		 	return this.cell.firstChild.src+(this.cell.titFl!=null?"^"+this.cell.tit:"");
		 else if(this.cell.firstChild.tagName=="A"){
		 	var out = this.cell.firstChild.firstChild.src+(this.cell.titFl!=null?"^"+this.cell.tit:"");
		 	out+="^"+this.cell.lnk;
			if(this.cell.trg)
				out+="^"+this.cell.trg
			return out;
		 }
	}
}
eXcell_img.prototype = new eXcell;
eXcell_img.prototype.getTitle = function(){
		return this.cell.tit
	}
eXcell_img.prototype.setValue = function(val){
					var title = val;
					if(val.indexOf("^")!=-1){
						var ar = val.split("^");
						val = ar[0]
						title = ar[1];
						//link
						if(ar.length>2){
							this.cell.lnk = ar[2]
							if(ar[3])
								this.cell.trg = ar[3]
						}
						this.cell.titFl = "1";
					}
					this.setCValue("<img src='"+(val||"")._dhx_trim()+"' border='0'>",val);
					if(this.cell.lnk){
						this.cell.innerHTML = "<a href='"+this.cell.lnk+"' target='"+this.cell.trg+"'>"+this.cell.innerHTML+"</a>"
					}
					this.cell.tit = title;
				}


/**
*	@desc: text editor with price (USD) formatting
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_price(cell){
	this.base = eXcell_ed;
	this.base(cell)
	this.getValue = function(){
		if(this.cell.childNodes.length>1)
			return this.cell.childNodes[1].innerHTML.toString()._dhx_trim()
		else
			return "0";
	}
}
eXcell_price.prototype = new eXcell_ed;
eXcell_price.prototype.setValue = function(val){
		if(isNaN(Number(val))){
			if(!(val||"") || (val||"")._dhx_trim()!="")
				val = 0;//alert("Value must be an integer")
			val = this.val || 0;
		}
		var color = "green";
		if(val<0) color = "red";
			
		this.setCValue("<span>$</span><span style='padding-right:2px;color:"+color+";'>"+val+"</span>",val);
	}


/**
*	@desc: text editor with additional formatting for positive and negative numbers (arrow down/up and color)
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_dyn(cell){
	this.base = eXcell_ed;
	this.base(cell)
	this.getValue = function(){
		return this.cell.firstChild.childNodes[1].innerHTML.toString()._dhx_trim()
	}

}

eXcell_dyn.prototype = new eXcell_ed;
eXcell_dyn.prototype.setValue = function(val){
		if(!val || isNaN(Number(val))){
			val = 0;
		}
		if(val>0){
			var color = "green";
			var img = "dyn_up.gif";
		}else if (val==0){
			var color = "black";
			var img = "dyn_.gif";
		}else{
			var color = "red";
			var img = "dyn_down.gif";
		}
		this.setCValue("<div style='position:relative;padding-right:2px; width:100%;'><img src='"+this.grid.imgURL+""+img+"' height='15' style='position:absolute;top:0px;left:0px;'><span style='width:100%;color:"+color+";'>"+val+"</span></div>",val);
	}



/**
*	@desc: readonly editor
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_ro(cell){
	if (cell){
		this.cell = cell;
    	this.grid = this.cell.parentNode.grid;
	}
	this.edit = function(){}
	this.isDisabled = function(){ return true; }
}
eXcell_ro.prototype = new eXcell;

function eXcell_ron(cell){
	this.cell = cell;
    this.grid = this.cell.parentNode.grid;
	this.edit = function(){}
	this.isDisabled = function(){ return true; }
	this.getValue = function(){
		return this.grid._aplNFb(this.cell.innerHTML.toString()._dhx_trim(),this.cell._cellIndex);
	}
}
eXcell_ron.prototype = new eXcell;
eXcell_ron.prototype.setValue = function(val){
						if (val===0) 
							val="0";
						else if(!val || val.toString()._dhx_trim()=="")
							return this.setCValue("&nbsp;");
						this.setCValue(this.grid._aplNF(val,this.cell._cellIndex));
				}
 

/**
*	@desc: readonly pure text editor (without HTML support)
*	@returns: dhtmlxGrid cell editor object
*	@type: public
*/
function eXcell_rotxt(cell){
	this.cell = cell;
    this.grid = this.cell.parentNode.grid;
	this.edit = function(){}
	this.isDisabled = function(){ return true; }
	this.setValue = function(val){
						if(!val || val.toString()._dhx_trim()=="")
							val=" ";
						this.setCTxtValue(val);
	}
}
eXcell_rotxt.prototype = new eXcell;


/**
	*	@desc: combobox object constructor (shouldn't be accessed directly - instead please use getCombo(...) method of the grid)
	*	@type: private
	*	@returns: combobox for dhtmlxGrid
	*/
function dhtmlXGridComboObject(){
	this.keys = new dhtmlxArray();
	this.values = new dhtmlxArray();
	/**
	*	@desc: puts new combination of key and value into combobox
	*	@type: public
	*	@param: key - object to use as a key (should be a string in the case of combobox)
	*	@param: value - object value of combobox line
	*/
	this.put = function(key,value){
					for(var i=0;i<this.keys.length;i++){
						if(this.keys[i]==key){
							this.values[i]=value;
							return true;
						}
					}
					this.values[this.values.length] = value;
					this.keys[this.keys.length] = key;
				}
	/**
	*	@desc: gets value corresponding to the given key
	*	@type: public
	*	@param: key - object to use as a key (should be a string in the case of combobox)
	*	@returns: value correspond. to given key or null if no such key
	*/
	this.get = function(key){
					for(var i=0;i<this.keys.length;i++){
						if(this.keys[i]==key){
							return this.values[i];
						}
					}
					return null;
				}
	/**
	*	@desc: clears combobox
	*	@type: public
	*/
	this.clear = function(){
					/*for(var i=0;i<this.keys.length;i++){
							this.keys._dhx_removeAt(i);
							this.values._dhx_removeAt(i);
					}*/
					this.keys = new dhtmlxArray();
					this.values = new dhtmlxArray();
				}
	/**
	*	@desc: remove pair of key-value from combobox with given key 
	*	@type: public
	*	@param: key - object to use as a key
	*/
	this.remove = function(key){
					for(var i=0;i<this.keys.length;i++){
						if(this.keys[i]==key){
							this.keys._dhx_removeAt(i);
							this.values._dhx_removeAt(i);
							return true;
						}
					}
				}
	/**
	*	@desc: gets the size of combobox 
	*	@type: public
	*	@returns: current size of combobox
	*/
	this.size = function(){
					var j=0;
					for(var i=0;i<this.keys.length;i++){
						if(this.keys[i]!=null)
							j++;
					}
					return j;
				}
	/**
	*	@desc: gets array of all available keys present in combobox
	*	@type: public
	*	@returns: array of all available keys
	*/
	this.getKeys = function(){
					var keyAr = new Array(0);
					for(var i=0;i<this.keys.length;i++){
						if(this.keys[i]!=null)
							keyAr[keyAr.length] = this.keys[i];
					}
					return keyAr;
				}

	/**
	*	@desc: save curent state
	*	@type: public
	*/
	this.save = function(){
                    this._save=new Array();
                    for(var i=0;i<this.keys.length;i++)
				   	    this._save[i]=[this.keys[i],this.values[i]];
				}


	/**
	*	@desc: restore saved state
	*	@type: public
	*/
	this.restore = function(){
                    if (this._save){
                        this.keys[i]=new Array();
                        this.values[i]=new Array();
                        for(var i=0;i<this._save.length;i++){
					       this.keys[i]=this._save[i][0];
                           this.values[i]=this._save[i][1];
                           }
                    }
				}
    return this;
}
function Hashtable(){
    this.keys = new dhtmlxArray();
	this.values = new dhtmlxArray();
    return this;
    }
Hashtable.prototype = new dhtmlXGridComboObject;
//(c)dhtmlx ltd. www.dhtmlx.com

//v.1.5 build 80319

/*
Copyright DHTMLX LTD. http://www.dhtmlx.com
To use this component please contact sales@dhtmlx.com to obtain license
*/

 
 dhtmlXGridObject.prototype.enableSmartRenderingSorting = function(mode){this._srndsort=convertStringToBoolean(mode)};dhtmlXGridObject.prototype.enableSmartRendering = function(mode,totalRows,bufferSize,renderSize){this._dload=convertStringToBoolean(mode)
 
 this.attachEvent("onBeforeSorting",function(){return this._srndsort});if (!this._dload){this.xmlLoader = new dtmlXMLLoaderObject(this.doLoadDetails,window,true,this.no_cashe);window.clearTimeout(this._dLoadTimer);if (this.deleteRow_WSRD){this.deleteRow=this.deleteRow_WSRD;this._insertRowAt=this._insertRowAt_WSRD;this.sortRows=this.sortRows_WSRD;this._initDrF=false};return};if (!this._srdh)this._srdh=20;if (!this.deleteRow_WSRD){this.deleteRow_WSRD=this.deleteRow;this.deleteRow=this.deleteRow_WSRDA;this._insertRowAt_WSRD=this._insertRowAt;this._insertRowAt=this._insertRowAt_WSRDA;this.sortRows_WSRD=this.sortRows;this.sortRows=function(col,type,order){order=(order||"asc").toLowerCase();var self=this;var arrTS=new Array();var temp=this.rowsCol;for (var i=0;i<this.rowsCol.length;i++)if ( this.rowsCol[i] && !this.rowsBuffer[1][i]){this.rowsBuffer[1][i]=this.rowsCol[i];this.rowsBuffer[0][i]=this.rowsCol[i].idd};var a=this.rowsBuffer[1];function getFirstChild(node,col){var z=node.firstChild;while (true){while (z && z.tagName!="cell" && z.tagName!="TD")z=z.nextSibling;col--;if (col<0)return (z.firstChild?z.firstChild.data:"")
 z=z.nextSibling};return ""};for (var i=0;i<a.length;i++){a[i]={obj:a[i], idd:(a[i].idd||a[i].getAttribute("id"))};arrTS[a[i].idd]=getFirstChild(a[i].obj,col)};var z=(new Date()).valueOf();if(type=='cus')a.sort(function(a,b){return self._customSorts[col](arrTS[a.idd],arrTS[b.idd],order,a.idd,b.idd)});else if(type=='str')a.sort(function(a,b){if(order=="asc")return arrTS[a.idd]>arrTS[b.idd]?1:-1;else return arrTS[a.idd]<arrTS[b.idd]?1:-1});else if(type=='int')a.sort(function(a,b){var aVal = parseFloat(arrTS[a.idd]);aVal=isNaN(aVal)?-99999999999999:aVal;var bVal = parseFloat(arrTS[b.idd]);bVal=isNaN(bVal)?-99999999999999:bVal;if(order=="asc")return aVal-bVal;else return bVal-aVal});else if(type=='date')a.sort(function(a,b){var aVal = Date.parse(arrTS[a.idd])||(Date.parse("01/01/1900"));var bVal = Date.parse(arrTS[b.idd])||(Date.parse("01/01/1900"));if(order=="asc")return aVal-bVal;else return bVal-aVal});var z2=(new Date()).valueOf();for (var i=0;i<a.length;i++)a[i]=a[i].obj;for(var i=1;i<this.obj.rows.length;i)this.obj.rows[1].parentNode.removeChild(this.obj.rows[1]);this.rowsCol=new dhtmlxArray();for(var i=0;i<a.length;i++)this.rowsBuffer[0][i]=(a[i].idd||a[i].getAttribute("id"));this._fastAddRowSpacer(0,this.limit*this._srdh);this._askRealRows(0)}};this._dInc=12;this._dl_start=new Array();this._limitC=this.limit=totalRows;this.multiLine=false;this._dloadSize=Math.floor(parseInt(this.entBox.offsetHeight)/this._srdh)+2;this.renderSize=renderSize;this.obj.className+=" row20px";this._dpref=bufferSize||this._dloadSize;if (this.hdr.childNodes[1])this._initD();else
 this._initDrF=true};dhtmlXGridObject.prototype.getStateOfView = function(){if (!this._srdh)this._srdh=20;return [
 Math.floor(this.objBox.scrollTop/this._srdh),
 Math.ceil(parseInt(this.objBox.offsetHeight)/this._srdh),
 this.limit
 ]};dhtmlXGridObject.prototype.setAwaitedRowHeight = function(height) {this._srdh=parseInt(height)};dhtmlXGridObject.prototype._initD = function(){if (this.limit)this._fastAddRowSpacer(0,this.limit*this._srdh);this._initDrF=false};dhtmlXGridObject.prototype.enableDOMLimit = function(mode,limit){if (!convertStringToBoolean(mode)) return;this._dom_limit=limit||1000};dhtmlXGridObject.prototype._checkPref=function(start, direction){if (_isKHTML)return start;var i=1;for (i;i<=this._dpref;i++)if (((i*direction+start)<0)||((i*direction+start)>this.limit)||(this.rowsCol[i*direction+start])||(this.rowsBuffer[i*direction+start])) break;return start+(i-1)*direction};dhtmlXGridObject.prototype._simplifyDom=function(a,b,c){var count=0;for (var i=0;i<this.obj._rowslength();i++)
 if ((i<a)||(i>b)){var z=this.obj._rows(i);if ((!z._rLoad)&&(!z._sRow)){if ((z.previousSibling)&&((z.previousSibling._sRow)||(z.previousSibling._rLoad)))
 {var ind=this.rowsCol._dhx_find(z);var zprev=z.previousSibling;this.rowsBuffer[1][ind]=z;zprev._sRow=true;z.parentNode.removeChild(z);this.rowsAr[this.rowsCol[ind].idd]=null;this.rowsCol[ind]=null;this._fixHeight(zprev,-this._srdh);i--}else
 if ((z.nextSibling)&&((z.nextSibling._sRow)||(z.nextSibling._rLoad)))
 {var ind=this.rowsCol._dhx_find(z);var zprev=z.nextSibling;this.rowsBuffer[1][ind]=z;zprev._sRow=true;z.parentNode.removeChild(z);this.rowsAr[this.rowsCol[ind].idd]=null;this.rowsCol[ind]=null;this._fixHeight(zprev,-this._srdh);i--}else{var ind=this.rowsCol._dhx_find(z);this.rowsBuffer[1][ind]=z;var id='temp_dLoad_'+this._dInc;this._dInc++;var zn=this._fastAddRow(id,ind,true,ind);z.parentNode.removeChild(z);zn._sRow=true;this._fixHeight(zn,0)};if (this.obj._rowslength()<=this._dom_limit) return}}};dhtmlXGridObject.prototype._addFromBufferSR=function(j){if ((!this.rowsCol[j])||(this.rowsCol[j]._sRow))
 this._splitRowAt(j);else
 if ((this.rowsBuffer[1][j])&&(this.rowsBuffer[1][j].tagName=="TR")){this.rowsCol[j].parentNode.insertBefore(this.rowsBuffer[1][j],this.rowsCol[j]);this.rowsCol[j].parentNode.removeChild(this.rowsCol[j]);this.rowsCol[j].grid=null;this.rowsCol[j]=this.rowsBuffer[1][j]};if (this._cssEven){if (j%2==1)this.rowsCol[j].className=this._cssUnEven;else this.rowsCol[j].className=this._cssEven};if (this.rowsBuffer[1][j].tagName=="row"){this.changeRowId(this.rowsCol[j].idd,this.rowsBuffer[1][j].getAttribute("id"));this._fillRowFromXML(this.rowsCol[j],this.rowsBuffer[1][j],-1)}else {this.rowsAr[this.rowsBuffer[1][j].idd]=this.rowsBuffer[1][j];this.rowsBuffer[1][j]._sRow=this.rowsBuffer[1][j]._rLoad=false};this.rowsCol[j]._rLoad=false;this.rowsBuffer[1][j]=null};dhtmlXGridObject.prototype._askRealRows=function(pos,afterCall){if ((this.renderSize)&&(this.renderSize>this._dloadSize))
 var cdload=this.renderSize;else
 var cdload=this._dloadSize;if (!this.limit){this._dl_start[0]=[0,cdload];this.xmlLoader = new dtmlXMLLoaderObject(this._askRealRows2,this);this.xmlLoader.waitCall=afterCall;this.callEvent("onXLS",[this]);if (this._dloadStr)this.xmlLoader.loadXMLString(this._dloadStr);else
 this.xmlLoader.loadXML(this._dload+((this._dload.indexOf("?")!=-1)?"&":"?")+"posStart="+0);return true};var gi=pos||Math.floor(this.objBox.scrollTop/this._srdh);if ((this._dom_limit)&&(this.obj._rowslength()>this._dom_limit))
 {this._simplifyDom(gi,gi+cdload)};if (gi>(this.limit-cdload)) gi=this.limit-cdload;if (gi<0)gi=0;var size=gi+cdload;if (size>this.limit)size=this.limit;for (var j=gi;j<size;j++)if ((!this.rowsCol[j])||(this.rowsCol[j]._rLoad)||(this.rowsCol[j]._sRow)) {if (this.rowsBuffer[1][j]){this._addFromBufferSR(j)}else
 {if (this._dpref){start=this._checkPref(j,-1);count=this._checkPref(size,1)-start}else


 {count=size-j;start=j};this._dl_start[start]=[gi-start,size-gi];this.xmlLoader = new dtmlXMLLoaderObject(this._askRealRows2,this);this.xmlLoader.waitCall=afterCall;this.callEvent("onXLS",[this]);this.xmlLoader.loadXML(this._dload+((this._dload.indexOf("?")!=-1)?"&":"?")+"posStart="+start+"&count="+count+"&sn="+(new Date().getDay()).valueOf());return}};if (afterCall)afterCall()};dhtmlXGridObject.prototype._askRealRows2=function(obj,xml,c,d,e){if (obj._refresh_mode)return obj._refreshFromXML(e);var top=e.getXMLTopNode("rows");var inmd=obj._initDrF;if (inmd){obj._parseHead(top);if (obj.limit && obj.rowsCol.length)inmd=false};var rows=e.doXPath("//rows/row",top);var z_t=top.getAttribute("total_count");if ((z_t)&&(!obj._limitC)){obj._limitC=obj.limit=parseInt(z_t)};if (inmd)obj._initD();var j=parseInt(top.getAttribute("pos"))||0;var llim=(obj._dl_start[j]||[0])[0];var tlim=llim+(obj._dl_start[j]||[0,rows.length])[1];if (!obj.limit){obj.limit=rows.length;obj._fastAddRowSpacer(0,obj.limit*obj._srdh);if (obj._ahgr)window.setTimeout(function(){obj._askRealRows()},1)};for (var i=0;i<rows.length;i++){if ((i<llim)||(i>tlim))
 {obj.rowsBuffer[1][j+i]=rows[i];obj.rowsBuffer[0][j+i]=rows[i].getAttribute("id")}else


 {obj.rowsBuffer[0][j+i]=null;if ((!obj.rowsCol[i+j])||(obj.rowsCol[i+j]._sRow))
 obj._splitRowAt(i+j);if (obj.rowsCol[i+j]._rLoad){if (obj._cssEven){if ((j+i)%2==1) obj.rowsCol[i+j].className=obj._cssUnEven;else obj.rowsCol[i+j].className=obj._cssEven;if (obj._fake)obj._fake.rowsCol[i+j].className=obj.rowsCol[i+j].className};obj.changeRowId(obj.rowsCol[i+j].idd,rows[i].getAttribute("id"));obj._fillRowFromXML(obj.rowsCol[i+j],rows[i],-1);obj.rowsCol[i+j]._rLoad=false;if (obj._fake)obj._fake.rowsCol[i+j]._rLoad=false}}};obj.callEvent("onXLE",[this,tlim-llim])};dhtmlXGridObject.prototype._splitRowAt=function(ind){var id='temp_dLoad_'+this._dInc;this._dInc++;var z=this.rowsCol[ind];if (!z){var ind2=this._findSParent(ind);var delta=ind2[1]-(ind-ind2[0])*this._srdh;this._fixHeight(this.rowsCol[ind2[0]],delta);var z2=this._fastAddRow(id,ind,true,ind2[0])
 z2._sRow=true;this._fixHeight(z2,-1*((ind2[1]-(ind-ind2[0])*this._srdh)-this._srdh));return this._splitRowAt(ind)}else
 if (z._sRow){if ((this.rowsBuffer[1][ind])&&(this.rowsBuffer[1][ind].tagName=="TR"))
 (this._fastAddRow(id,ind,true,null,this.rowsBuffer[1][ind]))._rLoad=false;else
 (this._fastAddRow(id,ind,true))._rLoad=true;if ((!z.style.height)||(parseInt(z.style.height)==this._srdh))
 z.parentNode.removeChild(z);else{this.rowsCol[ind+1]=z;this._fixHeight(z,this._srdh)};if (ind==0)this.setSizes()}};dhtmlXGridObject.prototype._findSParent=function(ind){for (var i=ind-1;i>=0;i--){if (this.rowsCol[i]){return [i,(parseFloat(this.rowsCol[i].style.height))]}}};dhtmlXGridObject.prototype._fixHeight=function(z,delta){var x=parseFloat(z.style.height||this._srdh)-delta;if (x==this._srdh){z._sRow=false;z._rLoad=true};z.style.height=x+"px";var n=z.childNodes.length;for (var i=0;i<n;i++)z.childNodes[i].style.height=x+"px"};dhtmlXGridObject.prototype._fastAddRowSpacer=function(ind,height){var id='temp_dLoad_'+this._dInc;this._dInc++;var z=this._fastAddRow(id,ind);z.style.height=height+"px";var n=z.childNodes.length;for (var i=0;i<n;i++)z.childNodes[i].style.height=height+"px";z._sRow=true};dhtmlXGridObject.prototype._fastAddRow=function(id,ind,nonshift,ind2,z){var z=z||this._prepareRow(id);if (((ind2)||(ind2=="0"))&&(this.rowsCol[ind2].nextSibling))
 this.rowsCol[ind2].parentNode.insertBefore(z,this.rowsCol[ind2].nextSibling);else
 {if ((ind==this.limit)||(this.obj._rowslength()==0)||(!this.rowsCol[ind])){if (_isKHTML)this.obj.appendChild(z);else{if (!this.obj.firstChild)this.obj.appendChild(document.createElement("TBODY"));this.obj.childNodes[0].appendChild(z)}}else
 this.rowsCol[ind2||ind].parentNode.insertBefore(z,this.rowsCol[ind])};this.rowsAr[id] = z;if (!nonshift)this.rowsCol._dhx_insertAt(ind,z);else
 this.rowsCol[ind]=z;return z};dhtmlXGridObject.prototype._insertRowAt_WSRDA = function(r,ind,skip){if (ind<0 || typeof ind == undefined)ind=this.rowsBuffer[0].length;if ((arguments.length<2)||(ind===window.undefined))
 ind = this.rowsBuffer[0].length
 else{if(ind>this.rowsBuffer[0].length)ind = this.rowsBuffer[0].length};var ind2=this.rowsBuffer[0][ind]||(this.rowsCol[ind]?this.rowsCol[ind].idd:null);if (ind2)this.getRowById(ind2);if (!skip)if (ind==this.rowsBuffer[0].length){if (_isKHTML)this.obj.appendChild(r);else{this.obj.firstChild.appendChild(r)};this.rowsBuffer[0][ind]=r.idd;this.rowsBuffer[1][ind]=null;ind2=ind}else
 {if (!this.rowsCol[ind])ind2=(this._findSParent(ind)[0]);else ind2=ind;this.rowsCol[ind2].parentNode.insertBefore(r,this.rowsCol[ind2]);this.rowsBuffer[0]._dhx_insertAt(ind,r.idd);this.rowsBuffer[1]._dhx_insertAt(ind,null)};this.limit+=1;this.rowsAr[r.idd] = r;this.rowsCol._dhx_insertAt(ind2,r);if (this._cssEven){if (ind%2==1)r.className+=" "+this._cssUnEven;else r.className+=" "+this._cssEven;if (ind!=(this.rowsCol.length-1))
 this._fixAlterCss(ind+1)};this.doOnRowAdded(r);if ((this.math_req)&&(!this._parsing_)){for(var i=0;i<this.hdr.rows[0].cells.length;i++)this._checkSCL(r.childNodes[i]);this.math_req=false};return r};dhtmlXGridObject.prototype.deleteRow_WSRDA = function(row_id,node){var ind=-1;var fixind=null;if (this.rowsAr[row_id]){ind=this.rowsCol._dhx_find(this.rowsAr[row_id]);if (this.deleteRow_WSRD(row_id,node)==false) return false};if (ind<0){var ind=this.rowsBuffer[0]._dhx_find(row_id);if (ind>-1)fixind=this.rowsCol[this._findSParent(ind)[0]]};if (ind>-1){this.rowsBuffer[0]._dhx_delAt(ind);this.rowsBuffer[1]._dhx_delAt(ind);this.limit-=1;if (fixind)this._fixHeight(fixind,this._srdh)};return true};//(c)dhtmlx ltd. www.dhtmlx.com
//v.1.5 build 80319

/*
Copyright DHTMLX LTD. http://www.dhtmlx.com
To use this component please contact sales@dhtmlx.com to obtain license
*/

//please beware that function started from _in_header_ must not be obfuscated

function sortNumber(a, b)
{
return (a - b)*-1;
}

/**
*   @desc: filter grid by mask
*   @type: public
*   @param: column - {number} zero based index of column
*   @param: value - {string} filtering mask
*   @param: preserve - {bool} filter current or initial state ( false by default )
*	@edition: Professional
*   @topic: 0
*/
dhtmlXGridObject.prototype.filterBy=function(column, value, preserve){
		if (this.pagingOn) return this.filterByPaging(column, value, preserve);
		if (this._dload) return this.filterBySRND(column, value, preserve);
		if (this._srowsCol){
			if (!preserve)
				this.rowsCol=dhtmlxArray([].concat(this._srowsCol));
		} else
			this._srowsCol=[].concat(this.rowsCol);	//backup copy
	
		
	if (!this.rowsCol.length) return;
	var d=true;

	if (typeof(column)=="object")
		for (var j=0; j<value.length; j++)
			this._filterA(column[j],value[j]);
	else
			this._filterA(column,value);
	
	//redraw grid
	
	var p=this.obj.parentNode; 
//	p.removeChild(this.obj); //operation outside DOM is much faster
	var pp=this.obj.rows[0].parentNode; //safari way
	if (_isKHTML) pp=pp.parentNode;
	for (var i=this.obj.rows.length-1; i>0; i--)
		pp.removeChild(this.obj.rows[i]);
		
	for (var i=0; i<this.rowsCol.length; i++)		
		pp.appendChild(this.rowsCol[i]);
		
	p.appendChild(this.obj); //put back in DOM	
	this.callEvent("onGridReconstructed",[])
}
dhtmlXGridObject.prototype._filterA=function(column,value){ 
	if (value=="") return;
	var d=true;
	if (typeof(value)=="function") d=false;
	else value=(value||"").toString().toLowerCase();
	if (!this.rowsCol.length) return;
	var start=0; 	while(this.rowsCol[start] && this.rowsCol[start]._cntr) start++;
	var e=this.cells3(this.rowsCol[start],column);
	//caching exCell, as result we lost collspans 
	
	for (var i=this.rowsCol.length-1; i>=0; i--){
		if (this.rowsCol[i]._cntr) continue;
		e.cell=this.rowsCol[i].childNodes[column];
		if (d?(e.getValue().toString().toLowerCase().indexOf(value)==-1):(!value(e.getValue()))){
			this.rowsCol.splice(i,1);//filter row
		}
		
	}
}
dhtmlXGridObject.prototype.filterBySRND=function(column, value, preserve){
		for (var i=0; i<this.rowsCol.length; i++)
    	if ( this.rowsCol[i] && !this.rowsBuffer[1][i]) {
        	this.rowsBuffer[1][i]=this.rowsCol[i];
            this.rowsBuffer[0][i]=this.rowsCol[i].idd;
        }
	this._filterBy_core(column, value, preserve);
	//redraw grid
	for(var i=this.rowsCol.length-1;i>=0;i--)
		if (this.rowsCol[i])
    		this.rowsCol[i].parentNode.removeChild(this.rowsCol[i]);
	this.rowsCol=new dhtmlxArray();
	this.limit=this.rowsBuffer[0].length;
    this._fastAddRowSpacer(0,this.limit*this._srdh);      
    if (this.limit)
    	this._askRealRows(0);
}
dhtmlXGridObject.prototype._filterBy_core=function(column, value, preserve){
	if (this._srowsBuf){
			if (!preserve){
				this.rowsBuffer=[dhtmlxArray([].concat(this._srowsBuf[0])),dhtmlxArray([].concat(this._srowsBuf[1]))];
			}
		} else{
			this._srowsBuf=[[].concat(this.rowsBuffer[0]),[].concat(this.rowsBuffer[1])];
		}
	
	if (!this.rowsBuffer[0].length) return;
	
		if (typeof(column)=="object" )
		for (var j=0; j<value.length; j++)
				this._filterB(column[j],value[j]);
		else	this._filterB(column,value);
}
dhtmlXGridObject.prototype._filterB=function(column,value){
	if (value=="") return;
	
	var d=true;
	if (typeof(value)=="function") d=false;
	else value=(value||"").toString().toLowerCase();
	
	var e=null;
	//caching exCell, as result we lost collspans 
	for (var i=this.rowsBuffer[0].length-1; i>=0; i--){
		if (this.rowsBuffer[1][i].idd){
			if (!e) e=this.cells3(this.rowsBuffer[1][i],column);
			e.cell=this.rowsBuffer[1][i].childNodes[column];
			var val=e.getValue();
		} else {
			var val=this.rowsBuffer[1][i].getElementsByTagName("cell")[this._m_order?this._m_order[column]:column];
			if (val && val.firstChild) val=val.firstChild.data;
			else val="";
		}
		if (d?(val.toString().toLowerCase().indexOf(value)==-1):(!value(val))){
			this.rowsBuffer[0].splice(i,1);//filter row
			this.rowsBuffer[1].splice(i,1);
		}
		
	}
}
dhtmlXGridObject.prototype.filterByPaging=function(column, value, preserve){
		var a0=[]; var a1=[];
		for (var i=0; i<this.rowsCol.length; i++){
			a0.push(this.rowsCol[i].idd);
			a1.push(this.rowsCol[i]);
		}
    	this.rowsBuffer[1]=dhtmlxArray(a1.concat(this.rowsBuffer[1]));
        this.rowsBuffer[0]=dhtmlxArray(a0.concat(this.rowsBuffer[0]));
        
		this._filterBy_core(column, value, preserve);
		
	//redraw grid

	for(var i=this.rowsCol.length-1;i>=0;i--)
		if (this.rowsCol[i].parentNode)
    		this.rowsCol[i].parentNode.removeChild(this.rowsCol[i]);
	this.rowsCol=new dhtmlxArray();
	this.limit=this.rowsBuffer[0].length;
	this.changePage(1);
}

/**
*   @desc: get all possible values in column
*   @type: public
*   @param: column - {number} zero based index of column
*   @returns: {array} array of all possible values in column
*	@edition: Professional
*   @topic: 0
*/
dhtmlXGridObject.prototype.collectValues=function(column){
	var c={}; var f=[];
	this._build_m_order();		
	var col=this.rowsCol;	/*updated*/
	var start=0; 	while(col[start] && col[start]._cntr) start++;
	if (!col[start]) return [];
	var e=this.cells3(col[start],column);
	for (var i=start; i<col.length; i++){
		if (!col[i] || col[i]._cntr) continue;
		e.cell=col[i].childNodes[this._m_order?this._m_order[column]:column];
		var val=e.getValue();
		if (val) c[val]=true;
	}
	
	var col=this.rowsBuffer;  /*updated*/
	for (var i=col[0].length-1; i>=0; i--){
		if (!col[1][i]) continue;
		if (col[1][i].idd){
			e.cell=col[1][i].childNodes[this._m_order?this._m_order[column]:column];
			var val=e.getValue();
			if (val) c[val]=true;
		} else {
			var val=col[1][i].getElementsByTagName("cell")[column];
			if (val && val.firstChild){
				val=val.firstChild.data;
				if (val) c[val]=true;
			}
		}
	}
	
	var vals=this.combos[this._m_order?this._m_order[column]:column];
	for (d in c) 
		if (c[d]===true) f.push(vals?(vals.get(d)||d):d);
	
	items=f.join();
	alphaExpression=/[a-z,]+/gi;
	mytest=alphaExpression.exec(items);
	if(mytest==","){
		return f.sort(sortNumber);
	}else{
		return f.sort();
	}		
	
}

dhtmlXGridObject.prototype._build_m_order=function(){
	if (this._c_order){
		this._m_order=[]
		for (var i=0; i < this._c_order.length; i++) {
			this._m_order[this._c_order[i]]=i;
		};
	}
}
dhtmlXGridObject.prototype.filterByAll=function(){
	var a=[];
	var b=[];
	this._build_m_order();

	for (var i=0; i<this.filters.length; i++){
		
		var ind=this._m_order?this._m_order[this.filters[i][1]]:this.filters[i][1];
		b.push(ind);
		
		
		var val=this.filters[i][0]._filter?this.filters[i][0]._filter():this.filters[i][0].value;
		var vals;
		if (typeof val != "function" && (vals=this.combos[ind])){
			ind=vals.values._dhx_find(val);
			val=(ind==-1)?val:vals.keys[ind];
		}
		a.push(val);
		
	}
	this.filterBy(b,a);
	if (this._cssEven) this._fixAlterCss();
	this.callEvent("onFilterEnd",[this.filters]);
}

/**
*   @desc: create a filter from any input element (text filter), select (dropdown) or DIV (combobox based on dhtmlxCombo)
*   @type: public
*   @param: id - {string|object} input id or input html object
*   @param: column - {number} index of column
*   @param: preserve - {bool} filter current state or initial one ( false by default )
*	@edition: Professional
*   @topic: 0
*/
dhtmlXGridObject.prototype.makeFilter=function(id,column,preserve){
	if (!this.filters) this.filters=[];
	if (typeof(id)!="object")
		id=document.getElementById(id);
	if(!id) return;
	var self=this;
	
	if (!id.style.width) id.style.width = "90%";
		
	if (id.tagName=='SELECT'){
		this.filters.push([id,column]);
		this._loadSelectOptins(id,column);
		id.onchange=function(){
			self.filterByAll();
		}
		if(_isIE)
			id.style.marginTop="1px";
			
		this.attachEvent("onEditCell",function(stage,a,ind){ 
			this._build_m_order();
			if (stage==2 && this.filters && ( this._m_order?(ind==this._m_order[column]):(ind==column) ))
				this._loadSelectOptins(id,column);
			return true;
		});
	} 
	else if (id.tagName=='INPUT'){
		this.filters.push([id,column]);
		id.value='';
		id.onkeydown=function(){
			if (this._timer) window.clearTimeout(this._timer);
			this._timer=window.setTimeout(function(){
				self.filterByAll();
			},500);
		};
	}
	else if (id.tagName=='DIV' && id.className=="combo"){
		this.filters.push([id,column]);
		id.style.padding="0px";id.style.margin="0px";
		if (!window.dhx_globalImgPath) window.dhx_globalImgPath=this.imgURL;
		var z=new dhtmlXCombo(id,"_filter","90%");
		z.enableFilteringMode(true);
		id.combo=z;
		id.value="";
		
		this._loadComboOptins(id,column);
		z.attachEvent("onChange",function(){
			id.value=z.getSelectedValue();
			self.filterByAll();
		});
	}
	if (id.parentNode)
		id.parentNode.className+=" filter";
}

/**
*   @desc: create a search box (set selection to the row with found value) from any input
*   @type: public
*   @param: id - {string|object} input id or input html object
*   @param: column - {number} index of column
*	@edition: Professional
*   @topic: 0
*/
dhtmlXGridObject.prototype.makeSearch=function(id,column){
	if (typeof(id)!="object")
		id=document.getElementById(id);
	if(!id) return;
	var self=this;
		
	if (id.tagName=='INPUT'){
		id.onkeypress=function(){
			if (this._timer) window.clearTimeout(this._timer);
			this._timer=window.setTimeout(function(){
				if (id.value=="") return;
				var z=self.findCell(id.value,column);
				if (z.length)
					self.selectRow(z[0][0])
			},500);
		};
	}
	if (id.parentNode)
		id.parentNode.className+=" filter";
}
	
dhtmlXGridObject.prototype._loadSelectOptins=function(t,c){ 
		var l=this.collectValues(c);
		t.innerHTML="";
		t.options[0]=new Option("","");
		var f=this._filter_tr?this._filter_tr[c]:null;
		for (var i=0; i<l.length; i++)
			t.options[t.options.length]=new Option(f?f(l[i]):l[i],l[i]);
}
dhtmlXGridObject.prototype.setSelectFilterLabel=function(ind,fun){ 
		if (!this._filter_tr) this._filter_tr=[];
		this._filter_tr[ind]=fun;
}

dhtmlXGridObject.prototype._loadComboOptins=function(t,c){ 
	var l=this.collectValues(c);
		t.combo.clearAll();
		t.combo.render(false);
		t.combo.addOption("","");
		for (var i=0; i<l.length; i++)
			t.combo.addOption(l[i],l[i]);
		t.combo.render(true);
}

/**
*   @desc: refresh filtering ( can be used if data in grid changed and filters need to be updated )
*   @type: public
*	@edition: Professional
*   @topic: 0
*/
dhtmlXGridObject.prototype.refreshFilters=function(){
	for (var i=0; i<this.filters.length; i++){
		switch(this.filters[i][0].tagName.toLowerCase()){
			case "input":
				break;
			case "select":
				this._loadSelectOptins.apply(this,this.filters[i]);
				break;
			case "div":
				this._loadComboOptins.apply(this,this.filters[i]);
				break;
		}
	}
}

dhtmlXGridObject.prototype._filters_ready=function(fl,code){
	this.attachEvent("onXLE",this.refreshFilters);
	this.attachEvent("onClearAll",function(){ 
		this.callEvent("onGridReconstructed",[])
		this._srowsCol=null; 
		this._srowsBuf=null;
		if (!this.obj.rows.length)
			this.filters=[];
	});
	this._filters_ready=function(){};
}

dhtmlXGridObject.prototype._in_header_text_filter=function(t,i){
	t.innerHTML="<input type='text' style='width:90%; font-size:8pt; font-family:Tahoma; -moz-user-select:text; '>";
	t.onclick=t.onmousedown = function(e){ (e||event).cancelBubble=true; return true; }
	t.onselectstart=function(){ return (event.cancelBubble=true); }
	this.makeFilter(t.firstChild,i);
	this._filters_ready();
}

dhtmlXGridObject.prototype._in_header_select_filter=function(t,i){
	t.innerHTML="<select style='width:90%; font-size:8pt; font-family:Tahoma;'></select>";
	t.onclick=function(e){ (e||event).cancelBubble=true; return false; }
	this.makeFilter(t.firstChild,i);
	this._filters_ready();
}

dhtmlXGridObject.prototype._in_header_combo_filter=function(t,i){
	t.innerHTML="<div style='width:100%; padding-left:2px; overflow:hidden; font-size:8pt; font-family:Tahoma; -moz-user-select:text;' class='combo'></div>";
	t.onselectstart=function(){ return (event.cancelBubble=true); }
	t.onclick=function(e){ (e||event).cancelBubble=true; return false; }
	this.makeFilter(t.firstChild,i);
	this._filters_ready();
}

dhtmlXGridObject.prototype._in_header_text_search=function(t,i){
	t.innerHTML="<input type='text' style='width:90%; font-size:8pt; font-family:Tahoma; -moz-user-select:text;'>";
	t.onclick= t.onmousedown = function(e){ (e||event).cancelBubble=true; return true; }
	t.onselectstart=function(){ return (event.cancelBubble=true); }
	this.makeSearch(t.firstChild,i);
}

dhtmlXGridObject.prototype._in_header_numeric_filter=function(t,i){
	this._in_header_text_filter.call(this,t,i);
	t.firstChild._filter=function(){
		var v=this.value;
		
		var r; var op="=="; var num=parseFloat(v.replace("=","")); var num2=null;
		
		if (v){
			if (v.indexOf("..")!=-1){
				v=v.split("..");
				num=parseFloat(v[0]);
				num2=parseFloat(v[1]);
				return function(v){
					if (v>=num && v<=num2) return true;
					return false;
					}
			}
			r=v.match(/>|>=|<=|</)
			if (r) {
				op=r[0];
				num=parseFloat(v.replace(op,""));
			}
			return Function("v"," if (v "+op+" "+num+" ) return true; return false;");
		}
	};
}

dhtmlXGridObject.prototype._in_header_master_checkbox=function(t,i){
	t.innerHTML="<input type='checkbox' />";
	var self=this;
	t.firstChild.onclick=function(){
		var val=this.checked?1:0;
		self.forEachRow(function(id){
			this.cells(id,i).setValue(val);
		});
	}
}

dhtmlXGridObject.prototype._in_header_stat_total=function(t,i,c){
	var calck=function(){
		var summ=0;
		for (var j=0; j<this.rowsCol.length; j++){
			var v=parseFloat(this.cells2(j,i).getValue());
			summ+=isNaN(v)?0:v;
		}
		
		return this._maskArr[i]?this._aplNF(summ,i):(Math.round(summ*100)/100);
	}
	this._stat_in_header(t,calck,i,c,c);
}
dhtmlXGridObject.prototype._in_header_stat_multi_total=function(t,i,c){
	var cols=c[1].split(":"); c[1]="";
	cols[0]=parseInt(cols[0]);
	cols[1]=parseInt(cols[1]);
	var calck=function(){
		var summ=0;
		for (var j=0; j<this.rowsCol.length; j++){
			var v=parseFloat(this.cells2(j,parseInt(cols[0])).getValue())*parseFloat(this.cells2(j,parseInt(cols[1])).getValue());
			summ+=isNaN(v)?0:v;
		}
		return this._maskArr[i]?this._aplNF(summ,i):(Math.round(summ*100)/100);
	}
	var track=[]; track[cols[0]]=true; track[cols[1]]=true;
	this._stat_in_header(t,calck,track,c,c);
}
dhtmlXGridObject.prototype._in_header_stat_max=function(t,i,c){
	var calck=function(){
		var summ=-999999999;
		if (this.getRowsNum()==0) return "";
		for (var j=0; j<this.rowsCol.length; j++)
			summ=Math.max(summ,parseFloat(this.cells2(j,i).getValue()));
		
		return this._maskArr[i]?this._aplNF(summ,i):summ;
	}
	this._stat_in_header(t,calck,i,c);
}
dhtmlXGridObject.prototype._in_header_stat_min=function(t,i,c){
	var calck=function(){
		var summ=999999999;
		if (this.getRowsNum()==0) return "";
		for (var j=0; j<this.rowsCol.length; j++)
			summ=Math.min(summ,parseFloat(this.cells2(j,i).getValue()));
		return this._maskArr[i]?this._aplNF(summ,i):summ;
	}
	this._stat_in_header(t,calck,i,c);
}
dhtmlXGridObject.prototype._in_header_stat_average=function(t,i,c){
	var calck=function(){
		var summ=0; var count=0;
		for (var j=0; j<this.rowsCol.length; j++){
			var v=parseFloat(this.cells2(j,i).getValue());
			summ+=isNaN(v)?0:v;
			count++;
		}
		return this._maskArr[i]?this._aplNF(summ,i):(Math.round(summ/count*100)/100);
	}
	this._stat_in_header(t,calck,i,c);
}
dhtmlXGridObject.prototype._in_header_stat_count=function(t,i,c){
	var calck=function(){
		return this.getRowsNum();
	}
	this._stat_in_header(t,calck,i,c);
}

dhtmlXGridObject.prototype._stat_in_header=function(t,calck,i,c){
	var that=this;
	var f=function(){
		this.dma(true)
		t.innerHTML=(c[0]?c[0]:"")+calck.call(this)+(c[1]?c[1]:"");
		this.dma(false)
		this.callEvent("onStatReady",[])
	}
	if (!this._stat_events) {
		this._stat_events=[];
		this.attachEvent("onClearAll",function(){ 
			if (!this.hdr.rows[1]){
				for (var i=0; i<this._stat_events.length; i++)
					for (var j=0; j < 4; j++) 
						this.detachEvent(this._stat_events[i][j]);
				this._stat_events=[];	
			}
		})
	}
	
	this._stat_events.push([
	this.attachEvent("onGridReconstructed",f),
	this.attachEvent("onXLE",f),
	this.attachEvent("onFilterEnd",f),
	this.attachEvent("onEditCell",function(stage,id,ind){
		if (stage==2 && ( ind==i || ( i && i[ind]) ) ) f.call(this);
		return true;
		})]);
	t.innerHTML="";
}



//(c)dhtmlx ltd. www.dhtmlx.com

function toggleit(obj){
	//var obj = $('loginContainer');
	if(obj.style.display == 'none'){ 
		//obj.style.display = 'inline';
		Effect.SlideDown(obj.id, { duration: .25, scaleFrom: 3, scaleTo: 100});
		 //try { dofocus('UserName'); } catch (e) { /**/};
	}else{
		Effect.SlideUp(obj.id, { duration: 0.6, scaleFrom: 100, scaleTo: 3});
	}
}
var t
function hb(){
	var kkdate = new Date(); mskk=kkdate.getTime();
	new Ajax.Request('hb.php',{parameters:{dvo: mskk }});
	t=setTimeout("hb()",120000);
}
hb();
