/****** event-selectors.js ******/
// EventSelectors
// Copyright (c) 2005-2006 Justin Palmer (http://encytemedia.com)
// Examples and documentation (http://encytemedia.com/event-selectors)

var EventSelectors = {
  version: '1.0_pre',
  cache: [],

  start: function(rules) {
    this.rules = rules || {};
    this.timer = new Array();
    this._extendRules();
    this.assign(this.rules);
  },

  assign: function(rules) {
    var observer = null;
    var start, end;
    this._unloadCache();
    for (selectorsString in rules) {
        start = new Date();
            (function(rule) {
              var selectors = $A(rule.key.split(','));
              selectors.each(function(selector) {
                var pair = selector.split(':');
                var event = pair[1];
                $$(pair[0]).each(function(element) {
                  if(pair[1] == '' || pair.length == 1) return rule.value(element);
                  if(event.toLowerCase() == 'loaded') {
                    this.timer[pair[0]] = setInterval(this._checkLoaded.bind(this, element, pair[0], rule), 15);
                  } else {
                    observer = function(event) {
                      var element = Event.element(event);
                      if (element.nodeType == 3) // Safari Bug (Fixed in Webkit)
                                    element = element.parentNode;
                      rule.value($(element), event);
                    }
                    this.cache.push([element, event, observer]);
                    Event.observe(element, event, observer);
                  }
                }.bind(this));
              }.bind(this));
            }.bind(this))({key: selectorsString, value: rules[selectorsString]})
            end = new Date();
            this.log(selectorsString, (end - start) + ' ms');
          };
  },

  // Scoped caches would rock.
  _unloadCache: function() {
    if (!this.cache) return;
    for (var i = 0; i < this.cache.length; i++) {
      Event.stopObserving.apply(this, this.cache[i]);
      this.cache[i][0] = null;
    }
    this.cache = [];
  },

  _checkLoaded: function(element, timer, rule) {
    var node = $(element);
    if(element.tagName != 'undefined') {
      clearInterval(this.timer[timer]);
      rule.value(node);
    }
  },

  _extendRules: function() {
    Object.extend(this.rules, {
     _each: function(iterator) {
       for (key in this) {
         if(key == '_each') continue;
         var value = this[key];
         var pair = [key, value];
         pair.key = key;
         pair.value = value;
         iterator(pair);
       }
     }
    });
  },

  log: function(title, message) {
      if (!window.Settings || !Settings.jsDebug) return;
      setTimeout(function() {
          if (window.console && console.log) {
              console.log(title + ': ' + message);
          } else if (window.opera && opera.postError) {
              opera.postError(title + ': ' + message);
          } else if (window.hackerConsole && hackerConsole.out) {
              hackerConsole.out(message, '', title);
          }
      }, 100);
  }
}

// Remove/Comment this if you do not wish to reapply Rules automatically
// on Ajax request.
/*Ajax.Responders.register({
  onComplete: function() { EventSelectors.assign(Rules);}
})*/






/****** doLoad.js ******/
DIC = {'loading_option': 'Èä¸ò çàãðóçêà...'}
function DependingSelect( select, params ) {
        var th = this;

        var invitingOptionValue = 'null'; // TODO: FIX ME
        var otherOptionValue = ''; // TODO: FIX ME

        this.select = $( select );

        // flag if this select is multiple-choice
        this.multiple = params.multiple;
        this.command = params.command;

        // save handle to field "other"
    this.otherFields = params.otherField;
    if (this.otherFields) {
        if (!(this.otherFields instanceof Array)) {
            this.otherFields = [ this.otherFields ];
        }
        for (var i = 0; i < this.otherFields.length; i++) {
            this.otherFields[i] = $(this.otherFields[i]);
            this.otherFields[i].origValue = this.otherFields[i].value;
            if (params.otherFieldDefault && params.otherFieldDefault[i] != null) {
                setDefaultValueForInput(params.otherFieldDefault[i], this.otherFields[i]);
            }
        }
    }
    if (params.otherHandler) {
        this.otherHandler = params.otherHandler;
    }

        // cache of this select's options: id => "Option object"
        this.id2option = {};

        // relation "parent value" => array of this select's corresponding values
        this.parentCache = {};

        if ( params.otherWord ) {
            th.otherOption = new Option( params.otherWord, otherOptionValue );
            th.otherOption.innerHTML = params.otherWord;
        }

    // this is patch for education. Schools list has different values depending on degree selected.
    this.setCommand = function( newCommand ) {
        th.command = newCommand;

        var parentValue = th.parentSelect.getValue();
        // parent select is single-choice in education, so erasing cache is easy
        if (parentValue[0] == 'value') {
            th.parentCache[parentValue[1]] = null;
            th.parentChoseValue(parentValue[1]);
        }
    }

        // CHILD METHODS

        // stop current loading of new data
        this.stopLoading = function() {
                if ( th.req ) {
                        th.req.onreadystatechange = null;
                        th.req = null;
                }
        }

        // set parent select
        this.bindToParent = function( parentSelect ) {
                // save parent select
                th.parentSelect = parentSelect;

                // bind parent select to myself
                parentSelect.bindToChild( th );

                var parentValue = th.parentSelect.getValue();

                // check what's value of parent
                switch ( parentValue[0] ) {
                // parent's value is some real value or values
                case "value":
                        var value = parentValue[1];

                        // if parent has only one value selected
                        if ( !( value instanceof Array ) ) {
                                // put my options to cache
                                th.parentCache[value] = [];
                                for ( var index = 0; index < th.select.options.length; index++ ) {
                                        var curOptionValue = th.select.options[index].value;

                                        // ignore inviting option & "other" option
                                        if ( curOptionValue == invitingOptionValue || curOptionValue == otherOptionValue ) continue;

                                        // cache value
                                        th.id2option[curOptionValue] = th.select.options[index];
                                        th.parentCache[value].push( curOptionValue );
                                }
                        } else {
                        // if parent has many values selected, load correspondence between values
                                th.loadOptionsForValues( value );
                        }
                        break;

                // parent's value is "please choose", repeat it
                case "invite":
                        th.parentChoseInvite( parentValue[1] );
                        break;

                // parent's value is "other"
                case "other":
                        break;
                }

                th.getValue();
                th.detectOtherState();
        }

        // parent should call this when "please choose grandparent" was chosen in it
        this.parentChoseInvite = function( invitingOptionName ) {
                th.enableOther( false );
                th.enableSelect( false );

                // remove all options
                while ( th.select.firstChild ) {
                        th.select.removeChild( th.select.firstChild );
                }

                // add option "please choose parent"
                var invitingOption = new Option( invitingOptionName, invitingOptionValue );
            invitingOption.innerHTML = invitingOptionName; // fix for IE
                th.select.appendChild( invitingOption );

                // handle changes
                th.valueChanged();
        }

        // parent should call this when 'other' was chosen in it
        this.parentChoseOther = function() {
                // IMPORTANT: "other field" is enabled, so that one "other field" may be used for several selects
                th.enableOther( true );
                th.enableSelect( false );

                // remove all options
                while ( th.select.firstChild ) {
                        th.select.removeChild( th.select.firstChild );
                }

                // handle changes
                th.valueChanged();
        }

        // parent should call this when real value(s) was chosen in it
        this.parentChoseValue = function( values ) {
                if ( !( values instanceof Array ) ) {
                        values = [values];
                }

                th.enableOther( false );
                th.enableSelect( true );

                // memorize selected options
                var selected = {};
                var selectedArray = [];
                for ( var index = 0; index < th.select.options.length; index++ ) {
                        var opt = th.select.options[index];
                        if ( opt.selected ) {
                                selected[opt.value] = true;
                                selectedArray.push( opt.value );
                        }
                }

                // detect options to load
                var need2load = [];
                for ( var index = 0; index < values.length; index++ ) {
                        if ( !th.parentCache[values[index]] ) {
                                need2load.push( values[index] );
                        }
                }

                if ( window.DIC && DIC.loading_option ) {
                    // remove all options from select
                    while ( th.select.firstChild ) {
                            th.select.removeChild( th.select.firstChild );
                    }
                var loadingOption = new Option( DIC.loading_option, '' );
                loadingOption.innerHTML = DIC.loading_option;
                th.select.appendChild( loadingOption );
                }

                // load nesessary options, put them to select & restore selection
                th.loadOptionsForValues( need2load, function() {
                        // remove all options from select
                        while ( th.select.firstChild ) {
                                th.select.removeChild( th.select.firstChild );
                        }

                        var realOptionsCount = 0;
                        var lastRealOption = null;
                        // add needed options to select
                        for ( var index = 0; index < values.length; index++ ) {
                                var optionIDs4value = th.parentCache[values[index]];

                                // there may be no options for some parent value
                                if ( !optionIDs4value ) continue;

                                for ( var innerIndex = 0; innerIndex < optionIDs4value.length; innerIndex++ ) {
                                        var optionID = optionIDs4value[innerIndex];
                                        realOptionsCount++;
                                        lastRealOption = th.id2option[optionID];
                                        th.select.appendChild( lastRealOption );
                                }
                        }

                        // if there is at least one real option
                        if ( realOptionsCount > 0 ) {

                            // if there's only one real option & it should be instantly selected, disable select
                            if ( realOptionsCount == 1 && params.instantSelect ) {
                                th.enableSelect( false );
                                if ( params.instantSelect && th.multiple ) {
                                    try { th.select.options[0].selected = true; } catch ( e ) { ; }
                                }
                            } else {
                            // else we have choice, so we add "other option" and inviting option

                                // enable self first
                                th.enableSelect( true );

                                // add "other option" to select, if possible
                                if ( th.otherOption ) {
                                    th.select.appendChild( th.otherOption );
                                }

                                // if this select is single-choice, add invitingOption to it and select it
                                if ( !th.multiple ) {
                                    var invitingOption = new Option( params.invitingOptionName, invitingOptionValue );
                                    invitingOption.innerHTML = params.invitingOptionName;
                                    th.select.insertBefore( invitingOption, th.select.options[0] );
                                    th.select.value = invitingOptionValue;
                                } else {
                                // else this select is multi-choice & we should restore selection
                                    for ( var index = 0; index < th.select.options.length; index++ ) {
                                            var opt = th.select.options[index];
                                            try {
                                                if ( selected[opt.value] ) {
                                                        opt.selected = true;
                                                }
                                            } catch ( e ) { ; } // IE has excitingly misterious error here
                                    }
                                }

                            }
                        } else {
                    // else no real options exist, so add only "other option", but not inviting option
                            if ( th.otherOption ) {
                                th.select.appendChild( th.otherOption );
                                th.select.value = otherOptionValue;

                                th.enableSelect( false );
                            }
                        }

                        // handle changes in value, if any
                        th.valueChanged();
                } )
        }

        // this is select onchange handler
        this.valueChanged = function() {
                // stop any loading operation of child
                if ( th.childSelect ) th.childSelect.stopLoading();

                // detect type of selected value
                th.getValue();
                // disable otherFields, if needed
            th.detectOtherState();

        // call appropriate method of child, if any
        if ( !th.childSelect ) return;

            var value = th.getValue();
            switch ( value[0] ) {
        case 'value':
                    th.childSelect.parentChoseValue( value[1] );
                break;
        case 'invite':
                    // detect what invitingOptionName to use
                    var name = params.invitingOptionName;
                    if ( th.parentSelect ) {
                        var parentValue = th.parentSelect.getValue();
                        if ( parentValue[0] == 'invite' ) {
                    name = parentValue[1];
                        }
                    }

            // pass invitingOptionName to child
            th.childSelect.parentChoseInvite( name );
                break;
        case 'other':
                    th.childSelect.parentChoseOther();
                break;
            }
        }

        // shortcut function to disable/enable otherFields
        this.enableOther = function ( state ) {
            if (th.otherHandler) {
                th.otherHandler( state );
                return;
            }
            if (!th.otherFields) return;
        for (var i = 0; i < th.otherFields.length; i++) {
            var otherField = th.otherFields[i];
            otherField.className = otherField.className.replace( /\bdisabled\b/i, '' );
            if (state) {
                otherField.disabled = false;
            } else {
                otherField.className += ' disabled';
                otherField.disabled = true;
                if (!otherField.nodeName.match(/select/i)) {
                    otherField.value = params.otherFieldDefault instanceof Array
                        ? (params.otherFieldDefault[i] != null? params.otherFieldDefault[i] : '')
                        : params.otherFieldDefault;
                }
            }
            }
        }

        // shortcut function to disable/enable select
        this.enableSelect = function ( state ) {
        th.select.className = th.select.className.replace( /\bdisabled\b/i, '' );
            if ( state ) {
                th.select.disabled = false;
            } else {
                th.select.className += ' disabled';
                //th.select.disabled = true;
            }
        }

        // shortcut function to disable otherFields if needed
        this.detectOtherState = function() {
            var value = th.getValue();
            switch (value[0]) {
        case 'value':
                    th.enableOther(false);
                break;
        case 'invite':
                      th.enableOther(false);
                break;
        case 'other':
                       th.enableOther(true);
                       if (th.otherFields) th.otherFields[0].focus();
                break;
            }
        }

        // load data for values, put them in cache, and optionally call function after that
        this.loadOptionsForValues = function( values, onloadHandler ) {
            // check do we really need to load anything?
            if ( values.length == 0 ) {
                if ( onloadHandler ) onloadHandler();
                return;
            }

                var req = new JsHttpRequest();
                th.req = req;
                req.onreadystatechange = function() {
            if ( req.readyState != 4 ) return;
            if ( !req.responseJS ) return;

            if (req.responseJS.errorMessage) {
                alert(req.responseJS.errorMessage);
                return;
            }

            for ( var k in req.responseJS.result ) {
                            var value = req.responseJS.result[k];
                            if ( value instanceof Function ) continue;

                            th.id2option[value.id] = new Option( value[params.language], value.id );
                            th.id2option[value.id].innerHTML = value[params.language]; // fix for IE

                            if ( !th.parentCache[value.parent_id] ) {
                                th.parentCache[value.parent_id] = [];
                            }
                            th.parentCache[value.parent_id].push( value.id );
            }

            if ( onloadHandler ) onloadHandler();
                }
                req.caching = true;
                req.open( 'get', th.command, true );
                req.send( { 'q': values.join( ',' ) } );
        }

        // PARENT METHODS

        // return what's my type of value and some additional params
        this.getValue = function() {
                // if i'm single-choice
                if ( !th.multiple ) {

                        // if i'm inviting user to choose parent
                        if ( th.select.value == invitingOptionValue ) {
                                th.valueType = "invite";
                                return ['invite', params.invitingOptionName];
                        }

                        // if my value is "other"
                        if ( (th.select.value == "") || (th.select.value == "choose") ) {
                                th.valueType = "other";
                                return ['other'];
                        }

                        // if I have real value, send it to my child
                        th.valueType = "value";
                        return ['value', th.select.value];
                } else {
                // if i'm multiple-choice
                        // collect selected values
                        var values = [];
                        for ( var index = 0; index < th.select.options.length; index++ ) {
                                if ( th.select.options[index].selected ) {
                                        values.push( th.select.options[index].value );
                                }
                        }

                        // detect self valueType
                        if ( values.length == 0 ) {
                            th.valueType = "invite";
                            return ['invite', params.invitingOptionName];
                        } else {
                            th.valueType = "value";
                            return ['value', values];
                        }
                }
        }

        // save handle to my shild
        this.bindToChild = function( child ) {
                th.childSelect = child;
        }

        // anti-FF hack: select only options that are selected in html
        for (var index = 0; index < th.select.options.length; index++) {
            th.select.options[index].selected = th.select.options[index].getAttribute('selected');
        }

        th.getValue();
    th.detectOtherState();

        Event.observe( this.select, 'change', this.valueChanged );
}



init();
