<commit_before>/*
* MultiSelect v0.9
* Copyright (c) 2012 Louis Cuny
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/

!function ($) {

  "use strict"; // jshint ;_;

 /* MULTISELECT CLASS DEFINITION
  * ====================== */

  var MultiSelect = function (element, options) {
    this.options = options;
    this.$element = $(element);
    this.$container = $('

');
    this.$selectableContainer = $('

');
    this.$selectionContainer = $('

');
    this.$selectableUl = $('

');
    this.$selectionUl = $('

');
  }

  MultiSelect.prototype = {
    constructor: MultiSelect,

    init: function(){
      var that = this,
          ms = this.$element;

      if (ms.next('.ms-container').length == 0){
        ms.css({ position: 'absolute', left: '-9999px' });
        ms.attr('id', ms.attr('id') ? ms.attr('id') : 'ms-'+Math.ceil(Math.random()*1000));

        var optgroupLabel = null,
            optgroupId = null,
            optgroupCpt = 0,
            scroll = 0;

        ms.find('optgroup, option').each(function(){
          if ($(this).is('optgroup')){
            optgroupLabel = $(this).attr('label');
            optgroupId = 'ms-'+ms.attr('id')+'-optgroup-'+optgroupCpt;

            that.$selectableUl.append(
              '
\

\

'+optgroupLabel+'
\

\

');
            that.$selectionUl.append(
              '
\

\

'+optgroupLabel+'
\

\

');
            optgroupCpt++;
          } else {

            var attributes = "";

            for (var cpt = 0; cpt < this.attributes.length; cpt++){
              var attr = this.attributes[cpt];

              attributes += attr.name+'="'+attr.value+'" ';
            }
            var selectableLi = $('
'+$(this).text()+'
'),
                selectedLi = selectableLi.clone();

            var value = $(this).val();
            selectableLi.addClass('ms-elem-selectable').attr('id', value+'-selectable');
            selectedLi.addClass('ms-elem-selection').attr('id', value+'-selection').hide();

            that.$selectionUl.find('.ms-optgroup-label').hide();

            if ($(this).prop('disabled') || ms.prop('disabled')){
              selectableLi.prop('disabled', true);
              selectableLi.addClass(that.options.disabledClass);
            }

            if (optgroupId){
              that.$selectableUl.children('#'+optgroupId+'-selectable').find('ul').first().append(selectableLi);
              that.$selectionUl.children('#'+optgroupId+'-selection').find('ul').first().append(selectedLi);
            } else {
              that.$selectableUl.append(selectableLi);
              that.$selectionUl.append(selectedLi);
            }
          }
        });

        if (that.options.selectableHeader){
          that.$selectableContainer.append(that.options.selectableHeader);
        }
        that.$selectableContainer.append(that.$selectableUl);

        if (that.options.selectionHeader){
          that.$selectionContainer.append(that.options.selectionHeader);
        }
        this.$selectionContainer.append(that.$selectionUl);

        that.$container.append(that.$selectableContainer);
        that.$container.append(that.$selectionContainer);
        ms.after(that.$container);
        that.$selectableUl.on('mouseenter', '.ms-elem-selectable', function(){
          $('li', that.$container).removeClass('ms-hover');
          $(this).addClass('ms-hover');
        }).on('mouseleave', function(){
          $('li', that.$container).removeClass('ms-hover');
        });

        if(that.options.dblClick) {
          that.$selectableUl.on('dblclick', '.ms-elem-selectable', function(){
            that.select($(this).attr('id').replace(/-selectable/, ''));
          });
          that.$selectionUl.on('dblclick', '.ms-elem-selection', function(){
            that.deselect($(this).attr('id').replace(/-selection/, ''));
          });
        } else {
          that.$selectableUl.on('click', '.ms-elem-selectable', function(){
            that.select($(this).attr('id').replace(/-selectable/, ''));
          });
          that.$selectionUl.on('click', '.ms-elem-selection', function(){
            that.deselect($(this).attr('id').replace(/-selection/, ''));
          });
        }

        that.$selectionUl.on('mouseenter', '.ms-elem-selection', function(){
          $('li', that.$selectionUl).removeClass('ms-hover');
          $(this).addClass('ms-hover');
        }).on('mouseleave', function(){
          $('li', that.$selectionUl).removeClass('ms-hover');
        });

        that.$selectableUl.on('focusin', function(){
          $(this).addClass('ms-focus');
          that.$selectionUl.focusout();
        }).on('focusout', function(){
          $(this).removeClass('ms-focus');
          $('li', that.$container).removeClass('ms-hover');
        });

        that.$selectionUl.on('focusin', function(){
          $(this).addClass('ms-focus');
        }).on('focusout', function(){
          $(this).removeClass('ms-focus');
          $('li', that.$container).removeClass('ms-hover');
        });

        ms.on('focusin', function(){
          that.$selectableUl.focus();
        }).on('focusout', function(){
          that.$selectableUl.removeClass('ms-focus');
          that.$selectionUl.removeClass('ms-focus');
        });

        ms.onKeyDown = function(e, keyContainer){
          var selectables = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', that.$container),
              selectablesLength = selectables.length,
              selectableFocused = $('.'+keyContainer+' li.ms-hover', that.$container),
              selectableFocusedIndex = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', this.$container).index(selectableFocused),
              liHeight = selectables.first().outerHeight(),
              numberOfItemsDisplayed = Math.ceil(that.$container.outerHeight()/liHeight),
              scrollStart = Math.ceil(numberOfItemsDisplayed/4);

          selectables.removeClass('ms-hover');
          if (e.keyCode == 32){ // space
            var method = keyContainer == 'ms-selectable' ? 'select' : 'deselect';
            if (keyContainer == 'ms-selectable'){
              that.select(selectableFocused.first().attr('id').replace('-selectable', ''));
            } else {
              that.deselect(selectableFocused.first().attr('id').replace('-selection', ''));
            }

          } else if (e.keyCode == 40){ // Down
            var nextIndex = (selectableFocusedIndex+1 != selectablesLength) ? selectableFocusedIndex+1 : 0,
                nextSelectableLi = selectables.eq(nextIndex);

            nextSelectableLi.addClass('ms-hover');
            if (nextIndex > scrollStart){
              scroll += liHeight;
            } else if (nextIndex == 0){
              scroll = 0;
            }
            $('.'+keyContainer+' ul', that.$container).scrollTop(scroll);
          } else if (e.keyCode == 38){ // Up
            var prevIndex = (selectableFocusedIndex-1 >= 0) ? selectableFocusedIndex-1 : selectablesLength-1,
                prevSelectableLi = selectables.eq(prevIndex);
            selectables.removeClass('ms-hover');
            prevSelectableLi.addClass('ms-hover');
            if (selectablesLength-prevIndex+1 < scrollStart){
              scroll = liHeight*(selectablesLength-scrollStart);
            } else {
              scroll -= liHeight;
            }
            $('.'+keyContainer+' ul', that.$container).scrollTop(scroll);
          } else if (e.keyCode == 37 || e.keyCode == 39){ // Right and Left
            if (that.$selectableUl.hasClass('ms-focus')){
              that.$selectableUl.focusout();
              that.$selectionUl.focusin();
            } else {
              that.$selectableUl.focusin();
              that.$selectionUl.focusout();
            }
          }
        }

        ms.on('keydown', function(e){
          if (ms.is(':focus')){
            var keyContainer = that.$selectableUl.hasClass('ms-focus') ? 'ms-selectable' : 'ms-selection';
            ms.onKeyDown(e, keyContainer);
          }
        });
      }

      var selectedValues = ms.find('option:selected').map(function(){ return $(this).val() }).get();
      that.select(selectedValues, 'init')

      if (typeof that.options.afterInit == 'function') {
        that.options.afterInit.call(this, this.$container);
      }
    },
    'refresh' : function() {
      $("#ms-"+this.$element.attr("id")).remove();
      this.init(this.options);
    },
    'select' : function(value, method){
      if (typeof value == 'string')
        value = [value]
      var that = this,
          ms = this.$element,
          selectables = this.$selectableUl.find('#' + value.join('-selectable, #')+'-selectable').filter(':not(.'+that.options.disabledClass+')'),
          selections = this.$selectionUl.find('#' + value.join('-selection, #') + '-selection'),
          options = ms.find('option:val('+value +')');

      if (selectables.length > 0){
        selectables.addClass('ms-selected').hide();
        selections.addClass('ms-selected').show();
        options.prop('selected', true);

        var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
        if (selectableOptgroups.length > 0){
          selectableOptgroups.each(function(){
            var selectablesLi = $(this).find('.ms-elem-selectable');
            if (selectablesLi.length == selectablesLi.filter('.ms-selected').length){
              $(this).find('.ms-optgroup-label').hide();
            }
          });

          var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
          selectionOptgroups.each(function(){
            var selectionsLi = $(this).find('.ms-elem-selection');
            if (selectionsLi.filter('.ms-selected').length > 0){
              $(this).find('.ms-optgroup-label').show();
            }
          });
        }
        if (method != 'init'){
          ms.focus();
          ms.trigger('change');
          if (typeof that.options.afterSelect == 'function') {
            that.options.afterSelect.call(this, value);
          }
        }
      }
    },
    'deselect' : function(value){
      if (typeof value == 'string')
        value = [value]
      var that = this,
          ms = this.$element,
          selectables = this.$selectableUl.find('#' + value.join('-selectable, #')+'-selectable'),
          selections = this.$selectionUl.find('#' + value.join('-selection, #')+'-selection').filter('.ms-selected'),
          options = ms.find('option:val('+value +')');

      if (selections.length > 0){
        selectables.removeClass('ms-selected').show();
        selections.removeClass('ms-selected').hide();
        options.prop('selected', false);

        var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
        if (selectableOptgroups.length > 0){
          selectableOptgroups.each(function(){
            var selectablesLi = $(this).find('.ms-elem-selectable');
            if (selectablesLi.filter(':not(.ms-selected)').length > 0){
              $(this).find('.ms-optgroup-label').show();
            }
          });

          var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
          selectionOptgroups.each(function(){
            var selectionsLi = $(this).find('.ms-elem-selection');
            if (selectionsLi.filter('.ms-selected').length == 0){
              $(this).find('.ms-optgroup-label').hide();
            }
          });
        }
        this.$selectionUl.focusin();
        ms.trigger('change');
        if (typeof that.options.afterDeselect == 'function') {
          that.options.afterDeselect.call(this, value);
        }
      }
    },
    'select_all' : function(){
      var ms = this.$element;

      ms.find('option').prop('selected', true);
      this.$selectableUl.find('.ms-elem-selectable').addClass('ms-selected').hide();
      this.$selectionUl.find('.ms-optgroup-label').show();
      this.$selectableUl.find('.ms-optgroup-label').hide();
      this.$selectionUl.find('.ms-elem-selection').addClass('ms-selected').show();
      this.$selectionUl.focusin();
      ms.trigger('change');
    },
    'deselect_all' : function(){
      var ms = this.$element;

      ms.find('option').prop('selected', false);
      this.$selectableUl.find('.ms-elem-selectable').removeClass('ms-selected').show();
      this.$selectionUl.find('.ms-optgroup-label').hide();
      this.$selectableUl.find('.ms-optgroup-label').show();
      this.$selectionUl.find('.ms-elem-selection').removeClass('ms-selected').hide();
      ms.focus();
      ms.trigger('change');
    }
  }

  /* MULTISELECT PLUGIN DEFINITION
   * ======================= */

  $.fn.multiSelect = function () {
    var option = arguments[0],
        args = arguments;

    return this.each(function () {
      var $this = $(this),
          data = $this.data('multiselect'),
          options = $.extend({}, $.fn.multiSelect.defaults, $this.data(), typeof option == 'object' && option);

      if (!data) $this.data('multiselect', (data = new MultiSelect(this, options)))

      if (typeof option == 'string'){
        data[option](args[1])
      } else {
        data.init();
      }
    })
  }

  $.fn.multiSelect.defaults = {
    disabledClass : 'disabled',
    dblClick : false
  }

  $.fn.multiSelect.Constructor = MultiSelect

  $.extend($.expr[':'], {
     val: function(elem, i, attr) {
       return elem.value === attr[3];
     }
  });

}(window.jQuery);<commit_msg>improve keyboard behaviour<commit_after>/*
* MultiSelect v0.9
* Copyright (c) 2012 Louis Cuny
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/

!function ($) {

  "use strict"; // jshint ;_;

 /* MULTISELECT CLASS DEFINITION
  * ====================== */

  var MultiSelect = function (element, options) {
    this.options = options;
    this.$element = $(element);
    this.$container = $('

');
    this.$selectableContainer = $('

');
    this.$selectionContainer = $('

');
    this.$selectableUl = $('

');
    this.$selectionUl = $('

');
    this.scrollTo = 0;
  }

  MultiSelect.prototype = {
    constructor: MultiSelect,

    init: function(){
      var that = this,
          ms = this.$element;

      if (ms.next('.ms-container').length == 0){
        ms.css({ position: 'absolute', left: '-9999px' });
        ms.attr('id', ms.attr('id') ? ms.attr('id') : 'ms-'+Math.ceil(Math.random()*1000));

        var optgroupLabel = null,
            optgroupId = null,
            optgroupCpt = 0,
            scroll = 0;

        ms.find('optgroup, option').each(function(){
          if ($(this).is('optgroup')){
            optgroupLabel = $(this).attr('label');
            optgroupId = 'ms-'+ms.attr('id')+'-optgroup-'+optgroupCpt;

            that.$selectableUl.append(
              '
\

\

'+optgroupLabel+'
\

\

');
            that.$selectionUl.append(
              '
\

\

'+optgroupLabel+'
\

\

');
            optgroupCpt++;
          } else {

            var attributes = "";

            for (var cpt = 0; cpt < this.attributes.length; cpt++){
              var attr = this.attributes[cpt];

              attributes += attr.name+'="'+attr.value+'" ';
            }
            var selectableLi = $('
'+$(this).text()+'
'),
                selectedLi = selectableLi.clone();

            var value = $(this).val();
            selectableLi.addClass('ms-elem-selectable').attr('id', value+'-selectable');
            selectedLi.addClass('ms-elem-selection').attr('id', value+'-selection').hide();

            that.$selectionUl.find('.ms-optgroup-label').hide();

            if ($(this).prop('disabled') || ms.prop('disabled')){
              selectableLi.prop('disabled', true);
              selectableLi.addClass(that.options.disabledClass);
            }

            if (optgroupId){
              that.$selectableUl.children('#'+optgroupId+'-selectable').find('ul').first().append(selectableLi);
              that.$selectionUl.children('#'+optgroupId+'-selection').find('ul').first().append(selectedLi);
            } else {
              that.$selectableUl.append(selectableLi);
              that.$selectionUl.append(selectedLi);
            }
          }
        });

        if (that.options.selectableHeader){
          that.$selectableContainer.append(that.options.selectableHeader);
        }
        that.$selectableContainer.append(that.$selectableUl);

        if (that.options.selectionHeader){
          that.$selectionContainer.append(that.options.selectionHeader);
        }
        this.$selectionContainer.append(that.$selectionUl);

        that.$container.append(that.$selectableContainer);
        that.$container.append(that.$selectionContainer);
        ms.after(that.$container);
        that.$selectableUl.on('mouseenter', '.ms-elem-selectable', function(){
          $('li', that.$container).removeClass('ms-hover');
          $(this).addClass('ms-hover');
        }).on('mouseleave', function(){
          $('li', that.$container).removeClass('ms-hover');
        });

        if(that.options.dblClick) {
          that.$selectableUl.on('dblclick', '.ms-elem-selectable', function(){
            that.select($(this).attr('id').replace(/-selectable/, ''));
          });
          that.$selectionUl.on('dblclick', '.ms-elem-selection', function(){
            that.deselect($(this).attr('id').replace(/-selection/, ''));
          });
        } else {
          that.$selectableUl.on('click', '.ms-elem-selectable', function(){
            that.select($(this).attr('id').replace(/-selectable/, ''));
          });
          that.$selectionUl.on('click', '.ms-elem-selection', function(){
            that.deselect($(this).attr('id').replace(/-selection/, ''));
          });
        }

        that.$selectionUl.on('mouseenter', '.ms-elem-selection', function(){
          $('li', that.$selectionUl).removeClass('ms-hover');
          $(this).addClass('ms-hover');
        }).on('mouseleave', function(){
          $('li', that.$selectionUl).removeClass('ms-hover');
        });

        that.$selectableUl.on('focusin', function(){
          $(this).addClass('ms-focus');
          that.$selectionUl.focusout();
        }).on('focusout', function(){
          $(this).removeClass('ms-focus');
          $('li', that.$container).removeClass('ms-hover');
        });

        that.$selectionUl.on('focusin', function(){
          $(this).addClass('ms-focus');
        }).on('focusout', function(){
          $(this).removeClass('ms-focus');
          $('li', that.$container).removeClass('ms-hover');
        });

        ms.on('focusin', function(){
          ms.focusout();
          that.$selectableUl.focusin();
        }).on('focusout', function(){
          that.$selectableUl.removeClass('ms-focus');
          that.$selectionUl.removeClass('ms-focus');
        });

        ms.onKeyDown = function(e, keyContainer){
          var ul = that.$container.find('.'+keyContainer).find('.ms-list'),
              lis = ul.find('li:visible:not(.ms-optgroup-label, .ms-optgroup-container)'),
              lisNumber = lis.length,
              liFocused = ul.find('li.ms-hover'),
              liFocusedIndex = liFocused.length > 0 ? lis.index(liFocused) : -1,
              ulHeight = ul.innerHeight(),
              liHeight = lis.first().outerHeight(true),
              numberOfLisDisplayed = Math.floor(ulHeight / liHeight);

          if (e.keyCode == 32){ // space
            if (liFocused.length >0){
              var method = keyContainer == 'ms-selectable' ? 'select' : 'deselect';
              if (keyContainer == 'ms-selectable'){
                that.select(liFocused.attr('id').replace('-selectable', ''));
              } else {
                that.deselect(liFocused.attr('id').replace('-selection', ''));
              }
              lis.removeClass('ms-hover');
              that.scrollTo = 0;
              ul.scrollTop(that.scrollTo);
            }
          } else if (e.keyCode == 40){ // Down
            if (lis.length > 0){
              var nextLiIndex = liFocusedIndex+1,
                  nextLi = (lisNumber != nextLiIndex) ? lis.eq(nextLiIndex) : lis.first(),
                  ulPosition = ul.position().top,
                  nextLiPosition = nextLi.position().top;

              lis.removeClass('ms-hover');
              nextLi.addClass('ms-hover');

              if (lisNumber == nextLiIndex){
                that.scrollTo = 0;
              } else if (nextLiPosition >= (ulPosition + (numberOfLisDisplayed * liHeight))){
                that.scrollTo += liHeight;
              }
              ul.scrollTop(that.scrollTo);
            }
          } else if (e.keyCode == 38){ // Up
            if (lis.length > 0){
              var prevLiIndex = Math.max(liFocusedIndex-1, -1),
                  prevLi = lis.eq(prevLiIndex),
                  ulPosition = ul.position().top,
                  prevLiPosition = prevLi.position().top;

              lis.removeClass('ms-hover');
              prevLi.addClass('ms-hover');
              if (prevLiPosition <= ulPosition){
                that.scrollTo -= liHeight;
              } else if (prevLiIndex < 0){
                that.scrollTo = (lisNumber - numberOfLisDisplayed) * liHeight;
              }
              ul.scrollTop(that.scrollTo);
            }
          } else if (e.keyCode == 37 || e.keyCode == 39){
            if (that.$selectableUl.hasClass('ms-focus')){
              that.$selectableUl.focusout();
              that.$selectionUl.focusin();
            } else {
              that.$selectableUl.focusin();
              that.$selectionUl.focusout();
            }
          }
        }

        ms.on('keydown', function(e){
          if (ms.is(':focus')){
            var keyContainer = that.$selectableUl.hasClass('ms-focus') ? 'ms-selectable' : 'ms-selection';
            ms.onKeyDown(e, keyContainer);
          }
        });
      }

      var selectedValues = ms.find('option:selected').map(function(){ return $(this).val() }).get();
      that.select(selectedValues, 'init')

      if (typeof that.options.afterInit == 'function') {
        that.options.afterInit.call(this, this.$container);
      }
    },
    'refresh' : function() {
      $("#ms-"+this.$element.attr("id")).remove();
      this.init(this.options);
    },
    'select' : function(value, method){
      if (typeof value == 'string')
        value = [value]
      var that = this,
          ms = this.$element,
          selectables = this.$selectableUl.find('#' + value.join('-selectable, #')+'-selectable').filter(':not(.'+that.options.disabledClass+')'),
          selections = this.$selectionUl.find('#' + value.join('-selection, #') + '-selection'),
          options = ms.find('option:val('+value +')');

      if (selectables.length > 0){
        selectables.addClass('ms-selected').hide();
        selections.addClass('ms-selected').show();
        options.prop('selected', true);

        var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
        if (selectableOptgroups.length > 0){
          selectableOptgroups.each(function(){
            var selectablesLi = $(this).find('.ms-elem-selectable');
            if (selectablesLi.length == selectablesLi.filter('.ms-selected').length){
              $(this).find('.ms-optgroup-label').hide();
            }
          });

          var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
          selectionOptgroups.each(function(){
            var selectionsLi = $(this).find('.ms-elem-selection');
            if (selectionsLi.filter('.ms-selected').length > 0){
              $(this).find('.ms-optgroup-label').show();
            }
          });
        }
        if (method != 'init'){
          that.$selectionUl.focusout();
          that.$selectableUl.focusin();
          ms.trigger('change');
          if (typeof that.options.afterSelect == 'function') {
            that.options.afterSelect.call(this, value);
          }
        }
      }
    },
    'deselect' : function(value){
      if (typeof value == 'string')
        value = [value]
      var that = this,
          ms = this.$element,
          selectables = this.$selectableUl.find('#' + value.join('-selectable, #')+'-selectable'),
          selections = this.$selectionUl.find('#' + value.join('-selection, #')+'-selection').filter('.ms-selected'),
          options = ms.find('option:val('+value +')');

      if (selections.length > 0){
        selectables.removeClass('ms-selected').show();
        selections.removeClass('ms-selected').hide();
        options.prop('selected', false);

        var selectableOptgroups = that.$selectableUl.children('.ms-optgroup-container');
        if (selectableOptgroups.length > 0){
          selectableOptgroups.each(function(){
            var selectablesLi = $(this).find('.ms-elem-selectable');
            if (selectablesLi.filter(':not(.ms-selected)').length > 0){
              $(this).find('.ms-optgroup-label').show();
            }
          });

          var selectionOptgroups = that.$selectionUl.children('.ms-optgroup-container');
          selectionOptgroups.each(function(){
            var selectionsLi = $(this).find('.ms-elem-selection');
            if (selectionsLi.filter('.ms-selected').length == 0){
              $(this).find('.ms-optgroup-label').hide();
            }
          });
        }
        this.$selectableUl.focusout();
        this.$selectionUl.focusin();
        ms.trigger('change');
        if (typeof that.options.afterDeselect == 'function') {
          that.options.afterDeselect.call(this, value);
        }
      }
    },
    'select_all' : function(){
      var ms = this.$element;

      ms.find('option').prop('selected', true);
      this.$selectableUl.find('.ms-elem-selectable').addClass('ms-selected').hide();
      this.$selectionUl.find('.ms-optgroup-label').show();
      this.$selectableUl.find('.ms-optgroup-label').hide();
      this.$selectionUl.find('.ms-elem-selection').addClass('ms-selected').show();
      this.$selectionUl.focusin();
      this.$selectableUl.focusout();
      ms.trigger('change');
    },
    'deselect_all' : function(){
      var ms = this.$element;

      ms.find('option').prop('selected', false);
      this.$selectableUl.find('.ms-elem-selectable').removeClass('ms-selected').show();
      this.$selectionUl.find('.ms-optgroup-label').hide();
      this.$selectableUl.find('.ms-optgroup-label').show();
      this.$selectionUl.find('.ms-elem-selection').removeClass('ms-selected').hide();
      this.$selectableUl.focusin();
      this.$selectionUl.focusout();
      ms.trigger('change');
    }
  }

  /* MULTISELECT PLUGIN DEFINITION
   * ======================= */

  $.fn.multiSelect = function () {
    var option = arguments[0],
        args = arguments;

    return this.each(function () {
      var $this = $(this),
          data = $this.data('multiselect'),
          options = $.extend({}, $.fn.multiSelect.defaults, $this.data(), typeof option == 'object' && option);

      if (!data) $this.data('multiselect', (data = new MultiSelect(this, options)))

      if (typeof option == 'string'){
        data[option](args[1])
      } else {
        data.init();
      }
    })
  }

  $.fn.multiSelect.defaults = {
    disabledClass : 'disabled',
    dblClick : false
  }

  $.fn.multiSelect.Constructor = MultiSelect

  $.extend($.expr[':'], {
     val: function(elem, i, attr) {
       return elem.value === attr[3];
     }
  });

}(window.jQuery);<|endoftext|>


<commit_before>/*
 * The MIT License
 *
 * Copyright 2014 Markus Sommerfeld (markus@simpleasthat.eu).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

"use strict";

(function($) {

    /**
     *
     * @type Object
     */
    var defaults = {

        enableMessage : true,

        messagePosition : 'top', // or bottom

        messageMarkup : '

',

        successMessage : null,

        successClass : 'success',

        errorMessage : null,

        errorClass : 'error',

        beforeSubmit: function(form) {},

        afterSubmit: function(response, form) {},

        onError : function(form, jqXHR, textStatus, errorThrown){}
    };
    /**
     *
     * @param {opject} options
     * @param {jqueryObject} element
     * @returns {MyPluginName}
     */
    var SatAjaxForm = function(options, element) {

        this.options = options;

        this.element = element;

        this.init();

        return this;
    };

    /**
     * The Plugin Prototype
     */
    SatAjaxForm.prototype = {
        element: null,

        options: null,

        url : null,

        method : 'post',

        dataType : 'json',

        formDataArray : null,

        formDataString : null,

        init : function(){
            // is form valid for ajax submit
            if(this.checkForm()){
                this.extractProperties();
                this.disableDefaultBehavior();
                this.injectMessageBox();
            }

            // not valid, show message
            else{
                var mBox = $(this.options.messageMarkup);
                mBox.addClass('sat_ajaxform message_box');
                mBox.addClass(this.options.errorClass);
                mBox.html(this.options.errorMessage);
                this.element.prepend(mBox);
            }
        },

        checkForm : function(){
            // check is upload form
            if (typeof this.element.attr('enctype') !== 'undefined' && this.element.attr('enctype') === 'multipart/form-data') {
                this.options.errorMessage = 'Ups!
The form seems to be a file upload form.
The plugin doesn\'t support this type of form';
                return false;
            }

            if (this.element.find('input[type=file]').length) {
                this.options.errorMessage = 'Ups!
Found one or more upload fields, the plugin doesn\'t support file upload.';
                return false;
            }

            return true;
        },

        extractProperties : function(){

            // set the submit url
            this.url    = this.element.attr('action');

            // set the submit method
            this.method = this.element.attr('method');

            this.dataType = this.element.attr('data-type');

        },

        /**
         *
         * @returns {void}
         */
        disableDefaultBehavior : function(){
            var submitBtn;

            // find input type=submit
            submitBtn = this.element.find('input[type="submit"]');

            // no input type, try to find button type=submit
            if(submitBtn.length === 0){
                submitBtn = this.element.find('button[type="submit"]');
            }

            submitBtn.bind('click', this, this.submit);

            // clear the action url
            this.element.attr('action', '#');
        },

        /**
         * Injects a message container to display Error or Success message
         *
         * @returns {void}
         */
        injectMessageBox : function(){
            if(this.options.enableMessage){
                var mBox = $(this.options.messageMarkup);
                mBox.addClass('sat_ajaxform message_box');
                mBox.css('display', 'none');
                switch(this.options.messagePosition){
                    case 'bottom':
                        this.element.append(mBox);
                        break;

                    case 'top':
                    default:
                        this.element.prepend(mBox);
                        break;
                }
            }
        },

        /**
         *
         * @param {type} event
         * @returns {Boolean} false to prevent default submit behavior
         */
        submit : function(event){
            var _this = event.data;

            _this.formDataArray     = _this.element.serializeArray();
            _this.formDataString    = _this.element.serialize();

            // trigger before submit event
            _this.options.beforeSubmit(_this);

            $.ajax({
                url         : _this.url,
                data        : _this.formDataString,
                dataType    : _this.dataType,
                type        : _this.method,

                error : function( jqXHR, textStatus, errorThrown ){
                    _this.onError(jqXHR, textStatus, errorThrown);

                },

                success : function(response, textStatus, jqXHR ){
                    _this.onSuccess(response, textStatus, jqXHR);
                }
            });

            return false;
        },

        /**
         *
         * @param {obj} jqXHR
         * @param {string} textStatus
         * @param {string} errorThrown
         * @returns {void}
         */
        onError : function(jqXHR, textStatus, errorThrown){
            // show error response, if messageBox is enabled
            var
                messageBox = this.element.find('.message_box'),
                message = (this.options.errorMessage) ? this.options.errorMessage : errorThrown;
            ;
            if(messageBox.length > 0){
                messageBox.show();
                messageBox.addClass(this.options.errorClass);
                messageBox.html(message);
            }

            // trigger onError Event
            this.options.onError(this, jqXHR, textStatus, errorThrown);
        },

        /**
         *
         * @param {mixed} response
         * @param {string} textStatus
         * @param {object} jqXHR
         * @returns {void}
         */
        onSuccess : function(response, textStatus, jqXHR){

            var
                messageBox = this.element.find('.message_box'),
                message = null
            ;

            // has message ?
            message = this.getMessage(response);

            if(messageBox.length > 0 && message){
                messageBox.show();
                messageBox.addClass(this.options.successClass);
                messageBox.html(message);
            }

            // trigger afterSubmit Event
            this.options.afterSubmit(response, this);
        },

        /**
         *
         * @param {mixed} response
         * @returns {Boolean|json.message|_L27.SatAjaxForm.options.successMessage|_L27.SatAjaxForm.options.errorMessage}
         */
        getMessage : function(response){
            var message;
            // is json
            switch(this.dataType){
                case 'json':
                    message = this.parseJSON(response);
                    break;

                case 'html':
                default:
                    message = response;
                break;
            }

            return message;
        },

        /**
         *
         * @param {JSON} json
         * @returns {json.message|_L27.SatAjaxForm.options.successMessage|_L27.SatAjaxForm.options.errorMessage|Boolean}
         */
        parseJSON : function(json){

            var message;

            // success = false
            if(!json.success){
                message = (typeof json.message !== "undefined" ) ? json.message : this.options.errorMessage;
                this.onError(json, 'error', message);
                return false;
            }

            // success = true
            if(json.success){
                message = (typeof json.message !== "undefined" ) ? json.message : this.options.successMessage;
                return message;
            }
        },

        /**
         *
         * @returns {_L27.SatAjaxForm.element.selector}
         */
        getSelector: function() {
            return this.element.selector;
        }
    };

    /**
     *
     * @param {type} options
     * @returns {undefined}
     */
    $.fn.satAjaxForm = function(options) {
        var options;
        options = $.extend({}, $.fn.satAjaxForm.defaults, options);

        this.each(function() {
            new SatAjaxForm(options, $(this));
        });
    };

    /**
     *
     */
    $.fn.satAjaxForm.defaults = defaults;

})(jQuery);

<commit_msg>code cleanup, and some comments added<commit_after>/*
 * The MIT License
 *
 * Copyright 2014 Markus Sommerfeld (markus@simpleasthat.eu).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

"use strict";

(function($) {

    /**
     * The defaults
     * @type Object
     */
    var defaults = {

        enableMessage : true,

        messagePosition : 'top', // or bottom

        messageMarkup : '

',

        successMessage : null,

        successClass : 'success',

        errorMessage : null,

        errorClass : 'error',

        beforeSubmit: function(form) {},

        afterSubmit: function(response, form) {},

        onError : function(form, jqXHR, textStatus, errorThrown){}
    };

    /**
     *
     * @param {opject} options
     * @param {jqueryObject} element
     * @returns {MyPluginName}
     */
    var SatAjaxForm = function(options, element) {

        this.options = options;

        this.element = element;

        this.init();

        return this;
    };

    /**
     * The Plugin Prototype
     */
    SatAjaxForm.prototype = {
        element: null,

        options: null,

        url : null,

        method : 'post',

        dataType : 'json',

        formDataArray : null,

        formDataString : null,

        init : function(){
            // is form valid for ajax submit
            if(this.checkForm()){
                this.extractProperties();
                this.disableDefaultBehavior();
                this.injectMessageBox();
            }

            // not valid, show message
            else{
                var mBox = $(this.options.messageMarkup);
                mBox.addClass('sat_ajaxform message_box');
                mBox.addClass(this.options.errorClass);
                mBox.html(this.options.errorMessage);
                this.element.prepend(mBox);
            }
        },

        /**
         * Checks the form, for file upload fields
         * FileReader isn't yet implemented
         *
         * @returns {Boolean}
         */
        checkForm : function(){
            // Check for multipart/form-data
            if (typeof this.element.attr('enctype') !== 'undefined' && this.element.attr('enctype') === 'multipart/form-data') {
                this.options.errorMessage = 'Ups!
The form seems to be a file upload form.
The plugin doesn\'t support this type of form';
                return false;
            }

            // Check for file input type
            if (this.element.find('input[type=file]').length) {
                this.options.errorMessage = 'Ups!
Found one or more upload fields, the plugin doesn\'t support file upload.';
                return false;
            }

            return true;
        },

        /**
         *
         * @returns {void}
         */
        extractProperties : function(){

            // extract and set the submit url
            this.url    = this.element.attr('action');

            // extract and set the submit method
            this.method = this.element.attr('method');

            // extract and set the ajax dataType
            this.dataType = this.element.attr('data-type');
        },

        /**
         *
         * @returns {void}
         */
        disableDefaultBehavior : function(){
            var submitBtn;

            // find input type=submit
            submitBtn = this.element.find('input[type="submit"]');

            // no input type, try to find button type=submit
            if(submitBtn.length === 0){
                submitBtn = this.element.find('button[type="submit"]');
            }

            submitBtn.bind('click', this, this.submit);

            // clear the action url
            this.element.attr('action', '#');
        },

        /**
         * Injects a message container to display Error or Success message
         *
         * @returns {void}
         */
        injectMessageBox : function(){
            if(this.options.enableMessage){
                var mBox = $(this.options.messageMarkup);
                mBox.addClass('sat_ajaxform message_box');
                mBox.css('display', 'none');
                switch(this.options.messagePosition){
                    case 'bottom':
                        this.element.append(mBox);
                        break;

                    case 'top':
                    default:
                        this.element.prepend(mBox);
                        break;
                }
            }
        },

        /**
         *
         * @param {object} event
         * @returns {Boolean} false to prevent default submit behavior
         */
        submit : function(event){
            var _this = event.data;

            _this.formDataArray     = _this.element.serializeArray();
            _this.formDataString    = _this.element.serialize();

            // trigger before submit event
            _this.options.beforeSubmit(_this);

            $.ajax({
                url         : _this.url,
                data        : _this.formDataString,
                dataType    : _this.dataType,
                type        : _this.method,

                error : function( jqXHR, textStatus, errorThrown ){
                    _this.onError(jqXHR, textStatus, errorThrown);

                },

                success : function(response, textStatus, jqXHR ){
                    _this.onSuccess(response, textStatus, jqXHR);
                }
            });

            return false;
        },

        /**
         *
         * @param {obj} jqXHR
         * @param {string} textStatus
         * @param {string} errorThrown
         * @returns {void}
         */
        onError : function(jqXHR, textStatus, errorThrown){
            // show error response, if messageBox is enabled
            var
                messageBox = this.element.find('.message_box'),
                message = (this.options.errorMessage) ? this.options.errorMessage : errorThrown;
            ;
            if(messageBox.length > 0){
                messageBox.show();
                messageBox.addClass(this.options.errorClass);
                messageBox.html(message);
            }

            // trigger onError Event
            this.options.onError(this, jqXHR, textStatus, errorThrown);
        },

        /**
         *
         * @param {mixed} response
         * @param {string} textStatus
         * @param {object} jqXHR
         * @returns {void}
         */
        onSuccess : function(response, textStatus, jqXHR){

            var
                messageBox = this.element.find('.message_box'),
                message = null
            ;

            // has message ?
            message = this.getMessage(response);

            if(messageBox.length > 0 && message){
                messageBox.show();
                messageBox.addClass(this.options.successClass);
                messageBox.html(message);
            }

            // trigger afterSubmit Event
            this.options.afterSubmit(response, this);
        },

        /**
         *
         * @param {mixed} response
         * @returns {Boolean|json.message|_L27.SatAjaxForm.options.successMessage|_L27.SatAjaxForm.options.errorMessage}
         */
        getMessage : function(response){
            var message;
            // is json
            switch(this.dataType){
                case 'json':
                    message = this.parseJSON(response);
                    break;

                case 'html':
                default:
                    message = response;
                break;
            }

            return message;
        },

        /**
         *
         * @param {JSON} json
         * @returns {json.message|_L27.SatAjaxForm.options.successMessage|_L27.SatAjaxForm.options.errorMessage|Boolean}
         */
        parseJSON : function(json){

            var message;

            // success = false
            if(!json.success){
                message = (typeof json.message !== "undefined" ) ? json.message : this.options.errorMessage;
                this.onError(json, 'error', message);
                return false;
            }

            // success = true
            if(json.success){
                message = (typeof json.message !== "undefined" ) ? json.message : this.options.successMessage;
                return message;
            }
        },

        /**
         *
         * @returns {_L27.SatAjaxForm.element.selector}
         */
        getSelector: function() {
            return this.element.selector;
        }
    };

    /**
     *
     * @param {type} options
     * @returns {undefined}
     */
    $.fn.satAjaxForm = function(options) {
        var options;
        options = $.extend({}, $.fn.satAjaxForm.defaults, options);

        this.each(function() {
            new SatAjaxForm(options, $(this));
        });
    };

    /**
     *
     */
    $.fn.satAjaxForm.defaults = defaults;

})(jQuery);

<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * A layer state is used to construct layer information (and later, layers), and is a state machine
 * that layer strategies from each node modify. Iterating through all of the nodes in a depth-first
 * manner will modify the LayerBuilder so that layer information can be retrieved.
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var scenery = require( 'SCENERY/scenery' );
  require( 'SCENERY/layers/LayerBoundary' );
  require( 'SCENERY/util/Trail' );
  require( 'SCENERY/util/TrailPointer' );

  /*
   * Builds layer information between trails
   *
   * previousLayerType should be null if there is no previous layer.
   */
  scenery.LayerBuilder = function( scene, previousLayerType, previousPaintedTrail, nextPaintedTrail ) {

    /*---------------------------------------------------------------------------*
    * Initial state
    *----------------------------------------------------------------------------*/

    this.layerTypeStack = [];
    this.boundaries = [];
    this.pendingBoundary = new scenery.LayerBoundary();
    this.pendingBoundary.previousLayerType = previousLayerType;
    this.pendingBoundary.previousPaintedTrail = previousPaintedTrail;

    /*
     * The current layer type active, and whether it has been 'used' yet. A node with isPainted() will trigger a 'used' action,
     * and if the layer hasn't been used, it will actually trigger a boundary creation. We want to collapse 'unused' layers
     * and boundaries together, so that every created layer has a node that displays something.
     */
    this.currentLayerType = previousLayerType;
    this.layerChangePending = previousPaintedTrail === null;

    /*---------------------------------------------------------------------------*
    * Start / End pointers
    *----------------------------------------------------------------------------*/

    if ( previousPaintedTrail ) {
      // Move our start pointer just past the previousPaintedTrail, since our previousLayerType is presumably for that trail's node's self.
      // Anything after that self could have been collapsed, so we need to start there.
      this.startPointer = new scenery.TrailPointer( previousPaintedTrail.copy(), true );
      this.startPointer.nestedForwards();
    } else {
      this.startPointer = new scenery.TrailPointer( new scenery.Trail( scene ), true );
    }

    if ( nextPaintedTrail ) {
      // include the nextPaintedTrail's 'before' in our iteration, so we can stitch properly with the next layer
      this.endPointer = new scenery.TrailPointer( nextPaintedTrail.copy(), true );
    } else {
      this.endPointer = new scenery.TrailPointer( new scenery.Trail( scene ), false );
    }

    this.includesEndTrail = nextPaintedTrail !== null;

    /*
     * LayerBoundary properties and assurances:
     *
     * previousLayerType  - initialized in constructor (in case there are no layer changes)
     *                      set in layerChange for "fresh" pending boundary
     * nextLayerType      - set and overwrites in switchToType, for collapsing layers
     *                      not set anywhere else, so we can leave it null
     * previousPaintedTrail  - initialized in constructor
     *                      updated in markPainted if there is no pending change (don't set if there is a pending change)
     * nextPaintedTrail      - set on layerChange for "stale" boundary
     *                      stays null if nextPaintedTrail === null
     * previousEndPointer - (normal boundary) set in switchToType if there is no layer change pending
     *                      set in finalization if nextPaintedTrail === null && !this.layerChangePending (previousEndPointer should be null in that case)
     * nextStartPointer   - set in switchToType (always), overwrites values so we collapse layers nicely
     */
  };
  var LayerBuilder = scenery.LayerBuilder;

  LayerBuilder.prototype = {
    constructor: LayerBuilder,

    // walks part of the state up to just before the startPointer. we want the preferred layer stack to be in place, but the rest is not important
    prepareLayerStack: function() {
      var pointer = new scenery.TrailPointer( new scenery.Trail( this.startPointer.trail.rootNode() ), true );

      // if the start pointer is going to execute an exit() instead of an enter() on its trail node, we need to bump up the layer stack an additional step
      var targetLength = this.startPointer.trail.length - ( this.startPointer.isBefore ? 1 : 0 );

      while ( pointer.trail.length <= targetLength ) {
        var node = pointer.trail.lastNode();
        if ( node.layerStrategy.hasPreferredLayerType( pointer, this ) ) {
          this.pushPreferredLayerType( node.layerStrategy.getPreferredLayerType( pointer, this ) );
        }
        pointer.trail.addDescendant( this.startPointer.trail.nodes[pointer.trail.length] );
      }
    },

    run: function() {
      var builder = this;

      // push preferred layers for ancestors of our start pointer
      this.prepareLayerStack();

      // console.log( '         stack: ' + _.map( builder.layerTypeStack, function( type ) { return type.name; } ).join( ', ' ) );

      builder.startPointer.depthFirstUntil( builder.endPointer, function( pointer ) {
        var node = pointer.trail.lastNode();

        if ( pointer.isBefore ) {
          // console.log( 'builder: enter ' + pointer.toString() );
          node.layerStrategy.enter( pointer, builder );
        } else {
          // console.log( 'builder: exit ' + pointer.toString() );
          node.layerStrategy.exit( pointer, builder );
        }
        // console.log( '         stack: ' + _.map( builder.layerTypeStack, function( type ) { return type.name; } ).join( ', ' ) );
      }, false ); // include the endpoints

      // special case handling if we are at the 'end' of the scene, so that we create another 'wrapping' boundary
      if ( !this.includesEndTrail ) {
        // console.log( 'builder: not including end trail' );
        this.pendingBoundary.previousEndPointer = builder.endPointer; // TODO: consider implications if we leave this null, to indicate that it is not ended?
        this.layerChange( null );
      }
    },

    // allows paintedPointer === null at the end if the main iteration's nextPaintedTrail === null (i.e. we are at the end of the scene)
    layerChange: function( paintedPointer ) {
      this.layerChangePending = false;

      var confirmedBoundary = this.pendingBoundary;

      confirmedBoundary.nextPaintedTrail = paintedPointer ? paintedPointer.trail.copy() : null;

      this.boundaries.push( confirmedBoundary );

      this.pendingBoundary = new scenery.LayerBoundary();
      this.pendingBoundary.previousLayerType = confirmedBoundary.nextLayerType;
      this.pendingBoundary.previousPaintedTrail = confirmedBoundary.nextPaintedTrail;
      // console.log( 'builder:   added boundary' );
    },

    /*---------------------------------------------------------------------------*
    * API for layer strategy or other interaction
    *----------------------------------------------------------------------------*/

    switchToType: function( pointer, layerType ) {
      this.currentLayerType = layerType;

      this.pendingBoundary.nextLayerType = layerType;
      this.pendingBoundary.nextStartPointer = pointer.copy();
      if ( !this.layerChangePending ) {
        this.pendingBoundary.previousEndPointer = pointer.copy();
      }

      this.layerChangePending = true; // we wait until the first markPainted() call to create a boundary
    },

    // called so that we can finalize a layer switch (instead of collapsing unneeded layers)
    markPainted: function( pointer ) {
      if ( this.layerChangePending ) {
        this.layerChange( pointer );
      } else {
        // TODO: performance-wise, don't lookup indices on this copy? make a way to create a lightweight copy?
        this.pendingBoundary.previousPaintedTrail = pointer.trail.copy();
      }
    },

    // can be null to indicate that there is no current layer type
    getCurrentLayerType: function() {
      return this.currentLayerType;
    },

    pushPreferredLayerType: function( layerType ) {
      this.layerTypeStack.push( layerType );
    },

    popPreferredLayerType: function() {
      this.layerTypeStack.pop();
    },

    getPreferredLayerType: function() {
      if ( this.layerTypeStack.length !== 0 ) {
        return this.layerTypeStack[this.layerTypeStack.length - 1];
      } else {
        return null;
      }
    },

    bestPreferredLayerTypeFor: function( renderers ) {
      for ( var i = this.layerTypeStack.length - 1; i >= 0; i-- ) {
        var preferredType = this.layerTypeStack[i];
        if ( _.some( renderers, function( renderer ) { return preferredType.supportsRenderer( renderer ); } ) ) {
          return preferredType;
        }
      }

      // none of our stored preferred layer types are able to support any of the default type options
      return null;
    }
  };

  return LayerBuilder;
} );
<commit_msg>LayerBuilder fix for walking up the tree (don't add an undefined value to a trail)<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * A layer state is used to construct layer information (and later, layers), and is a state machine
 * that layer strategies from each node modify. Iterating through all of the nodes in a depth-first
 * manner will modify the LayerBuilder so that layer information can be retrieved.
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'scenery' );

  var scenery = require( 'SCENERY/scenery' );
  require( 'SCENERY/layers/LayerBoundary' );
  require( 'SCENERY/util/Trail' );
  require( 'SCENERY/util/TrailPointer' );

  /*
   * Builds layer information between trails
   *
   * previousLayerType should be null if there is no previous layer.
   */
  scenery.LayerBuilder = function( scene, previousLayerType, previousPaintedTrail, nextPaintedTrail ) {

    /*---------------------------------------------------------------------------*
    * Initial state
    *----------------------------------------------------------------------------*/

    this.layerTypeStack = [];
    this.boundaries = [];
    this.pendingBoundary = new scenery.LayerBoundary();
    this.pendingBoundary.previousLayerType = previousLayerType;
    this.pendingBoundary.previousPaintedTrail = previousPaintedTrail;

    /*
     * The current layer type active, and whether it has been 'used' yet. A node with isPainted() will trigger a 'used' action,
     * and if the layer hasn't been used, it will actually trigger a boundary creation. We want to collapse 'unused' layers
     * and boundaries together, so that every created layer has a node that displays something.
     */
    this.currentLayerType = previousLayerType;
    this.layerChangePending = previousPaintedTrail === null;

    /*---------------------------------------------------------------------------*
    * Start / End pointers
    *----------------------------------------------------------------------------*/

    if ( previousPaintedTrail ) {
      // Move our start pointer just past the previousPaintedTrail, since our previousLayerType is presumably for that trail's node's self.
      // Anything after that self could have been collapsed, so we need to start there.
      this.startPointer = new scenery.TrailPointer( previousPaintedTrail.copy(), true );
      this.startPointer.nestedForwards();
    } else {
      this.startPointer = new scenery.TrailPointer( new scenery.Trail( scene ), true );
    }

    if ( nextPaintedTrail ) {
      // include the nextPaintedTrail's 'before' in our iteration, so we can stitch properly with the next layer
      this.endPointer = new scenery.TrailPointer( nextPaintedTrail.copy(), true );
    } else {
      this.endPointer = new scenery.TrailPointer( new scenery.Trail( scene ), false );
    }

    this.includesEndTrail = nextPaintedTrail !== null;

    /*
     * LayerBoundary properties and assurances:
     *
     * previousLayerType  - initialized in constructor (in case there are no layer changes)
     *                      set in layerChange for "fresh" pending boundary
     * nextLayerType      - set and overwrites in switchToType, for collapsing layers
     *                      not set anywhere else, so we can leave it null
     * previousPaintedTrail  - initialized in constructor
     *                      updated in markPainted if there is no pending change (don't set if there is a pending change)
     * nextPaintedTrail      - set on layerChange for "stale" boundary
     *                      stays null if nextPaintedTrail === null
     * previousEndPointer - (normal boundary) set in switchToType if there is no layer change pending
     *                      set in finalization if nextPaintedTrail === null && !this.layerChangePending (previousEndPointer should be null in that case)
     * nextStartPointer   - set in switchToType (always), overwrites values so we collapse layers nicely
     */
  };
  var LayerBuilder = scenery.LayerBuilder;

  LayerBuilder.prototype = {
    constructor: LayerBuilder,

    // walks part of the state up to just before the startPointer. we want the preferred layer stack to be in place, but the rest is not important
    prepareLayerStack: function() {
      var pointer = new scenery.TrailPointer( new scenery.Trail( this.startPointer.trail.rootNode() ), true );

      // if the start pointer is going to execute an exit() instead of an enter() on its trail node, we need to bump up the layer stack an additional step
      var targetLength = this.startPointer.trail.length - ( this.startPointer.isBefore ? 1 : 0 );

      while ( pointer.trail.length <= targetLength ) {
        var node = pointer.trail.lastNode();
        if ( node.layerStrategy.hasPreferredLayerType( pointer, this ) ) {
          this.pushPreferredLayerType( node.layerStrategy.getPreferredLayerType( pointer, this ) );
        }
        if ( pointer.trail.length < this.startPointer.trail.nodes.length ) {
          pointer.trail.addDescendant( this.startPointer.trail.nodes[pointer.trail.length] );
        } else {
          // TODO: a cleaner way of doing this, since we were adding undefined into a trail to cause the loop to exit
          break;
        }
      }
    },

    run: function() {
      var builder = this;

      // push preferred layers for ancestors of our start pointer
      this.prepareLayerStack();

      // console.log( '         stack: ' + _.map( builder.layerTypeStack, function( type ) { return type.name; } ).join( ', ' ) );

      builder.startPointer.depthFirstUntil( builder.endPointer, function( pointer ) {
        var node = pointer.trail.lastNode();

        if ( pointer.isBefore ) {
          // console.log( 'builder: enter ' + pointer.toString() );
          node.layerStrategy.enter( pointer, builder );
        } else {
          // console.log( 'builder: exit ' + pointer.toString() );
          node.layerStrategy.exit( pointer, builder );
        }
        // console.log( '         stack: ' + _.map( builder.layerTypeStack, function( type ) { return type.name; } ).join( ', ' ) );
      }, false ); // include the endpoints

      // special case handling if we are at the 'end' of the scene, so that we create another 'wrapping' boundary
      if ( !this.includesEndTrail ) {
        // console.log( 'builder: not including end trail' );
        this.pendingBoundary.previousEndPointer = builder.endPointer; // TODO: consider implications if we leave this null, to indicate that it is not ended?
        this.layerChange( null );
      }
    },

    // allows paintedPointer === null at the end if the main iteration's nextPaintedTrail === null (i.e. we are at the end of the scene)
    layerChange: function( paintedPointer ) {
      this.layerChangePending = false;

      var confirmedBoundary = this.pendingBoundary;

      confirmedBoundary.nextPaintedTrail = paintedPointer ? paintedPointer.trail.copy() : null;

      this.boundaries.push( confirmedBoundary );

      this.pendingBoundary = new scenery.LayerBoundary();
      this.pendingBoundary.previousLayerType = confirmedBoundary.nextLayerType;
      this.pendingBoundary.previousPaintedTrail = confirmedBoundary.nextPaintedTrail;
      // console.log( 'builder:   added boundary' );
    },

    /*---------------------------------------------------------------------------*
    * API for layer strategy or other interaction
    *----------------------------------------------------------------------------*/

    switchToType: function( pointer, layerType ) {
      this.currentLayerType = layerType;

      this.pendingBoundary.nextLayerType = layerType;
      this.pendingBoundary.nextStartPointer = pointer.copy();
      if ( !this.layerChangePending ) {
        this.pendingBoundary.previousEndPointer = pointer.copy();
      }

      this.layerChangePending = true; // we wait until the first markPainted() call to create a boundary
    },

    // called so that we can finalize a layer switch (instead of collapsing unneeded layers)
    markPainted: function( pointer ) {
      if ( this.layerChangePending ) {
        this.layerChange( pointer );
      } else {
        // TODO: performance-wise, don't lookup indices on this copy? make a way to create a lightweight copy?
        this.pendingBoundary.previousPaintedTrail = pointer.trail.copy();
      }
    },

    // can be null to indicate that there is no current layer type
    getCurrentLayerType: function() {
      return this.currentLayerType;
    },

    pushPreferredLayerType: function( layerType ) {
      this.layerTypeStack.push( layerType );
    },

    popPreferredLayerType: function() {
      this.layerTypeStack.pop();
    },

    getPreferredLayerType: function() {
      if ( this.layerTypeStack.length !== 0 ) {
        return this.layerTypeStack[this.layerTypeStack.length - 1];
      } else {
        return null;
      }
    },

    bestPreferredLayerTypeFor: function( renderers ) {
      for ( var i = this.layerTypeStack.length - 1; i >= 0; i-- ) {
        var preferredType = this.layerTypeStack[i];
        if ( _.some( renderers, function( renderer ) { return preferredType.supportsRenderer( renderer ); } ) ) {
          return preferredType;
        }
      }

      // none of our stored preferred layer types are able to support any of the default type options
      return null;
    }
  };

  return LayerBuilder;
} );
<|endoftext|>


<commit_before>import React from 'react-native'
import he from 'he' // HTML entity encode and decode

export default {
  decodeText(txt) {
    if (txt) {
      return he.decode(txt);
    }
    return '';
  },

  defaultChangeHandler(name, value) {
    if (typeof this.schema === 'undefined' || !this.schema.hasOwnProperty(name)) {
      console.error('Invalid validation schema');
      return;
    }
    if (!this.hasOwnProperty('joiCheckError')) {
      console.error('Invalid joi mixin');
      return;
    }
    const schema = {};
    schema[name] = this.schema[name];
    const object = {};
    object[name] = value;
    this.joiCheckError(object, schema);
    const state = {};
    state[name] = value;
    this.setState(state);
  },

  textFieldChangeHandler(name, text) {
    console.log('name: ', name)
    console.log('text: ', text)
    if (typeof text !== 'string') {
      console.error('Invalid text field value');
      return;
    }
    this.defaultChangeHandler(name, text);
  },

  checkboxChangeHandler: function(name, value) {
    if (typeof value !== 'boolean') {
      console.error('Invalid checkbox value');
      return;
    }
    this.defaultChangeHandler(name, value);
  },

  /**
   * scrolls to a view within a scrollview
   *
   * @param {string} scrollRef, the reference string to the ScrollView
   * @param {string} viewWrapperRef, the reference string to the View
   * @param {number} offset, any additional offset to scroll (not including the height of the view wrapper)
   */
  scrollToViewWrapper(scrollRef, viewWrapperRef, offset, syntheticEvent) {
    const scrollElement = this.refs[scrollRef];
    const viewWrapperElement = this.refs[viewWrapperRef];
    if (typeof viewWrapperElement === 'undefined') {
      console.warn('The <View> wrapper must have a ref assigned');
      return;
    }
    viewWrapperElement.measure((ox, oy, width, height, px, py) => {
      if (typeof scrollElement === 'undefined') {
        console.warn('The <ScrollView> must have a ref assigned');
        return;
      }
      if (typeof offset !== 'number') {
        syntheticEvent = offset
        offset = 0;
      }
      offset = offset + height;
      const scrollResponder = scrollElement.getScrollResponder();
      scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
        React.findNodeHandle(viewWrapperElement),
        offset,
        true
      );
    });
  },

  /**
   * determines the height of an element
   *
   * @return {function} done, the callback (err, height) of the element.
   */
  calculateHeight(viewRef, done) {
    const viewElement = this.refs[viewRef];
    if (typeof viewElement === 'undefined') {
      done('The <View> must have a ref assigned');
      return;
    }
    viewElement.measure((ox, oy, width, height, px, py) => {
      done(null, height);
    });
  },

  /**
   * scrolls to a vertical position within scrollview
   *
   * @param {string} scrollRef, the reference string to the ScrollView
   * @param {number} position, the vertical position to scroll within the ScrollView
   */
  scrollTo(scrollRef, position, syntheticEvent) {
    const scrollElement = this.refs[scrollRef];
    if (typeof scrollElement === 'undefined') {
      console.warn('The <ScrollView> must have a ref assigned');
      return;
    }
    if (typeof position !== 'number') {
      syntheticEvent = position
      position = 0;
    }
    scrollElement.scrollTo(position)
  },

  /**
   * trims TextInput value of whitespace
   *
   * @param {string} inputRef, the reference of the input element
   * @return {string} txt, the trimmed text
   */
  trimText(inputRef) {
    const inputElement = this.refs[inputRef];
    if (typeof inputElement === 'undefined') {
      console.warn('The <TextInput> must have a ref assigned');
      return;
    }
    const txt = inputElement._getText();
    if (typeof txt === 'string') {
      inputElement.setNativeProps({text: txt.trim()});
      this.textFieldChangeHandler(inputRef, txt.trim());
    }
  },

};
<commit_msg>remove console.log<commit_after>import React from 'react-native'
import he from 'he' // HTML entity encode and decode

export default {
  decodeText(txt) {
    if (txt) {
      return he.decode(txt);
    }
    return '';
  },

  defaultChangeHandler(name, value) {
    if (typeof this.schema === 'undefined' || !this.schema.hasOwnProperty(name)) {
      console.error('Invalid validation schema');
      return;
    }
    if (!this.hasOwnProperty('joiCheckError')) {
      console.error('Invalid joi mixin');
      return;
    }
    const schema = {};
    schema[name] = this.schema[name];
    const object = {};
    object[name] = value;
    this.joiCheckError(object, schema);
    const state = {};
    state[name] = value;
    this.setState(state);
  },

  textFieldChangeHandler(name, text) {
    if (typeof text !== 'string') {
      console.error('Invalid text field value');
      return;
    }
    this.defaultChangeHandler(name, text);
  },

  checkboxChangeHandler: function(name, value) {
    if (typeof value !== 'boolean') {
      console.error('Invalid checkbox value');
      return;
    }
    this.defaultChangeHandler(name, value);
  },

  /**
   * scrolls to a view within a scrollview
   *
   * @param {string} scrollRef, the reference string to the ScrollView
   * @param {string} viewWrapperRef, the reference string to the View
   * @param {number} offset, any additional offset to scroll (not including the height of the view wrapper)
   */
  scrollToViewWrapper(scrollRef, viewWrapperRef, offset, syntheticEvent) {
    const scrollElement = this.refs[scrollRef];
    const viewWrapperElement = this.refs[viewWrapperRef];
    if (typeof viewWrapperElement === 'undefined') {
      console.warn('The <View> wrapper must have a ref assigned');
      return;
    }
    viewWrapperElement.measure((ox, oy, width, height, px, py) => {
      if (typeof scrollElement === 'undefined') {
        console.warn('The <ScrollView> must have a ref assigned');
        return;
      }
      if (typeof offset !== 'number') {
        syntheticEvent = offset
        offset = 0;
      }
      offset = offset + height;
      const scrollResponder = scrollElement.getScrollResponder();
      scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
        React.findNodeHandle(viewWrapperElement),
        offset,
        true
      );
    });
  },

  /**
   * determines the height of an element
   *
   * @return {function} done, the callback (err, height) of the element.
   */
  calculateHeight(viewRef, done) {
    const viewElement = this.refs[viewRef];
    if (typeof viewElement === 'undefined') {
      done('The <View> must have a ref assigned');
      return;
    }
    viewElement.measure((ox, oy, width, height, px, py) => {
      done(null, height);
    });
  },

  /**
   * scrolls to a vertical position within scrollview
   *
   * @param {string} scrollRef, the reference string to the ScrollView
   * @param {number} position, the vertical position to scroll within the ScrollView
   */
  scrollTo(scrollRef, position, syntheticEvent) {
    const scrollElement = this.refs[scrollRef];
    if (typeof scrollElement === 'undefined') {
      console.warn('The <ScrollView> must have a ref assigned');
      return;
    }
    if (typeof position !== 'number') {
      syntheticEvent = position
      position = 0;
    }
    scrollElement.scrollTo(position)
  },

  /**
   * trims TextInput value of whitespace
   *
   * @param {string} inputRef, the reference of the input element
   * @return {string} txt, the trimmed text
   */
  trimText(inputRef) {
    const inputElement = this.refs[inputRef];
    if (typeof inputElement === 'undefined') {
      console.warn('The <TextInput> must have a ref assigned');
      return;
    }
    const txt = inputElement._getText();
    if (typeof txt === 'string') {
      inputElement.setNativeProps({text: txt.trim()});
      this.textFieldChangeHandler(inputRef, txt.trim());
    }
  },

};
<|endoftext|>


<commit_before>/******************************************
* Request Model
*/
app.Models.Request = app.Models.GenericModel.extend({

	fields     : ['id', 'name', 'actions', 'tooltip', 'create_date', 'create_uid', 'date_deadline', 'description', 'manager_id', 'note', 'partner_address', 'partner_id', 'partner_name', 'partner_phone', 'partner_service_id', 'partner_type', 'partner_type_code', 'people_name', 'people_email', 'people_phone', 'refusal_reason', 'service_id', 'site1', 'site_details', 'state', 'intervention_assignement_id', 'has_equipment', 'equipment_id', 'is_citizen'],

	urlRoot    : '/api/openstc/intervention_requests',

	searchable_fields: [
		{
			key  : 'id',
			type : 'numeric'
		},
		{
			key  : 'name',
			type : 'text'
		},
		{
			key  : 'partner_id.name',
			type : 'text'
		}
	],

	getSite : function(type) {
		if(this.get('site1')){

			switch(type){
				case 'id':
					return this.get('site1')[0];
				break;
				case 'json':
					return {id: this.get('site1')[0], name: this.get('site1')[1]};
				break;
				default:
					return _.titleize(this.get('site1')[1].toLowerCase());
			}
		}
		else{
			return false;
		}
	},
	setSite : function(value, silent) {
		this.set({ site1 : value }, {silent: silent});
	},

	getEquipment : function(type) {

		if(this.onEquipment()){
			switch(type){
				case 'id':
					return this.get('equipment_id')[0];
				break;
				case 'json':
					return {id: this.get('equipment_id')[0], name: this.get('equipment_id')[1]};
				break;
				default:
					return _.titleize(this.get('equipment_id')[1].toLowerCase());
			}
		}
		else{
			return false;
		}
	},
	setEquipment : function(value, silent) {
		this.set({ equipment_id : value }, {silent: silent});
	},

	getDescription : function() {
		if(this.get('description')){
			return this.get('description');
		}
	},
	setDescription : function(value, silent) {
		this.set({ description : value }, {silent: silent});
	},

	getRefusalReason : function() {
		return this.get('refusal_reason');
	},
	setRefusalReason  : function(value, silent) {
		this.set({ refusal_reason : value }, {silent: silent});
	},

	getState : function() {
		return this.get('state');
	},
	setState : function(value, silent) {
		this.set({ state : value }, {silent: silent});
	},

	// Note for the DST //
	getNote : function() {
		return this.get('note');
	},
	setNote : function(value) {
		this.set({ note : value });
	},

	getService : function(type) {
		var id = this.get('service_id')[0];
		var name = _.titleize(this.get('service_id')[1].toLowerCase());

		switch(type){
			case 'id':
				return id;
			break;
			case 'json':
				return {id: id, name: name};
			break;
			default:
				return name;
		}
	},
	setService : function(value, silent) {
		this.set({ service_id : value }, {silent: silent});
	},

	// Claimer of the resquest //
	getClaimer: function(type){
		var claimer = {};

		switch (type){
			case 'id':
				return this.get('partner_id')[0];
			break;
			case 'json':
				return {id: this.get('partner_id')[0], name: this.get('partner_id')[1]};
			break;
			default:
				return this.get('partner_id')[1];
		}

		return claimer;
	},

	setClaimerType: function(value, silent){
		this.set({ partner_type : value }, {silent: silent});
	},
	getClaimerType: function(type){
		if(this.get('partner_type')){
			switch (type){
				case 'id':
					return this.get('partner_type')[0];
				break;
				case 'json':
					return {id: this.get('partner_type')[0], name: this.get('partner_type')[1]};
				break;
				default:
					return this.get('partner_type')[1];
			}
		}
	},

	setClaimer: function(value, silent){
		this.set({ partner_id : value }, {silent: silent});
	},
	setClaimerContact: function(value, silent){
		this.set({ partner_address : value }, {silent: silent});
	},

	getClaimerPhone: function(){
		return this.get('partner_phone');
	},

	getClaimerContact: function(type){
		if(this.get('partner_address')){
			switch (type){
				case 'id':
					return this.get('partner_address')[0];
				break;
				case 'json':
					return {id: this.get('partner_address')[0], name: this.get('partner_address')[1]};
				break;
				default:
					return this.get('partner_address')[1];
			}
		}
	},

	getManager: function(type){
		switch(type){
			case 'id':
				return this.get('manager_id')[0];
			break;
			default:
				return _.capitalize(this.get('manager_id')[1]);
		}
	},

	getCreateDate: function(type){

		switch(type){
			case 'fromNow':
				return moment(this.get('create_date'), 'YYYY-MM-DD HH:mm:ss').add('hours',1).fromNow();
			break;
			default:
				return moment(this.get('create_date'), 'YYYY-MM-DD HH:mm:ss').add('hours',1).format('LLL');
		}
	},

	getCreateAuthor: function(type){
		switch(type){
			case 'id':
				return this.get('create_uid')[0];
			break;
			default:
				return _.capitalize(this.get('create_uid')[1]);
		}
	},

	onEquipment: function(){
		return this.get('has_equipment');
	},
	setOnEquipment : function(value, silent) {
		this.set({ has_equipment : value }, {silent: silent});
	},

	fromCitizen: function(){
		return this.get('is_citizen');
	},
	setFromCitizen: function(value, silent){
		this.set({ is_citizen : value }, {silent: silent});
	},

	getCitizenName: function(){
		if(this.fromCitizen()){
			return this.get('people_name');
		}
	},
	setCitizenName: function(value, silent){
		this.set({ people_name : value }, {silent: silent});
	},

	getCitizenPhone: function(){
		if(this.fromCitizen()){
			return this.get('people_phone');
		}
	},
	setCitizenPhone: function(value, silent){
		this.set({ people_phone : value }, {silent: silent});
	},
	getCitizenEmail: function(){
		if(this.fromCitizen()){
			return this.get('people_email');
		}
	},
	setCitizenEmail: function(value, silent){
		this.set({ people_email : value }, {silent: silent});
	},

	getPlaceDetails: function(){
		return this.get('site_details');
	},
	setPlaceDetails: function(value, silent) {
		this.set({ site_details : value }, {silent: silent});
	},

	getInformations: function(){
		return this.get('tooltip');
	},

	/** Model Initialization
	*/
	initialize: function (model) {
		//console.log("Request Model Initialization");

		// Set the translation for the states / actions //
		app.Models.Request.status.wait.translation     = app.lang.wait;
		app.Models.Request.status.valid.translation    = app.lang.valid;
		app.Models.Request.status.confirm.translation  = app.lang.confirm;
		app.Models.Request.status.refused.translation  = app.lang.refused;
		app.Models.Request.status.closed.translation   = app.lang.finished;

		app.Models.Request.actions.valid.translation   = app.lang.actions.validate;
		app.Models.Request.actions.confirm.translation = app.lang.actions.confirmChief;
		app.Models.Request.actions.refused.translation = app.lang.actions.refuse;

	},

}, {

	// Status of the requests //
	status : {
		wait: {
			key 		: 'wait',
			color 		: 'info',
			translation : ''
		},
		valid: {
			key 		: 'valid',
			color 		: 'success',
			translation : ''
		},
		confirm: {
			key 		: 'confirm',
			color 		: 'warning',
			translation : ''
		},
		refused: {
			key 		: 'refused',
			color 		: 'danger',
			translation : ''
		},
		closed: {
			key 		: 'closed',
			color 		: 'default',
			translation : ''
		}
	},

	// Actions of the requests //
	actions : {
		valid: {
			key 		: 'valid',
			color 		: 'success',
			icon 		: 'icon-ok',
			translation : ''
		},
		confirm: {
			key 		: 'confirm',
			color 		: 'warning',
			icon 		: 'icon-level-up',
			translation : ''
		},
		refused: {
			key 		: 'refused',
			color 		: 'danger',
			icon 		: 'icon-remove',
			translation : ''
		},
	}

});<commit_msg>BUG fix : filter on partner or individual person<commit_after>/******************************************
* Request Model
*/
app.Models.Request = app.Models.GenericModel.extend({

	fields     : ['id', 'name', 'actions', 'tooltip', 'create_date', 'create_uid', 'date_deadline', 'description', 'manager_id', 'note', 'partner_address', 'partner_id', 'partner_name', 'partner_phone', 'partner_service_id', 'partner_type', 'partner_type_code', 'people_name', 'people_email', 'people_phone', 'refusal_reason', 'service_id', 'site1', 'site_details', 'state', 'intervention_assignement_id', 'has_equipment', 'equipment_id', 'is_citizen'],

	urlRoot    : '/api/openstc/intervention_requests',

	searchable_fields: [
		{
			key  : 'id',
			type : 'numeric'
		},
		{
			key  : 'name',
			type : 'text'
		},
		{
			key  : 'partner_name',
			type : 'text'
		}
	],

	getSite : function(type) {
		if(this.get('site1')){

			switch(type){
				case 'id':
					return this.get('site1')[0];
				break;
				case 'json':
					return {id: this.get('site1')[0], name: this.get('site1')[1]};
				break;
				default:
					return _.titleize(this.get('site1')[1].toLowerCase());
			}
		}
		else{
			return false;
		}
	},
	setSite : function(value, silent) {
		this.set({ site1 : value }, {silent: silent});
	},

	getEquipment : function(type) {

		if(this.onEquipment()){
			switch(type){
				case 'id':
					return this.get('equipment_id')[0];
				break;
				case 'json':
					return {id: this.get('equipment_id')[0], name: this.get('equipment_id')[1]};
				break;
				default:
					return _.titleize(this.get('equipment_id')[1].toLowerCase());
			}
		}
		else{
			return false;
		}
	},
	setEquipment : function(value, silent) {
		this.set({ equipment_id : value }, {silent: silent});
	},

	getDescription : function() {
		if(this.get('description')){
			return this.get('description');
		}
	},
	setDescription : function(value, silent) {
		this.set({ description : value }, {silent: silent});
	},

	getRefusalReason : function() {
		return this.get('refusal_reason');
	},
	setRefusalReason  : function(value, silent) {
		this.set({ refusal_reason : value }, {silent: silent});
	},

	getState : function() {
		return this.get('state');
	},
	setState : function(value, silent) {
		this.set({ state : value }, {silent: silent});
	},

	// Note for the DST //
	getNote : function() {
		return this.get('note');
	},
	setNote : function(value) {
		this.set({ note : value });
	},

	getService : function(type) {
		var id = this.get('service_id')[0];
		var name = _.titleize(this.get('service_id')[1].toLowerCase());

		switch(type){
			case 'id':
				return id;
			break;
			case 'json':
				return {id: id, name: name};
			break;
			default:
				return name;
		}
	},
	setService : function(value, silent) {
		this.set({ service_id : value }, {silent: silent});
	},

	// Claimer of the resquest //
	getClaimer: function(type){
		var claimer = {};

		switch (type){
			case 'id':
				return this.get('partner_id')[0];
			break;
			case 'json':
				return {id: this.get('partner_id')[0], name: this.get('partner_id')[1]};
			break;
			default:
				return this.get('partner_id')[1];
		}

		return claimer;
	},

	setClaimerType: function(value, silent){
		this.set({ partner_type : value }, {silent: silent});
	},
	getClaimerType: function(type){
		if(this.get('partner_type')){
			switch (type){
				case 'id':
					return this.get('partner_type')[0];
				break;
				case 'json':
					return {id: this.get('partner_type')[0], name: this.get('partner_type')[1]};
				break;
				default:
					return this.get('partner_type')[1];
			}
		}
	},

	setClaimer: function(value, silent){
		this.set({ partner_id : value }, {silent: silent});
	},
	setClaimerContact: function(value, silent){
		this.set({ partner_address : value }, {silent: silent});
	},

	getClaimerPhone: function(){
		return this.get('partner_phone');
	},

	getClaimerContact: function(type){
		if(this.get('partner_address')){
			switch (type){
				case 'id':
					return this.get('partner_address')[0];
				break;
				case 'json':
					return {id: this.get('partner_address')[0], name: this.get('partner_address')[1]};
				break;
				default:
					return this.get('partner_address')[1];
			}
		}
	},

	getManager: function(type){
		switch(type){
			case 'id':
				return this.get('manager_id')[0];
			break;
			default:
				return _.capitalize(this.get('manager_id')[1]);
		}
	},

	getCreateDate: function(type){

		switch(type){
			case 'fromNow':
				return moment(this.get('create_date'), 'YYYY-MM-DD HH:mm:ss').add('hours',1).fromNow();
			break;
			default:
				return moment(this.get('create_date'), 'YYYY-MM-DD HH:mm:ss').add('hours',1).format('LLL');
		}
	},

	getCreateAuthor: function(type){
		switch(type){
			case 'id':
				return this.get('create_uid')[0];
			break;
			default:
				return _.capitalize(this.get('create_uid')[1]);
		}
	},

	onEquipment: function(){
		return this.get('has_equipment');
	},
	setOnEquipment : function(value, silent) {
		this.set({ has_equipment : value }, {silent: silent});
	},

	fromCitizen: function(){
		return this.get('is_citizen');
	},
	setFromCitizen: function(value, silent){
		this.set({ is_citizen : value }, {silent: silent});
	},

	getCitizenName: function(){
		if(this.fromCitizen()){
			return this.get('people_name');
		}
	},
	setCitizenName: function(value, silent){
		this.set({ people_name : value }, {silent: silent});
	},

	getCitizenPhone: function(){
		if(this.fromCitizen()){
			return this.get('people_phone');
		}
	},
	setCitizenPhone: function(value, silent){
		this.set({ people_phone : value }, {silent: silent});
	},
	getCitizenEmail: function(){
		if(this.fromCitizen()){
			return this.get('people_email');
		}
	},
	setCitizenEmail: function(value, silent){
		this.set({ people_email : value }, {silent: silent});
	},

	getPlaceDetails: function(){
		return this.get('site_details');
	},
	setPlaceDetails: function(value, silent) {
		this.set({ site_details : value }, {silent: silent});
	},

	getInformations: function(){
		return this.get('tooltip');
	},

	/** Model Initialization
	*/
	initialize: function (model) {
		//console.log("Request Model Initialization");

		// Set the translation for the states / actions //
		app.Models.Request.status.wait.translation     = app.lang.wait;
		app.Models.Request.status.valid.translation    = app.lang.valid;
		app.Models.Request.status.confirm.translation  = app.lang.confirm;
		app.Models.Request.status.refused.translation  = app.lang.refused;
		app.Models.Request.status.closed.translation   = app.lang.finished;

		app.Models.Request.actions.valid.translation   = app.lang.actions.validate;
		app.Models.Request.actions.confirm.translation = app.lang.actions.confirmChief;
		app.Models.Request.actions.refused.translation = app.lang.actions.refuse;

	},

}, {

	// Status of the requests //
	status : {
		wait: {
			key 		: 'wait',
			color 		: 'info',
			translation : ''
		},
		valid: {
			key 		: 'valid',
			color 		: 'success',
			translation : ''
		},
		confirm: {
			key 		: 'confirm',
			color 		: 'warning',
			translation : ''
		},
		refused: {
			key 		: 'refused',
			color 		: 'danger',
			translation : ''
		},
		closed: {
			key 		: 'closed',
			color 		: 'default',
			translation : ''
		}
	},

	// Actions of the requests //
	actions : {
		valid: {
			key 		: 'valid',
			color 		: 'success',
			icon 		: 'icon-ok',
			translation : ''
		},
		confirm: {
			key 		: 'confirm',
			color 		: 'warning',
			icon 		: 'icon-level-up',
			translation : ''
		},
		refused: {
			key 		: 'refused',
			color 		: 'danger',
			icon 		: 'icon-remove',
			translation : ''
		},
	}

});<|endoftext|>