﻿/* jquery based utilities - BEGIN */
var jutils = {

  /** 
  * return a jquery obj
  * @param joo: jquery selector or jquery object
  */
  getJqueryObj: function (joo) {
    if (typeof joo == 'object' && joo.selector != null) {
      return joo;
    }
    else if (typeof joo == 'string') {
      return $(joo);
    }
    return null;
  },

  /* ticker related utilities */
  ticker:
  {
    content: '#ims-ui-ticker-content',
    ticks: '.ims-ui-ticker',
    timer: null,
    delay: 10000,
    count: 0,

    enable: function (jselticks, jselcontent, delay) {
      jutils.ticker.content = jutils.getJqueryObj(jselcontent ? jselcontent : jutils.ticker.content);
      jutils.ticker.content = jutils.ticker.content.find('li');
      jutils.ticker.ticks = jutils.getJqueryObj(jselticks ? jselticks : jutils.ticker.ticks);
      if (jutils.ticker.content == null || jutils.ticker.ticks == null) {
        return;
      }
      jutils.ticker.tick();
      jutils.ticker.timer = setInterval('jutils.ticker.tick()', delay && delay > 0 ? delay : jutils.ticker.delay);
    },

    tick: function () {
      var litem;
      if (jutils.ticker.content.size() > 0) {
        litem = jutils.ticker.content.eq(jutils.ticker.count);
        jutils.ticker.count = ++jutils.ticker.count % jutils.ticker.content.size();
        jutils.ticker.ticks.hide();
        jutils.ticker.ticks.html(litem.text());
        jutils.ticker.ticks.fadeToggle('slow', 'linear');
      }
    }
  }, // ticker

  /* form related utilities */
  form:
  {
    /* empty function */
    nofunction: function () {
    },
    /* call callback function without parameters */
    proceed: function (func) {
      if (func != null && typeof (func) == 'function') { func(); }
    },
    /* returns success/error function */
    callback: function (func) {
      if (func != null && typeof (func) == 'function') { return func; } else { return form.nofunction; }
    },

    /**
    * perform an ajax request with jquery by sending a xml file to the server
    * could be used to send request to many target at once
    *
    * @param data: array with container/form ids
    * @param index: array with target names
    * @param post: specify whether data should be immediately ajax-posted if checking succeeds; if false or null data are returned to the caller and no ajax request is made
    * @param succ(optional): function to be called in case that the ajax post succeeds
    * @param err(optional): function to be called in case that the ajax post fails
    * @param before(optional): function to be called before the container/form inputs are checked
    * @param after(optional): function to be called after the container/form inputs are checked or when checking fails
    * @param action(optional): string to be used by the server to determine what to do with the data
    *
    * !! WARNING - 1. submitting the 'action' parameter will completely change the xml structure
    */
    checkAndPost: function (data, index, post, succ, err, before, after, action) {
      // sanitize
      if (typeof (data) != typeof (index) || data.constructor != Array) { return; }

      // get form data
      var actionstr = action != null && $.trim(action) != '';
      var formdata = actionstr ? '<values>' : ''; // see !! WARNING - 1.
      actionstr = actionstr ? '<action>' + action + '</action></values>' : ''; // see !! WARNING - 1.
      var fd;

      jutils.form.proceed(before);
      for (var i = 0; i < data.length; i++) {
        fd = ajaxForms.getDataStr(document.getElementById(data[i]), index[i]);
        if (fd.indexOf('data') == 1) { formdata += fd; }
        else {
          jutils.form.proceed(after);
          $.msgbox.alert("Ihre Angaben sind unvollständig oder unkorrekt!");
          return null;
        }
      } // for

      formdata += actionstr;
      jutils.form.proceed(after);

      // remove node generated by the attachment form
      var torem = $('.faqdata form:first input[type=file]').attr('name');
      torem = '<' + torem + '.*' + torem + '>';
      torem = torem.replace(/\$/g, '\\$');
      torem = new RegExp(torem, "g");
      formdata = formdata.replace(torem, '');

      if (post == null || !post) {
        return formdata;
      }

      // submit xml with jquery ajax
      $.ajax({
        type: 'POST',
        contentType: 'text/xml',
        data: formdata,
        processData: false,
        success: jutils.form.callback(succ),
        error: jutils.form.callback(err)
      });
    },

    selectbox: {
      populateHtml: "<option></option>",
      novalue: "",
      novaluetext: "--",

      /**
      * divtip a select box with options to be choosen
      *
      * @param jssel: jquery selector of the select box
      * @param jstrigger: jquery selector of the divtip trigger
      * @param cssClass: css class to be used to tweak the divtip appearance
      * @param func: function name to be call upon the value of the selected option of the select box
      *   ex: callfunc   -> callfunc(selected_value_of_the_select_box);
      *       callfunc() -> callfunc();
      * @param opts: options to be used by the divtip
      * @param selected: whether the selected option should be marked in the divtip
      */
      enableDropDown: function (jssel, jstrigger, cssClass, func, opts, selected) {
        $(function () {
          var sb = $(jssel);
          $(jstrigger)
            .divtip(function () {
              var seltemp = jutils.ui._getTemp('div', 'ims-select-temp');
              seltemp.html(sb.html().replace(/<option /ig, '<div class="' + cssClass + '" ').replace(/<\/option>/ig, '</div>'))
                .children('div')
                .each(function () {
                  var _this = $(this);
                  _this.removeClass('ims-select-dtselected');
                  _this.wrapInner('<a href="javascript:' + (func.indexOf('(') > 0 ? func : func + '(\'' + _this.attr('value') + '\')') + '" />').append('&nbsp;');
                  if (selected && selected == true && sb.val() == _this.attr('value')) {
                    _this.addClass('ims-select-dtselected');
                  }
                });
              return $('#' + seltemp.attr('id'));
            }, opts)
            .hover(
              function () {
                $(this).addClass('ims-select-dttrigger');
              },
              function () {
                $(this).removeClass('ims-select-dttrigger');
              });
          $(jstrigger + ' span').html(sb.children(':selected').text());
        });
      },

      /**
      * @param jsel: jquery selector of the select box
      * count number of non empty values
      */
      count: function (jsel) {
        var cnt = 0;
        $(jsel + ' option').each(function () {
          if ($(this).attr('value') != jutils.form.selectbox.novalue) { cnt++; }
        });
        return cnt;
      },

      /** 
      * set select boxES to valueS contained in values and trigger the change event for EACH select box specified by the selector
      * @param jsel: jquery selector of the select boxes
      * @param values: array of values
      */
      set: function (jsel, values) {
        $(jsel).each(function (index) {
          for (var i = 0; i < values.length; i++) {
            $(this).val(values[i]);
            $(this).trigger('change');
          }
        });
      },

      /**
      * clear the select box
      * @param jsel: jquery selector of the select box
      * @param pre: specify whether empty entry should be prepended to the cleared select box
      */
      clear: function (jsel, pre) {
        $(jsel).children('option').remove();
        if (pre && pre == true) {
          $(jsel).prepend($(this.populateHtml).show().attr('value', this.novalue).html(this.novaluetext));
        }
      },

      /* add a single option to a select box
      * this function is used by jutils.form.selectbox.add
      * @param jsel: jquery selector of the select box
      * @param cc: options object {id:, name:}
      */
      option: function (jsel, cc) {
        $(this.populateHtml)
          .show()
          .attr('value', cc.id)
          .html(cc.name)
          .appendTo($(jsel));
      },

      /**
      * add options to a select box and prepend an optional label
      * @param jsel: jquery selector of the select box
      * @param lo: json array of options objects i.e. [ {"id" : "", "name": ""}, ... ]
      * @param label: optional; label name -to use only with simple selectors (.xxx or #xxx) since the for attribute is set in accordance to the selector-
      */
      add: function (jsel, lo, label) {
        var aoo = (typeof lo == "string") ? $.parseJSON(lo) : lo;
        if (aoo == null) { return; }
        for (var i = 0; i < aoo.length; i++) {
          this.option(jsel, aoo[i]);
        }
        if (label) { $(jsel).prev('label').remove(); $(jsel).before($('<label></label>').text(label + ':').attr('for', jsel.substr(1))); } // prepend label
      },

      /**
      * remove options from a select box
      * @param jsel: jquery selector of the select box
      * @param lo: json array of options objects i.e. [ {"id" : "", "name": ""}, ... ]
      */
      remove: function (jsel, lo) {
        var aoo = (typeof lo == "string") ? $.parseJSON(lo) : lo;
        if (aoo == null)
          return;
        for (var i = 0; i < aoo.length; i++) {
          $(jsel + ' option[value=' + aoo[i] + ']').remove();
        }
      },

      /**
      * copy content from one select box to the other
      * @param jselfrom: select box from
      * @param jselto: select box to
      * @param pre: specify whether empty entry should be prepended to the cleared select box to
      */
      copy: function (jselfrom, jselto, pre) {
        var lngarr = new Array();
        jutils.form.selectbox.clear(jselto, pre);
        $(jselfrom).children('option').each(function () {
          lngarr.push({ id: $(this).val(), name: $(this).text() });
        });
        jutils.form.selectbox.add(jselto, lngarr);
      },

      /**
      * sort the selectboxes in an alphabetical order
      */
      sort: function () {
        var selobj = $("select");
        selobj.each(function () {
          if ($(this).attr("id") != null && $(this).css("display") != "none" && $(this).attr("sort") == "sort") {
            jutils.form.selectbox.singleSort(this);
          }
        });
      },

      /**
      * sort a selectbox in an alphabetical order
      * @obj selectbox object
      */
      singleSort: function (selobj) {
        var selval = selobj.value;
        try {
          var sorted = $.makeArray($("#" + selobj.id + " option")).sort(function (a, b) { return $(a).text() > $(b).text() ? 1 : -1; });
          $("#" + selobj.id).empty().append(sorted);
          selobj.value = selval;
        } catch (e) { /* no sorting */ }
      }

    }, // selectbox

    input:
    {
      /** 
      * enable default text for the given input field, which should have the 'title' attribute set
      * @param joo: jquery selector of the input field
      */
      enableDefaultText: function (jsel) {
        $(function () {
          var joinp = $(jsel);
          if ($.trim(joinp.attr('title')) == '') {
            return;
          }

          joinp.val(joinp.attr('title') + '...');
          joinp.focusin(function () {
            if ($.trim($(this).val()) == $(this).attr('title') + '...') {
              $(this).val('');
            }
          });
          joinp.focusout(function () {
            if ($.trim($(this).val()) == '') {
              $(this).val($(this).attr('title') + '...');
            }
          });
        });
      }, // enableDefaultText

      /** 
      * enable submit on given input field
      * @param jsel: jquery selector of the input field
      * @param func: function to be called with the input value
      */
      enableEnterOnSubmit: function (jsel, func) {
        $(function () {
          if (typeof func != 'function') { return; }
          $(jsel).keypress(function (event) {
            if (event.keyCode == '13') {
              func.call(this, $(this).val());
            }
          });
        });
      } // enableEnterOnSubmit
    } // input
  }, // form

  search:
  {
    sortqs: '_ORDER',
    attr: 'sort',
    sortCssClass: 'ims-ui-sort-icon',
    sortOnCssClass: 'ims-ui-sort-icon-on',
    sortaCssClass: 'ims-ui-sort-icona',
    sortdCssClass: 'ims-ui-sort-icond',
    sortaImgSrc: null,
    sortdImgSrc: null,

    /**
    * enable sorting by making a new request
    *
    * @param hsel: jquery selector of the header
    */
    enableSorting: function (hsel) {
      if (hsel == null || hsel == '') { return; }
      var hselo = typeof hsel == 'object' ? hsel : $(hsel);

      jutils.search.sortaImgSrc = jutils.ui.getImgSrc(jutils.search.sortaCssClass);
      jutils.search.sortdImgSrc = jutils.ui.getImgSrc(jutils.search.sortdCssClass);
      $(hselo).addClass(jutils.search.sortCssClass).click(function () { jutils.search.sort($(this)) });
    },

    /**
    * enable selection of columns by chechboxes
    *
    * @param trigger: jquery selector of the master checkbox
    * @param rows: jquery selector of the other checkbox
    */
    enableChecking: function (trigger, rows, activated) {
      if (trigger == null || trigger == '' || rows == null || rows == '') { return; }
      rows = $(rows).not(trigger);
      if (activated == null || activated == true) {
        rows = rows.not(':disabled');
      }
      trigger = $(trigger);

      trigger.click(function () {
        if (rows.size() > 0) {
          rows.attr('checked', $(this).attr('checked'));
        }
        else {
          $(this).attr('checked', false);
        }
      });
      rows.click(function () {
        if (!$(this).attr('checked')) { trigger.attr('checked', false); }
        else if (!rows.is(':not(:checked)')) { trigger.attr('checked', true); }
      });
    },

    /**
    * mark the column the current sorting is made upon
    *
    * @param hsel: jquery selector of the header
    */
    setSortingColumnFromURL: function (hsel) {
      var sp = window.location.href.split(jutils.search.sortqs + '=');
      sp = sp.length > 1 ? sp[1].split('&') : null;
      sp = sp != null && sp.length > 0 ? sp[0] : null;
      sp = sp != null ? decodeURIComponent(sp).replace(/^%(.*)%$/, '$1') : null;
      if (sp == null) { return; }
      var jo = $(hsel + '[' + jutils.search.attr + '^=' + sp.split(' ')[0] + ']:first').attr(jutils.search.attr, sp);
      jutils.search.setSortingIcon(jo);
    },

    /**
    * set the sorting icon for the given header
    *
    * @param jo: jquery object of the header
    */
    setSortingIcon: function (jo) {
      var col = decodeURIComponent(jo.attr(jutils.search.attr)).split(' ');
      var order = col[1];
      col = col[0];

      jo.addClass(jutils.search.sortOnCssClass);
      jo.attr('abbr', col + ' ' + order); // set current order from url
      // note that classes are switched because picts are inverted, should be normally desc -> sortdCssClass !!
      order = order == 'desc' ? jutils.search.sortaImgSrc : jutils.search.sortdImgSrc;
      jo.remove('img').append($('<img />').attr('src', order));
    },

    /**
    * send a request to sort results by changing 'window.location.search'
    *
    * @param ho: jquery object of the header
    */
    sort: function (jo) {
      var sort = jo.attr(jutils.search.attr);
      if (sort == null || sort == '') { return; }

      var abbr = sort.split(' ');
      var sortpos;
      var wls;
      if (abbr.length != 2) {
        return;
      }
      else {
        sort = abbr[0] + (abbr[1] == 'desc' ? ' asc' : ' desc');
        jo.attr(jutils.search.attr, sort); // switch order
        wls = window.location.search;
      }
      sortpos = wls.indexOf(jutils.search.sortqs);
      wls = sortpos - 1 >= 0 ? wls.substr(0, sortpos - 1) : wls;
      wls += wls.indexOf('?') >= 0 ? '&' : '?';
      wls += jutils.search.sortqs + '=' + encodeURIComponent(sort);
      window.location.search = wls;
    }, // sort

    /**
    * get a string to be used for searching, which is computed from the values from the given inputs.
    * Note that the given inputs should be bounded to a datepicker prior calling.
    *
    * @param from: jquery selector of the input with the start date
    * @param until: jquery selector of the input with the end date
    */
    getFromUntilDates: function (from, until) {
      var df, dt;
      df = jutils.getJqueryObj(from);
      if (df.size() == 0) return;
      dt = jutils.getJqueryObj(until);

      var ds = 'ft[', dts;
      dts = jutils.date.getDate(df.datepicker('getDate'), '-');
      ds += dts == '' ? '' : 'convert(datetime,\'' + dts + '\')'; // dts should be format independent (see formatDate and ISO_8601 in datepicker) *********************TODO*********************
      ds += ';';
      dts = jutils.date.getDate(dt.datepicker('getDate'), '-');
      ds += dts == '' ? '' : 'convert(datetime,\'' + dts + '\')'; // dts should be format independent (see formatDate and ISO_8601 in datepicker) *********************TODO*********************
      ds += ']';
      return ds.indexOf('convert') >= 0 ? ds : '';
    }, // getFromUntilDates

    /**
    * set the values from the given inputs from the given string.
    * Note that the given inputs should be bounded to a datepicker prior calling.
    *
    * @param from: jquery selector of the input with the start date
    * @param until: jquery selector of the input with the end date
    * @param val: string which were previously generated by 'jutils.search.getFromUntilDates'
    */
    setFromUntilDates: function (from, until, val) {
      $(function () {
        var dates = val.split(';');
        var dt = jutils.getJqueryObj(until), df, rep;
        if (dt.size() == 0) { return; } else { rep = '-'; }
        df = jutils.getJqueryObj(from);
        jutils.date.setDate(df, dates[0], rep);
        jutils.date.setDate(dt, dates[1], rep);
      });
    } // setFromUntilDates
  }, // search

  /* navigation related utilities */
  navi:
  {
    active: '',
    cssClasses: ["ST", "KN", "MO", "CZ", "AD", "SV", "HM", "TM", "TR"],

    searchActive: function () {
      for (var i = 0; i < jutils.navi.cssClasses.length; i++) {
        $('.' + jutils.navi.cssClasses[i]).filter(function () {
          jutils.navi.active = $(this).css('display') == 'inline' ? jutils.navi.cssClasses[i] : jutils.navi.active; // != 'none' ??
          return false;
        });
        if (jutils.navi.active != '') {
          break;
        }
      }
    },

    setActive: function (cls) {
      if (cls == null || $.trim(cls) == '') {
        jutils.navi.searchActive();
      }
      $('#mainNavigation li')
        .removeClass('active-tab')
        .filter(function () {
          return $(this).children('a').hasClass(jutils.navi.active);
        })
      .toggleClass('active-tab');
    },

    enableDropDown: function (opts) {
      $(function () {
        $('.tab-wrap').each(function () {
          if ($(this).hasClass('logout')) {
            $(this).divtip('.links', $.extend({}, opts, { offset: [0, -8] }));
          }
          else if ($(this).hasClass('login') && $('.links .HL').size() > 0) {
            $(this).divtip('.links .HL', $.extend({}, opts, { offset: [0, -8] }));
          }
          else {
            var _selected_ = $.trim($(this).find('.tab-link').attr('class').replace('tab-link', ''));
            var _header_ = _selected_ == 'KN' ? '#navigation .' + _selected_ + '.VEACCORDIONHEADER, #navigation .' + _selected_ + '.HLACCORDIONHEADERACTIVE'
                : $('#navigation .' + _selected_ + '.VEACCORDIONHEADER, #navigation .' + _selected_ + '.HLACCORDIONHEADERACTIVE').first().next(); // '#navigation .' + _selected_ + '.VEACCORDIONCONTENT, #navigation .' + _selected_ + '.VEACCORDIONCONTENTACTIVE';
            if (_selected_ != 'ST' && $(_header_).size() > 0) {
              $(this).divtip(function () {
                return $(_header_).clone()
                  .find('.ARROWUNDERLINE a')
                  .each(function () {
                    $(this).attr('href', $(this).attr('ohref'));
                  }).end();
              }, opts);
            }
          }
        });
      });
    },

    disableDropDown: function (jsel) {
      jutils.getJqueryObj(jsel).unbind(tooltip.evtns);
    },

    enableActiveMenu: function () {
      if (typeof jutils.navi.active != 'undefined') {
        var menuac = jutils.navi.active == '' ? '.VEACCORDIONHEADER' : '.VEACCORDIONHEADER.' + jutils.navi.active;
        var item = $('#navigation div' + menuac);
        for (var i = 0; i < item.size(); i++) {
          var iitem = item.eq(i).next('div').find('.HL .ARROWUNDERLINE2');
          var aitem = iitem.filter(function () { return window.location.href.indexOf($(this).children('a').attr('href')) > 0; });
          if (aitem.size() == 1) {
            item.eq(i).trigger('click');
            aitem.parent().addClass('ITEMACTIVE');
            break;
          }
          else if (item.size() == 1 && item.eq(0).hasClass('VEACCORDIONHEADER')) {
            item.eq(0).trigger('click');
            break;
          }
        }
      }
    } // enableActiveMenu
  }, // navi

  /* player related utilities */
  player: {
    iframeID: '#cf',
    resetIframeWhenClosed: false,
    /**
    * show the player container
    *
    * @param url: url to be loaded into the player
    * @param mime: mime type of content
    * @param title: player title
    * @param width: player width
    * @param height: player height
    **/
    show: function (url, mime, title, width, height) {
      var player = $.aspid('player');
      $('#ims-player-stub').find('div.ims-player-div').remove().end().find('div.ims-player-footer').before('<div class="ims-player-div"></div>');
      var stub = $('#ims-player-stub div.ims-player-div').empty().removeClass().addClass('ims-player-div ims-player-' + mime);

      if (width) { stub.width(width); }
      if (height) { stub.height(height); }
      var frameborder = 18;
      var cf = $(jutils.player.iframeID);

      cf.attr('src', url);
      var paddem = cf.css('font-size').replace('px', '');

      if (stub.width() == 0) { stub.removeClass().addClass('ims-player-div ims-player-misc'); }
      player.dialog('option', 'title', title);
      cf.css('minHeight', $('#ims-player-stub').height() + 1 * paddem + frameborder); // 1 x 1em min of jquery padding + frameborder
      player.dialog('option', 'width', stub.width() + 2 * paddem + frameborder); // 2 x 1em of jquery padding + frameborder
      player.dialog('option', 'height', 'auto');
      player.data('player', false);
      if (!jutils.player.resetIframeWhenClosed) {
        $.aspid('player').bind('dialogclose', function () {
          $(jutils.player.iframeID).attr('src', 'about:blank'); // reset url of iframe to force audio/video to stop !!
          jutils.player.resetIframeWhenClosed = true;
        });
      }
      player.dialog('open');
    },

    close: function (func) {
      if (func == null) { // call dialog close function
        var player = $.aspid('player');
        player.data('player', true);
        player.dialog('close');
      }
      else { // attach a function to the dialog close event
        $(function () {
          $.aspid('player').bind('dialogclose',
            function () {
              if (!$(this).data('player')) {
                func.call(this);
              }
              else {
                $(this).data('player', false);
              }
            });
        });
      }
    }
  }, // player

  /* date related utilities (formatting, ...) */
  date: {
    isoformat: 'T00:00:00',
    sep: '-',

    /**
    * init the date format to the ui locale from the jquery locale file
    *
    * @param jquilocale: jquery lng+cty part of the jquery dateformat file
    * @current: shortdatepattern of the current cultureinfo
    * ! requires the jquery datepicker
    **/
    setJQDateFormat: function (jquilocale, current) {
      if (jquilocale != null) {
        _utils_const_.jquilocale = jquilocale;

        if (jquilocale == '' || typeof current != 'undefined') {
          format = current.toLowerCase();
          format = current.indexOf('yyyy') >= 0 ? current.replace('yyyy', 'yy') : current.replace('yy', 'y'); // jquery: one 'y' counts as two
          $.datepicker.regional[jquilocale].dateFormat = format;
        }
        $.datepicker.regional[jquilocale].dateFormat = $.datepicker.regional[jquilocale].dateFormat.toLowerCase();
        $.datepicker.setDefaults($.datepicker.regional[jquilocale]);
      }
    },

    /**
    * return the current dateformat of the jquery datepicker
    **/
    getJQDateFormat: function () {
      var format = null;
      if (typeof _utils_const_ != 'undefined' && _utils_const_.dateformat != null && _utils_const_.dateformat != '') {
        format = _utils_const_.dateformat.toLowerCase();
        format = format.indexOf('yyyy') >= 0 ? format.replace('yyyy', 'yy') : format.replace('yy', 'y'); // jquery: one 'y' counts as two
      }
      return format;
    },

    /*
    * get the iso format of the datepicker
    * @param jqdate: jquery object of the date picker
    * @param sep: DEPRECATED i.e. ignored
    **/
    getDate: function (jqdate, sep) {
      var ds = '', dtjs = null;
      if (jqdate != null) {
        dtjs = [jqdate.getFullYear().toString(), (jqdate.getMonth() + 1).toString(), jqdate.getDate().toString()];
        ds = dtjs == null ? '' : dtjs.join(jutils.date.sep) + jutils.date.isoformat;
        ds = ds.replace(/-([1-9]{1})(-|T)/ig, '-0$1$2').replace(/-([1-9]{1})(-|T)/ig, '-0$1$2')
      }
      return ds;
    },

    setDate: function (jqdatepicker, date, rep) {
      if (date.indexOf(',') >= 0) {
        var doj = date.split(',')[1].replace(jutils.date.isoformat, '').replace(')', '').replace(/'/g, '').replace(']', '');
        doj = $.trim(doj);
        doj = doj.split(rep);
        if (doj != null && doj.length >= 3) {
          jqdatepicker.datepicker('setDate', new Date(doj[0], parseInt(doj[1]) - 1, doj[2]));
        }
      }
    },

    restrictFromUntil: function (from, until) {
      $(function () {
        var df = jutils.getJqueryObj(from), dt = jutils.getJqueryObj(until);
        if (!df || !dt || df.size() == 0 || dt.size() == 0) { return; }

        dt.datepicker('option', 'onSelect', function () {
          df.datepicker('option', 'maxDate', $(this).datepicker('getDate'));
        });
        df.datepicker('option', 'onSelect', function () {
          dt.datepicker('option', 'minDate', $(this).datepicker('getDate'));
        });
      });
    }
  }, // date

  /* ui language and country related utilities */
  lngcty: {
    /**
    * set a language for the ui
    *
    * @param jsel: jquery selector of the select box
    * @param lngcty: value to be selected which should be of the format language-country (i.e de-DE, en-GB, ...) 
    *                or the first value starting with the language would be selected (de-US or de* => de-DE)
    **/
    set: function (jsel, lngcty) {
      var lcs = $(jsel), lcsval = _utils_const_.lngcty;
      if (lcs.size() > 0) {
        if (lcs.children('option[value=' + lngcty + ']:first').size() > 0) {
          lcsval = lngcty;
        }
        else {
          var flcv = lcs.children('option[value^=' + lngcty.substr(0, 2) + ']:first');
          if (flcv.size() > 0) {
            lcsval = flcv.attr('value');
          }
        }
      }
      lcs.val(lcsval);
      jutils.form.selectbox.singleSort(lcs[0]);
    }
  }, // lngcty

  /* ui related utilities */
  ui: {

    /**
    * get a temporary html element, which is hidden
    *
    * @param elt: element name
    * @param id: id the element should have
    */
    _getTemp: function (elt, id) {
      var jstemp = $('#' + id.replace('#', ''));
      jstemp = $('#' + id).size() != 1 ? $('<' + elt + '></' + elt + '>').attr('id', id).hide().appendTo('body') : jstemp;
      return jstemp;
    },
    /**
    * setup the date picker
    *
    * @param jsel: jquery selector of the input box to be used as datepicker
    * ! requires the jquery datepicker
    **/
    setupBDatePicker: function (jsel) {
      $(function () {
        var birthday = $(jsel), yr;

        if (birthday.size() > 0) {
          yr = new Date().getFullYear();
          birthday.datepicker({
            showOn: 'both',
            showButtonPanel: true,
            buttonImage: '../images/icons/date.png',
            buttonImageOnly: true,
            changeMonth: true,
            changeYear: true,
            hideIfNoPrevNext: true,
            maxDate: new Date(yr - 10, 11, 31),
            minDate: new Date(yr - 100, 0, 1),
            yearRange: (yr - 100) + ':' + (yr - 10)
          });
          birthday.attr('disabled', 'disabled');
        }
      });
    },

    /* get image src of a specific css class having the background-image property set */
    getImgSrc: function (cssClass) {
      var uitemp = $('#ims-ui-tmpspan');
      uitemp = uitemp.attr('id') == undefined ? $('<span></span>').attr('id', 'ims-ui-tmpspan').hide().appendTo('body') : uitemp;
      return uitemp.removeClass().addClass(cssClass).css('backgroundImage').replace('url("', '').replace('")', '');
    },

    /* get jquery image element with the source set to the bng used for the given css class */
    getImg: function (cssClass) {
      return $('<img/>').attr('src', jutils.ui.getImgSrc(cssClass));
    },

    popupLoginBox: function () {
      var login = $('.marginalBOX.formOutlined').addClass('formPopup');
      var prevdiv = login.prev();
      var pdiv = login.parent();
      var loginbox = $('#ims-form-popup');
      loginbox = loginbox.attr('id') == null ? $('<div></div>').attr('id', 'ims-form-popup').appendTo('body') : loginbox;
      loginbox.hide().append(login).css({ 'padding-left': '1em', 'padding-right': '2em', 'overflow': 'inherit', 'width': login.width() }); // !!! use classes instead

      loginbox.dialog({
        modal: true,
        resizable: false,
        draggable: false,
        title: login.find('.boxHead span').html(),
        minWidth: loginbox.width() + 3 * login.css('font-size').replace('px', ''),
        close: function (ev, ui) {
          $(this).dialog('destroy');
          if (prevdiv.size() == 0) {
            pdiv.prepend(login.removeClass('formPopup').show());
          }
          else {
            prevdiv.after(login.removeClass('formPopup').show());
          }
        }
      });
      login.find('input[type=text]:first').focus();
    },

    enableHighlighting: function (jsel) {
      var jselo = jutils.getJqueryObj(jsel);
      jselo
        .mouseenter(function (evt) { $(this).addClass('ims-ui-highlight'); })
        .mouseleave(function (evt) { $(this).removeClass('ims-ui-highlight'); });
    },

    enableToolTitle: function () {
      $(function () {
        $('input').each(function () {
          var $this = $(this);
          var attr = $this.attr('tooltitle');
          if (attr != null && attr != '' && $this.css('display') != 'none') {
            var img = jutils.ui.getImg('ims-ui-tooltip-icon')
              .attr({ title: attr.replace(/(\\n|\\r\\n)/g, '<br/>') })
              .addClass('ims-ui-tooltip-img');
            if ($this.attr('type') == 'checkbox') { $this.before(img); } else { $this.after(img); }
          }
        });
        jutils.ui.setupTooltip('img.ims-ui-tooltip-img[title]');
      });
    },

    setupTooltip: function (jsel) {
      if ($.fn.hasOwnProperty('tooltip') && !$(jsel).data('hasTooltip')) { // requires jQuery Tooltip from 'flowplayer.org'
        $(jsel).data('hasTooltip', true);
        $(jsel).tooltip({ position: 'bottom right', offset: [-2, -2], tipClass: 'ims-ui-tooltip' })
          .dynamic({ classNames: 'ims-ui-tooltip-top ims-ui-tooltip-right ims-ui-tooltip-bottom ims-ui-tooltip-left' });
      }
    },

    enableToggle: function (jseltrigger, jseltarget, htmlOff, htmlOn) {
      $(function () {
        $(jseltrigger).toggle(
        function () {
          $(jseltrigger).html($(jseltarget).css('display') == 'none' ? htmlOn : htmlOff);
          $(jseltarget).slideToggle('slow');
        },
        function () {
          $(jseltrigger).html($(jseltarget).css('display') == 'none' ? htmlOn : htmlOff);
          $(jseltarget).slideToggle('slow');
        });
      });
    }, // enableToggle

    /*
    * get summarized text and append link to tooltip the fulltext
    * @param txt: fulltext to be summarized
    * @param length: length to use for summarization
    * @param lnk: whether fulltext should be show in tooltip
    * !! WARNING - 1. this rely on the css class 'hidden' and the jutils.ui.setupTooltip(...)
    */
    summarize: function (txt, length, lnk) {
      if (txt == null) {
        return '';
      }
      var reg = new RegExp('^.{0,' + length + '}\\s', "gi");
      var res = reg.exec(txt + ' ');
      res = res ? res : '';
      return txt.length <= length ? txt : res + (lnk ? '<span class="summary" onmouseover="jutils.ui.setupTooltip(this)">...</span><div class="ims-ui-tooltip hidden">' + txt + '</div>' : '...');
    } // summarize
  } // ui
}
/* jquery based utilities - END */

/* jquery extensions BEGIN */ // ?-> jutils.ext.* (aspid, msgbox, ...)

/**
* enable extension of jquery to support ASP.NET generated ids in js code 
* by allowing jquery to register and retrieve ASP.NET webcontrols by id 
**/
var aspids = {
  /* register uc id with generated id */
  register: function (ucid, aspid) {
    aspids.ids[ucid] = aspid;
    return aspids.id(ucid);
  },
  /* get jquery object of the uc */
  id: function (ucid) {
    return aspids.ids.hasOwnProperty(ucid) ? $('#' + aspids.ids[ucid.replace('#', '')]) : null;
  },

  proxy: function (method) {
    if (aspids[method]) {
      return aspids[method].apply(this, Array.prototype.slice.call(arguments, 1));
    }
    else if (arguments.length == 2 && typeof arguments[0] === 'string' && typeof arguments[1] === 'string') {
      return aspids.register.apply(this, arguments);
    }
    else if (arguments.length == 1 && typeof arguments[0] === 'string') {
      return aspids.id.apply(this, arguments);
    }
    else {
      alert('jQuery.aspid doesn\'t support the call with the following argument "' + Array.prototype.slice.call(arguments, 0) + '" !');
    }
  },

  ids: {}
};   // aspids

/**
* allow extension of jquery to support customized message boxes
*/
var boxes = {
  debug: false,

  /* internal object for managing box ids */
  ids: {
    alert: 'jqalert',
    info: 'jqinfo',
    confirm: 'jqconfirm',
    load: 'jqload',
    prompt: 'jqprompt'
  },

  /* internal object for implementing confirmation scheme */
  confirm: {
    jqconfirm: null,
    count: 0,
    limit: 0,
    msgarr: new Array(),
    title: '',
    func: null, // callback function after confirmation
    nofunc: null, // callback function after cancellation

    /**
    * start the confirmation sequence 
    * @param msgs: array of string messages to display each after the other
    * @param title: dialog title 
    * @param confirmaction: handler for click on 'ok'
    * @param cancelaction: handler for click on 'cancel'
    */
    init: function (msgs, title, ok, cancel) {
      boxes.confirm.jqconfirm = $.aspid(boxes.ids.confirm);
      var confirmaction = ok ? ok : boxes.confirm.jqconfirm.data('ok'), cancelaction = cancel ? cancel : boxes.confirm.jqconfirm.data('cancel');
      if (typeof confirmaction == 'function') { boxes.confirm.callback(confirmaction, true); }
      if (typeof cancelaction == 'function') { boxes.confirm.callback(cancelaction, false); }

      var cmsgs = new Array();
      cmsgs.push(msgs);
      cmsgs = msgs.constructor == Array ? msgs : (typeof msgs == 'string' ? cmsgs : '');

      if (cmsgs !== '' && cmsgs.length >= boxes.confirm.limit) {
        boxes.confirm.limit = cmsgs.length;
        boxes.confirm.msgarr = cmsgs;
        boxes.confirm.title = title;

        boxes.attach(boxes.confirm.jqconfirm, boxes.confirm.show, boxes.confirm.jqconfirm.data('ok'));
        boxes.attach(boxes.confirm.jqconfirm, boxes.confirm.noshow, boxes.confirm.jqconfirm.data('cancel'));
        boxes.showbox(boxes.confirm.jqconfirm, boxes.confirm.msgarr[boxes.confirm.count], boxes.confirm.title);
        boxes.confirm.count++;
      }
    },

    /*
    * declare callback function after confirmation/cancellation event which will be nulled after one of this event occurs
    * @param func: function
    * @param okOrCancel: true means confirmation, false means cancellation
    */
    callback: function (func, okOrCancel) {
      if (typeof (func) == 'function') {
        if (okOrCancel) {
          boxes.confirm.func = func;
        }
        else {
          boxes.confirm.nofunc = func;
        }
      }
    },

    /**
    * reset the confirm setup i.e. reset all callbacks, arrays and counters
    */
    reset: function () {
      boxes.confirm.count = 0;
      boxes.confirm.limit = 0;
      boxes.confirm.msgarr = new Array();
      boxes.confirm.func = null;
      boxes.confirm.nofunc = null;
    },

    /* show dialog until user reaches limit of confirmation or cancel */
    show: function () {
      if (boxes.confirm.count < boxes.confirm.limit) {
        boxes.showbox(boxes.confirm.jqconfirm, boxes.confirm.msgarr[boxes.confirm.count], boxes.confirm.title);
        boxes.confirm.count++;
      }
      else { // call function after confirmation
        if (boxes.confirm.func != null) { boxes.confirm.func.call(this); }
        boxes.confirm.reset();
      }
    }, // used when deleting

    /* call function after cancellation */
    noshow: function () {
      if (boxes.confirm.nofunc != null) { boxes.confirm.nofunc.call(this); }
      boxes.confirm.reset();
    }
  }, // confirm

  /* internal object for implementing prompt scheme */
  prompt: {
    jqprompt: null,
    content: '',
    func: null,
    values: null,

    init: function (msg, title, ok) {
      boxes.prompt.jqprompt = $.aspid(boxes.ids.prompt);
      var promptaction = ok ? ok : boxes.prompt.jqprompt.data('ok');
      boxes.prompt.setContent(msg, promptaction);
      boxes.attach(boxes.prompt.jqprompt, boxes.prompt.proceed, boxes.prompt.jqprompt.data('ok'));
      boxes.attach(boxes.prompt.jqprompt, '', boxes.prompt.jqprompt.data('cancel'));
      boxes.showbox(boxes.prompt.jqprompt, boxes.prompt.content, title);
    },

    /* caller to the function which should proceed input values */
    proceed: function () {
      if (typeof boxes.prompt.func == 'function') {
        boxes.prompt.values = boxes.prompt.getContent();
        boxes.prompt.func.call(this, boxes.prompt.values);
      }
    },

    /* get values of input fields as string or array */
    getContent: function () {
      var values = $.aspid(boxes.ids.prompt).find('input'), res;
      if (values.size() == 1) {
        res = values.val();
      }
      else {
        res = new Array();
        for (var i = 0; i < values.length; i++) {
          res.push(values.eq(i).val());
        }
      }
      return res;
    },

    /* set content with input fields */
    setContent: function (msg, func) {
      boxes.prompt.content = '';
      if (typeof msg == 'string') {
        boxes.prompt.content = boxes.prompt.getInput(msg).html();
      }
      else if (msg.constructor == Array) {
        for (var i = 0; i < msg.length; i++) {
          boxes.prompt.content += boxes.prompt.getInput(msg[i]).html() + '<br/><br/>';
        }
      }
      boxes.prompt.func = func;
    },
    getInput: function (label) {
      return $('<div></div>')
        .append($('<label>' + label + ':</label>').addClass('ims-ui-box-label'))
        .append($('<input />').addClass('ims-ui-box-input'));
    }
  }, // prompt

  showbox: function (box, msg, title) {
    if (box) {
      $('.ims-ui-box').each(function () { $(this).parents('.ui-widget-content:first').dialog('close'); }); // close all boxes first !!
      if (title && title != '') { box.dialog('option', 'title', title); } else { box.attr('title', ''); box.dialog('option', 'title', ''); }

      boxes.msg(box, msg);
      box.dialog('open');
      if (box.data('autoClose')) {
        boxes.close(box.attr('id'), box.data('delay'));
      }
      box.nextAll('.ui-dialog-buttonpane').addClass('ims-ui-box-buttonpane').find('.ui-dialog-buttonset').addClass('ims-ui-box-buttonpane');
      return box;
    }
  }, // showbox

  msg: function (box, txt) {
    box.find('div:first').html(txt ? txt.replace(/(\n|\r\n)/g, '<br/>') : 'null');
  },

  close: function (id, delay) {
    setTimeout('$(\'#' + id + '\').dialog(\'close\')', delay);
  }, // close

  attach: function (jqb, func, data) {
    var fclick = func ? func : data.click, jqbb = jqb.dialog('option', 'buttons');

    if (typeof fclick == 'function') {
      jqbb[data.title] = function () {
        $(this).dialog('close');
        fclick.call(this);
      };
    }
    else {
      jqbb[data.title] = function () {
        $(this).dialog('close');
      }
    }
    jqb.dialog('option', 'buttons', jqbb);
  } // attach
};  // boxes

/**
* tooltip
* 1. allow 1 level drop down menu (divtip)
* 2. allow tooltip trigger icon to be displayed next to input (this has been moved to jutils.ui.enableToolTitle) !!
*/
var tooltip = {
  id: '#ims-ui-divtip',
  cssClass: 'ims-ui-divtip',
  evtns: '.ims-ui-divtip',
  options: { position: 'absolute', display: 'block' },
  initialized: false,
  to: null,
  todelay: 0,
  todobj: null,

  /** setup divtip 
  * @param obj: tooltip trigger
  * @param div: tooltip container element (a jquery selector/object OR a parameterless function which return a jquery selector/object)
  * @param opt: tooltip options
  */
  setup: function (obj, div, opts) {
    var jo = jutils.getJqueryObj(obj);
    if (jo != null && div != null) {
      if (tooltip.to == null) {
        tooltip.to = $('<div></div>').attr('id', tooltip.id.replace('#', '')).hide().appendTo('body');
      }
      jo.data('ims-ui-divtip-sel', typeof div == 'function' ? div : jutils.getJqueryObj(div).selector);
      jo.bind('mouseenter' + tooltip.evtns, function (evt) {
        tooltip.to.data('ims-ui-divtip-trigger', $(this));
        tooltip.show($(this), opts);
      });
      jo.bind('mouseleave' + tooltip.evtns, function (evt) {
        tooltip.hide($(this), opts);
      });

      if (!tooltip.initialized) {
        tooltip.to.mouseenter(function (evt) {
          $(this).data('ims-ui-divtip-trigger').trigger('mouseenter');
          $(this).css('display', 'block');
        });
        tooltip.to.mouseleave(function (evt) {
          $(this).data('ims-ui-divtip-trigger').trigger('mouseleave');
          $(this).css('display', 'none');
        });
        tooltip.initialized = true;
      }
    }
  },

  show: function (obj, opts) {
    var jsel = obj.data('ims-ui-divtip-sel');
    clearTimeout(tooltip.todobj);
    obj.addClass('ims-ui-divtip-trigger');
    var tobj = typeof jsel;
    if (tobj == 'string' || tobj == 'function') {
      jsel = tobj == 'function' ? jsel.call() : $(jsel);
    }
    else if (tobj == 'object' && obj.selector != null) {
      jsel = $(jsel);
    }
    else {
      jsel = null;
    }
    tooltip.content(jsel);
    tooltip.position(obj, opts);
    if ((jsel != null && jsel.size() == 0) || $.trim(jsel.html()) == '') {
      tooltip.hide(obj, opts);
    }
  },

  hide: function (obj, opts) {
    obj.removeClass('ims-ui-divtip-trigger');
    tooltip.todobj = setTimeout("tooltip.to.css('display', 'none')", opts && opts.delay ? opts.delay : tooltip.todelay);
  },

  content: function (jsel) {
    tooltip.to.html('');
    if (jsel != null) {
      if (jsel.size() == 1) {
        tooltip.to.html(jsel.html());
      }
      else if (jsel.size() > 1) {
        jsel.each(function () {
          tooltip.to.html(tooltip.to.html() + $(this).html());
        });
      }
      else {
        tooltip.to.html(jsel.html());
      }
    }
  },

  position: function (obj, opts) {
    tooltip.to.addClass(tooltip.cssClass);
    var jo = jutils.getJqueryObj(obj);
    var toblw = tooltip.to.css('borderLeftWidth').replace('px', '');
    toblw = toblw == 'medium' ? 0 : toblw;
    if (jo != null) {
      tooltip.options['top'] = jo.offset().top + jo.height() + (opts && opts.offset && opts.offset[0] ? opts.offset[0] : 0);
      tooltip.options['left'] = jo.offset().left - toblw + (opts && opts.offset && opts.offset[1] ? opts.offset[1] : 0);
      tooltip.options['minWidth'] = jo.width();
      tooltip.options['left'] -= opts && opts.align && opts.align['right'] ? tooltip.to.outerWidth() - jo.width() + (opts && opts.offset && opts.offset[1] ? 2 * opts.offset[1] : 0) : 0;
      $.extend(true, tooltip.options, opts);
      tooltip.to.css(tooltip.options);
    }
  }
};   // divtip + tooltip

var vote = {
  cssClass: 'ims-ui-vote',
  img: null, // for vote icon
  options: { tip: '#ims-ui-vote', position: ['top', 'center'], offset: [-5, 70], delay: 100 },

  /*
  * @param votes = { uvote:,  ucomment:, sum:, avg:, votes: [{ type:, count: }, {}, ...] }
  */
  setup: function (jo, votes) {
    var jro = typeof (votes) == 'string' ? $.parseJSON(votes) : votes;
    if (jro == null || jro == '') { return; }
    jo.tooltip(vote.options);
    $('#' + vote.cssClass + '-header span.sum, #' + vote.cssClass + '-avg span.sum').html(jro.sum);
    $('#' + vote.cssClass + '-content').html('');
    for (var i = 0; i < jro.votes.length; i++) {
      vote.setVote(jro.votes[i].type, jro.votes[i].count, _isInteger(jro.sum) ? Math.ceil(100.0 * jro.votes[i].count / parseInt(jro.sum)) : 0);
    }
  },

  // vote icon to each vote type
  getImg: function (type) {
    //    vote.img = vote.img == null ? jutils.ui.getImgSrc(vote.cssClass + '-icon' + type) : vote.img;
    vote.img = vote.img == null ? jutils.ui.getImg(vote.cssClass + '-icon') : vote.img;
    return vote.img.clone();
  },

  setVote: function (type, count, ratio) {
    var vcdiv = $('<div></div>').addClass('ims-ui-vote-item');
    vcdiv.append(vote.getVoteIcon(type));
    vcdiv.append(vote.getVoteBar(ratio));
    vcdiv.append(vote.getVoteNumber(count));
    $('#' + vote.cssClass + '-content').append(vcdiv);
  },
  getVoteIcon: function (count) {
    //    return $('<div></div>').addClass('ims-ui-vote-typ').append(vote.getImg(count));
    var div = $('<div></div>').addClass('ims-ui-vote-typ');
    for (var i = 0; i < count; i++) {
      div.append(vote.getImg(count));
    }
    return div;
  },
  getVoteBar: function (ratio) {
    return $('<div></div>').addClass('ims-ui-vote-bar').append($('<div/>').addClass('ims-ui-vote-barv').css('width', ratio + '%'));
  },
  getVoteNumber: function (count) {
    return $('<div></div>').addClass('ims-ui-vote-count').append($('<span></span>').html(count));
  }
}; // vote

/* extension calls */
/* this is call after the page has loaded, which includes this script */
(function ($) {

  $.extend({
    /* ASP.NET id extension */
    aspid: aspids.proxy,

    /* customized message boxes extensions */
    msgbox: {
      alert: function (msg, title, ok) {
        var b = $.aspid(boxes.ids.alert);
        if(b == null) { alert(msg); return; }
        boxes.attach(b, ok, b.data('ok'));
        boxes.showbox(b, msg, title);
      },
      debug: function (msg, title) {
        if (typeof msg == 'boolean') {
          boxes.debug = msg;
          return;
        }
        else {
          if (boxes.debug == true) {
            alert(msg);
          }
        }
        return $.aspid(boxes.ids.alert);
      },
      info: function (msg, title) {
        var b = $.aspid(boxes.ids.info);
        if(b == null) { alert(msg); return; }
        boxes.showbox(b, msg, title);
      },
      confirm: function (msg, title, ok, cancel) {
        var b = $.aspid(boxes.ids.confirm);
        if(b == null) { alert(msg); return; }
        b.dialog('option', 'buttons', {});
        b.dialog('option', 'closeOnEscape', false);
        b.one('dialogopen.hcb', function (evt, ui) { b.prev().find('.ui-dialog-titlebar-close').hide(); });
        boxes.confirm.init(msg, title, ok, cancel);
      },
      load: function (msg, title) {
        var b = $.aspid(boxes.ids.load);
        if(b == null) { return; }
        if (msg == '#close') { // !!
          b.dialog('close');
          return;
        }
        var imgdiv = $('<span></span>').addClass('loadimg');
        var span = $('<span></span>').html(msg.replace(/(\r|\n|\r\n)/g, '<br/>')).addClass('loadmsg');
        var div = $('<div></div>').append(imgdiv).append(span);
        b.dialog('option', 'closeOnEscape', false);
        boxes.showbox(b, div.html(), title);
      },
      prompt: function (msg, title, ok) {
        var b = $.aspid(boxes.ids.prompt);
        if(b == null) { alert(msg); return; }
        boxes.prompt.init(msg, title, ok);
      }
    } // msgbox
  }); // extend

  /** divtip - 
  * use the given element as tooltip of the caller (trigger); opts are css attributes for positioning and sizing the divtip ... 
  * @param obj tooltip container element
  */

  $.fn.divtip = function (obj, opts) {
    return this.each(function () {
      var $this = $(this);
      tooltip.setup($this, obj, opts);
    });
  }; // divtip

  /* vote - show the rating overview as tooltip of the caller (trigger) */
  $.fn.vote = function (votes) {
    return this.each(function () {
      var $this = $(this);
      vote.setup($this, votes);
    });
  }; // vote
})(jQuery);
/* jquery extensions - END */

/* jquery based element attribut extensions - BEGIN */
// see jutils.ui.enable*()
/* jquery based element attribut extensions - END */
