cpiAjax = new function(){

    var me = this;

    // cpiWebApps Base URL
    me.appUrl = '\/app\/';

    me.load = function(url,data,callback){
        if(typeof data == 'object'){
            if(data.tagName == 'FORM'){
                data = $(data).serialize();
            }
        }
        me.post(url,data,function(response){
            I = /<!-- (cpiJsId[a-f0-9]{13}) -->/.exec(response);
            Json = /<!-- cpiJsJson -->/.exec(response);
            Error = /<!-- cpiJsError ([^\n]*) -->/.exec(response);
            NoContent = /<!-- No content -->/.exec(response);
            // If identified content
            if(I){
                I = I[1];
                $('body').append('<div id="' + I + '" style="display:none;" class="cpiWebApps"></div>');
                $('#' + I).html(response);

                if(typeof callback == 'function'){
                    callback(I);
                }
            }
            // If JSON data
            else if(Json){
                if(typeof callback == 'function'){
                    callback(JSON.parse(response.replace('<!-- cpiJsJson -->\n','')));
                }
            }
            // If error message
            else if(Error){
                alert('Server error: ' + Error[1]);
            }
            // If 'identified' no content
            else if(NoContent){
                if(typeof callback == 'function'){
                    callback();
                }
            }
            // If non identified content
            else{
                if(typeof callback == 'function'){
                    callback(response);
                }
            }

            // alert( response );
            return false;

        });
        return false;
    };

    me.loadInto = function(parent,url,data){
        me.load(url,data,function(response){
            $(parent).empty();
            $(parent).html(response);
        });
    };

    me.sendForm = function(form,url,callback){
        data = $(form).serialize();
        me.post(url,data,callback);
    };

    me.post = function(url,data,callback){
        url = this.appUrl + url;
        $.post(url,data,function(response){
            callback(response);
        });
    };

    me.reload = function(){
        window.location.reload();
    };

}();


//-----------------------------------------------CWA
cwa = {
    libs:{
        ui:{}
    },
    ui:{
        Auth:{},
        HtmlEditor:{},
        Web:{}
    }
};
//###############################################



//-----------------------------------------------CWA LIBS UI DIALOG
cwa.libs.ui.dialog = function(){
  var me = this;
  
  /* PRIVATE MEMBERS */
  var elRoot =    $('<div>');
  
  var title = '';
  
  var cbOnBeforeClose = function(){};
  
  /* SETTERS */
  me.setCbOnBeforeClose = function(cb){
      if(typeof cb == 'function') cbOnBeforeClose = cb;
  };
  me.setTitle = function(t){
    title = t;  
  };

  /* PUBLIC METHODS */
  me.load = function(xtElRoot){
      if(typeof xtElRoot == 'object') elRoot = xtElRoot;
      makeDialog();
      return elRoot;
  };
  me.show = function(){
      show();
  };
  me.close = function(){
      close();
  };
  
  /* PRIVATE METHODS */
  var makeDialog = function(){

      elRoot.dialog({
          title:title,
          width:'auto',
          modal:true,
          minHeight:20,
          position:['center',30],
          resizable:false,
          draggable:true,
          autoOpen:false,
          dialogClass:'cpiWebApps'
      });
      
      elRoot.bind('dialogbeforeclose',function(){
          cbOnBeforeClose();
      });
          
      elRoot.bind('dialogclose',function(){
          elRoot.dialog('destroy');
          elRoot.remove();
      });
  };
  
  var show = function(){
      elRoot.dialog('open');
  };
  
  var close = function(){
      elRoot.dialog('close');
  };
};
//###############################################



//----------------------------------------------- CWA LIBS UI FORM
cwa.libs.ui.form = function(){
  var me = this;
  
  /* PRIVATE MEMBERS */
  var form = {
          parent:null,                 // Element to put the form in
          get:"",                      // GET action. Used to get fields values when opening form
          set:"",                      // SET action. Used to submit form
          getForNew:false,             // Flag for GET action on new element. If true, get fields values for null pk
          primaryKey:"id",             // Primary key of the edited object
          displaySubmitButton:true,
          submitLabel:'Enregistrer',  
          displayCancelButton:true,
          cancelLabel:'Annuler',
          fields:{},
          groups:{},
          defaultGroup:null
  };
  
  var fieldsPatterns = {
      hidden:{type:'hidden',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      text:{type:'text',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      mediumText:{type:'mediumText',name:'',label:'nolabel',style:'',autoValue:true,help:null,width:200,height:40,heightOnFocus:150},
      password:{type:'password',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      timestamp:{type:'timestamp',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      bool:{type:'bool',name:'',label:'nolabel',style:'',autoValue:true,help:null,onLabel:'oui',offLabel:'non'},
      color:{type:'color',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      html:{type:'html',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      htmlCode:{type:'htmlCode',name:'',label:'nolabel',style:'',autoValue:true,help:null},
      selector:{type:'selector',name:'',label:'nolabel',style:'',autoValue:true,help:null,nullable:false,items:[]},
      foreignSelector:{type:'foreignSelector',name:'',label:'nolabel',style:'',autoValue:true,help:null,nullable:false,editor:'',foreignKey:'id',foreignObjectLabel:'_ObjectString'},
      actionButton:{type:'actionButton',name:'',label:'nolabel',style:'',buttonLabel:'',buttonIcon:'',actionsBase:'',data:{}}
  };
  
  var selectorItemPattern = {label:'nolabel',value:''};
  
  var groupPattern = {label:'nolabel',minWidth:0,startNewLine:false,fields:[]};
    
  var pk = null;
  var cbOnSubmit = function(){};
  var cbOnCancel = function(){};

  var elForm = $('<form onsubmit="return false" class="cpiForm">');
  var elErrorMsg = $('<div class="ui-state-error" style="display:none;padding: 0pt 0.7em; margin: 5px;text-align:center;">\
                      <p><span class="ui-icon ui-icon-alert" style="float: left; margin-right: 0.3em;"></span>\
                      Certains champs ont été corrigés ou refusés.<br>Vérifiez ci-dessous.</p></div>')
                   .appendTo(elForm);
  var elGroupsTabs = $('<div class="groupsTabs" style="display:none;">').appendTo(elForm);
  var elFieldsTable = $('<tbody>').appendTo($('<table>').appendTo(elForm));
  $('<hr>').appendTo(elForm);
  var elButtonsDiv = $('<div class="buttons">').appendTo(elForm);
  
  /* SETTERS */
  me.setPk = function(value){
      pk = value;
  };
  me.setCbOnSubmit = function(cb){
      if(typeof cb == 'function') cbOnSubmit = cb;
  };
  me.setCbOnCancel = function(cb){
      if(typeof cb == 'function') cbOnCancel = cb;
  };
  
  /* PUBLIC METHODS */
  me.load = function(config){
      
      $(config.parent).empty().append(elForm);
      
      // Merge config with form
      for(var fieldIndex in config.fields){
          if(config.fields[fieldIndex].type=='selector'){
              for(var itemIndex in config.fields[fieldIndex].items){
                  config.fields[fieldIndex].items[itemIndex] = $.extend({},selectorItemPattern,config.fields[fieldIndex].items[itemIndex]);
              }
          }
          config.fields[fieldIndex] = $.extend({},fieldsPatterns[config.fields[fieldIndex].type],config.fields[fieldIndex]);
      }
      for(var groupIndex in config.groups){
          config.groups[groupIndex] = $.extend({},groupPattern,config.groups[groupIndex]);
      }
      $.extend(true,form,config);
      
      buildForm(); 
  };
  
  /* PRIVATE METHODS */
  var buildForm = function(){

      // ADD FIELDS DESCRIPTION 
      for(var fieldIndex in form.fields){
          var field = form.fields[fieldIndex];
          elForm.append(
              $('<input type="hidden" name="fields['+fieldIndex+']" value="'+field.name+'">')
          );
      }
      elForm.append(
          $('<input type="hidden" name="fields['+(++fieldIndex)+']" value="'+form.primaryKey+'">')
      );
      
      // ADD PK
      elForm.append(
          $('<input type="hidden" name="Pk" value="'+(pk?pk:'')+'">')
      );
      
    // Add fields
    for( var fieldKey in form.fields ){
        var field = form.fields[fieldKey];
        var labelCell = $('<td class="label">');
        var fieldCell = $('<td class="field">');
        var helpCell = $('<td class="help">');
        var fieldRow = $('<tr>').append(labelCell).append(fieldCell).append(helpCell).appendTo(elFieldsTable);
        fieldRow.attr("data-fieldKey",fieldKey); // strange, but doesnt work if inserted inside line above. so don't touch.
        var htmlLabel = field.label.replace( / /g, '&nbsp;' );
        htmlLabel = htmlLabel.replace( /\n/g, '<br />\n' );
        labelCell.append($('<label>').attr('for',field.name).html(htmlLabel));
        
        if(field.help){
            $('<span class="ui-icon ui-icon-help" style="margin:3px 0px 8px 0px;cursor:pointer;">')
            .bind('mouseenter',function(field){
                return function(event){
                    $(this).after($('<div class="tooltip">')
                                    .css('position','fixed')
                                    .css('background-color','#E6F1FF')
                                    .css('border','2px solid #6D87A8')
                                    .css('z-index','10000')
                                    .css('top',$(this).offset().top-5)
                                    .css('left',$(this).offset().left+24)
                                    .css('width','200px')
                                    .css('height','auto')
                                    .css('max-height','200px')
                                    .append($('<p>')
                                            .css('margin','4px 4px 4px 4px')
                                            .css('color','#1B3C66')
                                            
                                            .text(field.help)
                                    )
                                   );
                };
            }(field))
            .bind('mouseleave',function(helpCell){
                return function(){
                  $('div',helpCell).remove();  
                };
            }(helpCell))
            .appendTo(helpCell);
        }
        
        var name = 'values['+field.name+']';
        switch(field.type){
            case 'text':
                $('<input type="text">')
                    .attr('name',name)
                    .appendTo(fieldCell);
                break;
            case 'mediumText':
                $('<textarea>')
                    .attr('name',name)
                    .css('width',field.width+'px')
                    .css('height',field.height+'px')
                    .bind('focus',function(field){
                        return function(){
                            $(this).animate({height:field.heightOnFocus},300);
                        };
                    }(field))
                    .bind('blur',function(field){
                        return function(){
                            $(this).animate({height:field.height},300);
                        };
                    }(field))
                    .appendTo(fieldCell);
                break;
            case 'password':
                $('<input type="password">')
                    .attr('name',name)
                    .appendTo(fieldCell);
                break;
            case 'timestamp':
                $('<input type="text">')
                    .attr('name',name)
                    .appendTo(fieldCell);
                break;
            case 'bool':
                $('<input type="hidden" />')
                .attr('name',name)
                .attr('value',1)
                .appendTo(fieldCell);

                var fakeInput = $('<span class="fakeInput bool" tabindex="0">')
                .attr('data-fieldName',name)
                .appendTo(fieldCell);
                $('<span class="boolOn selected">'+field.onLabel+'</span>')
                .appendTo(fakeInput);
                $('<span class="boolOff">'+field.offLabel+'</span>')
                .appendTo(fakeInput);
                fakeInput.bind('click keypress',function(fieldCell){
                    return function(){
                        if(parseInt($('input[type="hidden"]',fieldCell).val())){
                            $('.boolOn',fieldCell).removeClass('selected');
                            $('.boolOff',fieldCell).addClass('selected');
                            $('input[type="hidden"]',fieldCell).val(0);
                        }
                        else{
                            $('.boolOn',fieldCell).addClass('selected');
                            $('.boolOff',fieldCell).removeClass('selected');
                            $('input[type="hidden"]',fieldCell).val(1);
                        }
                    };
                }(fieldCell));
                break;
            case 'color':
                var colorPicker = $('<div style="margin:6px;padding:2px;width:200px;">')
                .attr('data-fieldName',name)
                .appendTo(fieldCell);
                var colorPickerInput = $('<input type="text" value="#ffffff">')
                .attr('data-fieldName',name)
                .attr('name',name)
                .appendTo(fieldCell);
                $.farbtastic(colorPicker,colorPickerInput);
                break;
            case 'html':
                $('<textarea style="display:none;">')
                    .attr('name',name)
                    .appendTo(fieldCell);
                $('<button>')
                    .button('option',{label:'Modifier (Wysiwyg)',icons:{primary:'pencil'}})
                    .bind('click',function(field){
                        return function(){
                          // TODO: insert editor  
                        };
                    }(field))
                    .appendTo(fieldCell);
                $('<br>').appendTo(fieldCell);
                $('<button>')
                .button('option',{label:'Modifier (Code)',icons:{primary:'pencil'}})
                .bind('click',function(field){
                    return function(){
                      // TODO: insert editor  
                    };
                }(field))
                .appendTo(fieldCell);
                break;
            case 'htmlCode':
                $('<textarea style="display:none;">')
                    .attr('name',name)
                    .appendTo(fieldCell);
                $('<button>')
                    .button('option',{label:'Modifier (Code)',icons:{primary:'pencil'}})
                    .bind('click',function(field){
                        return function(){
                          // TODO: insert editor  
                        };
                    }(field))
                .appendTo(fieldCell);
                break;
            case 'selector':
                var selector = $('<select>')
                                .attr('name',name)
                                .appendTo(fieldCell);
                for(var itemIndex in field.items){
                    selector.append(
                            $('<option>')
                                .val(field.items[itemIndex].value)
                                .text(field.items[itemIndex].label)
                            );
                }
                break;
            case 'foreignSelector':
                $('<input type="hidden">')
                .attr('name',name)
                .appendTo(fieldCell);
                $('<div class="fakeInput" tabindex="0">')
                .attr('data-fieldName',name)
                .bind('click keypress',function(field,fieldCell){
                    return function(){
                        var editor = new field.editor();
                        editor.addExtraAction('select',{label:"sélectionner",displayAs:"submit",activeOnSelect:"one",position:-2,callback:function(sel){
                                $('input',fieldCell).val(sel[field.foreignKey]);
                                $('div.fakeInput.erasable',fieldCell).text(sel[field.foreignObjectLabel]);
                                editor.close();
                            }
                        });
                        editor.addExtraAction('cancel',{label:"annuler",displayAs:"cancel",activeOnSelect:"any",position:-1,callback:function(){
                                editor.close();
                            }
                        });
                        editor.load();
                    };
                }(field,fieldCell))
                .appendTo(fieldCell);
                if(field.nullable){
                    $('div.fakeInput',fieldCell).addClass('erasable');
                    $('<div class="fakeInput eraser"><span class="ui-icon ui-icon-closethick"></span></div>')
                    .bind('click keypress',function(field,fieldCell){
                        return function(){
                            $('input',fieldCell).val('');
                            $('div.fakeInput.erasable',fieldCell).text('');
                        };
                    }(field,fieldCell))
                    .appendTo(fieldCell);
                }
                break;
            case 'actionButton':
                break;
        }
    }
    // Add buttons
    if(form.displaySubmitButton){
        $('<button class="submit">')
            .text(form.submitLabel)
            .appendTo(elButtonsDiv)
            .bind('click',function(){
                submit();
            });
    }
    if(form.displayCancelButton){
        $('<button class="cancel">')
            .text(form.cancelLabel)
            .appendTo(elButtonsDiv)
            .bind('click',function(){
                cbOnCancel();
            });
    }
    
    // Add groups tabs
    // Count groups
    var nGroups = 0; for(var i in form.groups) nGroups++;
    // Count lines
    var nLines = 1; for(var i in form.groups){if(form.groups[i].startNewLine)nLines++;}
    if(nGroups){
        var iLine = 0;
        var wastedHeight = 0;
        var padHeight = 0;
        var tabsToolbarWidth = 0;
        var tTabsToolbarWidth = 0;
        for(var groupKey in form.groups){
            var group = form.groups[groupKey];
            if(group.startNewLine||iLine==0){
                if(iLine) $('<br>').appendTo(elGroupsTabs);
                iLine++;
                // Calculate height wasted by padding
                wastedHeight += padHeight;
                // Calculate element padding
                padHeight = (nLines-iLine)*22;
                if(tTabsToolbarWidth>tabsToolbarWidth)tabsToolbarWidth=tTabsToolbarWidth;
                tTabsToolbarWidth=0;
            }
            // Calculate line width
            tTabsToolbarWidth += group.minWidth+20;
            $('<span tabindex="0">')
            .text(group.label)
            .addClass(groupKey)
            .css('minWidth',group.minWidth+'px')
            .css('padding-bottom',padHeight+'px')
            .css('position','relative')
            .css('top','-'+wastedHeight+'px')
            .bind('click keypress',function(group){
                return function(){
                    $('tr[data-fieldKey]',elFieldsTable).hide();
                    for(var i in group.fields) $('tr[data-fieldKey="'+group.fields[i]+'"]',elFieldsTable).show();
                    $('span',elGroupsTabs).removeClass('selected');
                    $(this).addClass('selected');
                };
            }(group))
            .appendTo(elGroupsTabs);
        }
        elGroupsTabs
        .css('height',(nLines*22)+'px')
        .css('width',tabsToolbarWidth+'px')
        .show();
        
        // Select first tab (or default tab)
        if(form.defaultGroup){
            $('span.'+form.defaultGroup,elGroupsTabs).click();
        }
        else{
            for(var groupKey in form.groups){
                $('span.'+groupKey,elGroupsTabs).click();
                break;
            }
            
        }
    }
    
    loadFields();
  };
  
  var loadFields = function(){
      $.getJSON(form.get,elForm.serialize(),fillFields);
  };
  
  var fillFields = function(data){
      for( var fieldIndex in form.fields ){
          var field = form.fields[fieldIndex];
          var name = 'values['+field.name+']';
          
          switch(field.type){
              case 'text':
                  $('input[name="'+name+'"]',elFieldsTable).val(data.object[field.name]);
                  break;
              case 'mediumText':
                  $('textarea[name="'+name+'"]',elFieldsTable).val(data.object[field.name]);
                  break;
              case 'password':
                  $('input[name="'+name+'"]',elFieldsTable).val(data.object[field.name]);
                  break;
              case 'timestamp':
                  $('input[name="'+name+'"]',elFieldsTable).val(data.object[field.name]);
                  break;
              case 'bool':
                  // Default state is 'On', invert if needed.
                  if(!parseInt(data.object[field.name])){
                      $('span.fakeInput[data-fieldName="'+name+'"]',elFieldsTable).click();
                  }
                  break;
              case 'color':
                  // Set input's value
                  //$('input[name="'+name+'"]',elFieldsTable).val(data.object[field.name]);
                  // Set picker color
                  $.farbtastic('div[data-fieldName="'+name+'"]',elFieldsTable).setColor(data.object[field.name]);
              case 'html':
                  break;
              case 'htmlCode':
                  break;
              case 'selector':
                  $('select[name="'+name+'"] > option[value="'+data.object[field.name]+'"]',elFieldsTable).attr('selected','selected');
                  break;
              case 'foreignSelector':
                  $('input[name="'+name+'"]',elFieldsTable).val(data.object[field.name]['Pk']);
                  $('div.fakeInput[data-fieldName="'+name+'"]',elFieldsTable).text(data.object[field.name]['_ObjectString']);
                  break;
              case 'actionButton':
                  break;
          }
      }
  };

  
  var submit = function(){
      $.getJSON(form.set,elForm.serialize(),handleResponse);
  };
  
  var handleResponse = function(data){
      if(data!=true){
          elErrorMsg.show();

         for( var fieldIndex in form.fields ){
             var field = form.fields[fieldIndex];
             var name = 'values['+field.name+']';
             if(data[field.name]==undefined){
                 $('[name="'+name+'"]',elForm).removeClass('corrected uncorrectable');
             }
             else{
                 if(data[field.name]['status']=='corrected'){
                     $('[name="'+name+'"]',elForm).val(data[field.name].value).addClass('corrected').removeClass('uncorrectable');
                 }
                 else{
                     $('[name="'+name+'"]',elForm).addClass('uncorrectable').removeClass('corrected');
                 }
             }
         }
         setTabsStatus();
         return false;
      }
      
      cbOnSubmit();
      return false;
  };
  
  var setTabsStatus = function(){
      for( var groupKey in form.groups){
          var tabStatus = 'valid';
          for(var i in form.groups[groupKey].fields){
              var fieldKey = form.groups[groupKey].fields[i];
              var tr = $('tr[data-fieldKey="'+fieldKey+'"]',elFieldsTable);
              if($('input',tr).hasClass('corrected')){
                  if(tabStatus=='valid')tabStatus = 'corrected';
              }
              else if($('input',tr).hasClass('uncorrectable')){
                  tabStatus = 'uncorrectable';
              }
          }
          if(tabStatus=='corrected'){
              $('span.'+groupKey,elGroupsTabs).addClass('corrected');
              $('span.'+groupKey,elGroupsTabs).removeClass('corrected');
          }
          else if(tabStatus=='uncorrectable'){
              $('span.'+groupKey,elGroupsTabs).removeClass('corrected');
              $('span.'+groupKey,elGroupsTabs).addClass('uncorrectable');
          }
          else{
              $('span.'+groupKey,elGroupsTabs).removeClass('corrected');
              $('span.'+groupKey,elGroupsTabs).removeClass('uncorrectable');
          }
      }     
  };
  
};
//###############################################



//-----------------------------------------------CWA LIBS UI DATAGRID
cwa.libs.ui.datagrid = function(){
    var me = this;
    
    // private 
    var datagrid = {
            parent:null,
            get:"",
            displayHeaders: true,
            displayFilters: true,
            displayPaginator: true,
            displayOrderByFields: true,
            selectable: true, // true, 'multi', false
            limit:12,
            actions:{},
            primaryKey:"id",
            columns:[],
            orderBy:[]
    };
    
    var actionPattern = {
        label:"",
        activeOnSelect:"any", // 'any', 'none', 'one', 'multi'
        activeOnFilter:[],
        displayAs:"",
        position:0,
        callback:null
    };
    
    var columnPattern = {
            label:"No label",
            width:30,
            filters:{},
            fieldName:"",
            fieldType:"text"
    };
    
     var filterPattern = {
            label:"No label",
            type:"text",
            filter:"",
            width:null
    };
    
    var orderByPattern = {
            label:"No label",
            orderBy:"",
            selected:"false"
    };
    
    var selectedLines = {};
    
    var paginator = {
            debug:false,
            total:0,
            offset:0,
            oldOffset:0,
            move:function(arg){
                var oldOffset = paginator.offset;
                switch(arg){
                    case 'first':
                        if(paginator.debug) alert('moving to first');
                        paginator.offset = 0;
                        break;
                    case 'prev':
                        if(paginator.debug) alert('moving to previous');
                        paginator.offset -= datagrid.limit;
                        break;
                    case 'next':
                        if(paginator.debug) alert('moving to next');
                        paginator.offset += datagrid.limit;
                        break;
                    case 'last':
                        if(paginator.debug) alert('moving to last');
                        paginator.offset = Math.floor((paginator.total-1)/datagrid.limit)*datagrid.limit;
                        break;
                    default: 
                        if(paginator.debug) alert('moving to '+arg);
                        paginator.offset = (arg-1)*datagrid.limit;
                        break;
                }

                if(paginator.offset<0){              
                    paginator.offset = 0;
                    if(paginator.debug) alert('Offset < 0 -> reset to 0.');
                }
                else if(paginator.offset>(paginator.total-1)){
                    paginator.offset = Math.floor((paginator.total-1)/datagrid.limit)*datagrid.limit;
                    if(paginator.debug) alert('Offset > total-1 -> reset to '+paginator.offset);
                }
                
                if(paginator.debug) alert('Displaying new values');
                $('input[name="page"]',thead).val((paginator.offset/datagrid.limit)+1);
                $('input[name="offset"]',thead).val(paginator.offset);
                if(paginator.offset!=oldOffset){
                    if(paginator.debug) alert('Reloading lines');
                    me.triggerLoadLines();
                }
                
            },
            reset: function(total,offset){
                if(paginator.debug) alert('Resetting paginator');
                paginator.total = parseInt(total);
                paginator.offset = parseInt(offset);
                $('input[name="page"]',thead).val((paginator.offset/datagrid.limit)+1);
                $('input[name="offset"]',thead).val(paginator.offset);
                $('span.total',thead).text(Math.ceil(paginator.total/datagrid.limit));
            }
    };

    var cbOnReady = function(){};
    var cbOnSelect = function(){};

    var form = $('<form onsubmit="return false" class="cpiDatagrid">');
    var table = $('<table cellpadding=0 cellspacing=0>').appendTo(form);
    var thead = $('<thead>').appendTo(table);
    var tbody = $('<tbody>').appendTo(table);
    var tfoot = $('<tfoot>').appendTo(table);
    
    /* SETTERS */
    me.setCbOnReady = function(cb){
        if(typeof cb == 'function') cbOnReady = cb;
    };
    me.setCbOnSelect = function(cb){
        if(typeof cb == 'function') cbOnSelect = cb;
    };
    
    /* GETTERS */
    me.getWidth = function(){
        var tableWidth = 0;
        for(var colIndex in datagrid.columns){
            var col = datagrid.columns[colIndex];
            tableWidth += parseInt(col.width);
        }
        return tableWidth;
    };
    
    /* PUBLIC METHODS */
    me.load = function(config){
        
        $(config.parent).empty().append(form);
        
        // Merge config with datagrid
        for(var obfIndex in config.orderByFields){
            config.orderByFields[obfIndex] = $.extend({},orderByFieldPattern,config.orderByFields[obfIndex]);
        }
        for(var actionKey in config.actions){
            config.actions[actionKey] = $.extend({},actionPattern,config.actions[actionKey]);
        }
        for(var colIndex in config.columns){
            for(var filterIndex in config.columns[colIndex].filters){
                config.columns[colIndex].filters[filterIndex] = $.extend({},filterPattern,config.columns[colIndex].filters[filterIndex]);
            }
            config.columns[colIndex] = $.extend({},columnPattern,config.columns[colIndex]);
        }
        $.extend(true,datagrid,config);
        
        buildDatagrid();
    };
    
    var buildDatagrid = function(){
        
        // ADD TABLE WIDTH
        table.attr('width',me.getWidth());
        // ADD COLUMN ELEMENTS
        for(var colIndex in datagrid.columns){
            var col = datagrid.columns[colIndex];
            thead.before($('<col width='+col.width+'>'));
        }
        
        
        // ADD FIELDS DESCRIPTION
        for(var colIndex in datagrid.columns){
            var col = datagrid.columns[colIndex];
            form.append(
                $('<input type="hidden" name="fields['+colIndex+']" value="'+col.fieldName+'">')
            );
        }
        form.append(
            $('<input type="hidden" name="fields['+(++colIndex)+']" value="'+datagrid.primaryKey+'">')
        );
        
        // ADD HEADERS
        var trHeaders = $('<tr>').appendTo(thead);
        for(var colIndex in datagrid.columns){
            var col = datagrid.columns[colIndex];
            trHeaders.append(
                $('<td>').text(col.label).css('width',col.width)
            );
        }

        // ADD FILTERS
        var trFilters = $('<tr class="filters">').appendTo(thead);
        for(var colIndex in datagrid.columns){
            var col = datagrid.columns[colIndex];
            var tdFilters = $('<td>').css('width',col.width).appendTo(trFilters);
            for(var filterIndex in col.filters){
                var filter = col.filters[filterIndex];
                tdFilters.append(
                        $('<input type="'+filter.type+'" style="width:'+
                                (filter.width?(filter.width-10):(col.width-10))
                                +'px" name="filter['+filter.filter+']">')
                );
            }   
        }
        $('input[type="text"]',trFilters).bind('keyup',function(){
            me.triggerLoadLines();
        });
        $('input[type="checkbox"]',trFilters).bind('change',function(){
            me.triggerLoadLines();
        });
        
        //ADD PAGINATOR
        var nCols = 0;
        for(var i in datagrid.columns) nCols++;
        var trPaginator = $('\
<tr class="paginator">\
    <td colspan="'+nCols+'">\
        <span class="first"></span>\
        <span class="prev"></span>\
        <span class="n">Page n°&nbsp;\
        <input type="text" value=0 name="page" />\
        <input type="hidden" value=0 name="offset" />\
        <input type="hidden" value=0 name="limit" />\
        &nbsp;sur&nbsp;<span class="total"></span></span>\
        <span class="next"></span>\
        <span class="last"></span>\
    </td>\
</tr>\
').appendTo(thead);
        $('input[name="limit"]',thead).val(datagrid.limit);
        $('span.first',thead).bind('click',function(){
            paginator.move('first');
        });
        $('span.prev',thead).bind('click',function(){
            paginator.move('prev');
        });
        $('span.next',thead).bind('click',function(){
            paginator.move('next');
        });
        $('span.last',thead).bind('click',function(){
            paginator.move('last');
        });
        $('input[name="page"]',thead).bind('change',function(){
            paginator.move($(this).val());
        });
        
        // ADD ORDER BY OPTIONS
        var trFoot = $('<tr>').appendTo(tfoot);
        var tdFoot = $('<td>Classer par:&nbsp;</td>').appendTo(trFoot);
        var nCols = 0;
        for(var i in datagrid.columns) nCols++;
        tdFoot.attr('colspan',nCols);
        var selectOrderBy = $('<select name="filter[ORDER BY][0]">').appendTo(tdFoot);
        for(var orderByIndex in datagrid.orderBy){
            var orderBy = datagrid.orderBy[orderByIndex];
            selectOrderBy.append(
                    $('<option value="'+orderBy.orderBy+'::ASC">'+orderBy.label+' - asc</option>')
            );
            selectOrderBy.append(
                    $('<option value="'+orderBy.orderBy+'::DESC">'+orderBy.label+' - dsc</option>')
            );
        }
        $('option[value="'+datagrid.defaultOrderBy+'"]',selectOrderBy).attr('selected','selected');
        selectOrderBy.bind('change',function(){
            me.triggerLoadLines();
        });
        
        // ADD ACTIONS
        var spanActions = $('<span>').appendTo(tdFoot);
        var sortedActions = [];
        // Select displayable actions
        for(var actionKey in datagrid.actions){
            var action = datagrid.actions[actionKey];
            if(action.label.length>0){
                sortedActions.push(action);
            }
        }
        // Sort actions
        sortedActions.sort(function(a,b){return (a.position-b.position);});
        // build actions
        var isFirst = true;
        for(var actionKey in sortedActions){
            var action = sortedActions[actionKey];
            if(action.position == 0 && !isFirst){
                spanActions.append('<span class="separator">&nbsp;|&nbsp;</span>');
            }
            var aAction = $('<a>')
                                .addClass('disabled')
                                .addClass(action.activeOnSelect)
                                .addClass(action.displayAs)
                                .text(action.label)
                                .appendTo(spanActions);
            for( var filterIndex in action.activeOnFilter){
                var filterName = action.activeOnFilter[filterIndex].replace(/[^a-zA-Z]/g,'_');
                aAction.addClass('needFilter'+filterName);
            }
            aAction.bind('click',function(action){
                return function(){
                    if($(this).hasClass('disabled')) return false;
                    execAction(action);
                };
            }(action));
            isFirst = false;
        }
        
        
        loadLines();
    };
    
    // LINES
    var loadLines = function(){
        $.getJSON(datagrid.get,form.serialize(),buildLines);
    };
    
    var buildLines = function(data){
        paginator.reset(data.total,data.offset);
        tbody.empty();
        for(var i in data.lines){
            var line = data.lines[i];
            var trLine = $('<tr>')
                .attr('data-primaryKey',line[datagrid.primaryKey])
                .appendTo(tbody);
            for(var colIndex in datagrid.columns){
                var col = datagrid.columns[colIndex];
                trLine
                    .append($('<td class="'+col.fieldType+'">')
                        .append($('<span style="width:100%;">')
                            .text(line[col.fieldName])
                        )
                    );
            }
            trLine
                .bind('click',
                    function(line){
                         return function(){
                             selectLine(line);
                         };
                    }(line)
                );
        }
        // Fill table with empty lines
        var emptyLines = datagrid.limit-data.lines.length;
        for( var i=0;i<emptyLines;i++){
            var tr = $('<tr>').appendTo(tbody);
            var td = $('<td class="empty">').appendTo(tr);
            td.attr('colspan',datagrid.columns.length);            
            $('<span>')
            .text(' ')
            .appendTo(td);
        }
        updateSelection();
    };
    
    // SELECTION
    var selectLine = function(line){
        if(datagrid.selectable === true){
            var pk = line[datagrid.primaryKey];
            $('tr.selected',tbody).removeClass('selected');
            $('tr[data-primaryKey="'+pk+'"]',tbody).addClass('selected');
            selectedLines = {};
            selectedLines['0']=line;
            cbOnSelect(line);
        }
        else if(datagrid.selectable === 'multi'){
            var pk = line[datagrid.primaryKey];
            if(selectedLines[pk]!=null){
                delete selectedLines[pK];
                $('tr[data-primaryKey="'+pk+'"]',tbody).removeClass('selected');                                     
            }
            else{
                selectedLines[pk]=line;
                $('tr[data-primaryKey="'+pk+'"]',tbody).addClass('selected');                                     
            }
            cbOnSelect(selectedLines);
        }
        enableActions();        
    };
    
    var updateSelection = function(){
        if(datagrid.selectable === true){
            // Try to re-select same line
            if(selectedLines['0']){
                var line = selectedLines['0'];
                var pk = line[datagrid.primaryKey];
                if($('tr[data-primaryKey="'+pk+'"]',tbody).size()==1){
                    $('tr[data-primaryKey="'+pk+'"]',tbody).click();
                }
                else{
                    selectedLines = {};
                    cbOnSelect(false);
                }
            }
        }
        else if(datagrid.selectable === 'multi'){
            // Reset selected lines
            selectedLines = {};
        }
        enableActions();     
    };
    
    var getSelection = function(){
        if(datagrid.selectable === true){
            return getSelectionOne();            
        }
        else if(datagrid.selectable === 'multi'){
            return getSelectionMulti();
        }
    };
    
    var getSelectionOne = function(){
        if(selectedLines['0'] != undefined){
            return selectedLines['0'];
        }
        else{
            return false;
        }        
    };
    
    var getSelectionMulti = function(){
        return selectedLines;        
    };
    
    // ACTIONS
    var enableActions = function(){
        // Apply activeOnSelection rules
        $('a.any',tfoot).removeClass('disabled');
        var nLines = 0;
        for(var i in selectedLines) nLines++;
        if(nLines == 0){
            $('a.none',tfoot).removeClass('disabled');
            $('a.one',tfoot).addClass('disabled');
            $('a.multi',tfoot).addClass('disabled');
        }
        else if(nLines == 1){
            $('a.none',tfoot).addClass('disabled');
            $('a.one',tfoot).removeClass('disabled');
            $('a.multi',tfoot).removeClass('disabled');
        }
        else if(nLines > 1){
            $('a.none',tfoot).addClass('disabled');
            $('a.one',tfoot).addClass('disabled');
            $('a.multi',tfoot).removeClass('disabled');            
        }
        // Apply activeOnFilter rules
        $('tr.filters input',thead).each(function(index,element){
            element = $(element);
            if(element.val()==''){
                filter = element.attr('name');
                filter = filter.substring(7,(filter.length-1));
                filter = filter.replace(/[^a-zA-Z]/g,'_');
                $('a.needFilter'+filter,tfoot).addClass('disabled');
            }
        });
    };
    
    var execAction = function(action){
        if(action.activeOnSelect=='multi'||action.activeOnSelect=='any'){
            action.callback(getSelectionMulti());
        }
        else if(action.activeOnSelect=='one'){
            action.callback(getSelectionOne());            
        }
        else{
            action.callback();
        }
    };
    
    
    // INTERACTIONS
    me.setFilterValue = function(filter,value){
        if( value != $('input[name="filter['+filter+']"]').val()){
            $('input[name="filter['+filter+']"]').val(value);
            me.triggerLoadLines();
        }
    };

    me.getFilterValue = function(filter){
        return $('input[name="filter['+filter+']"]').val();
    };
    
    me.getSelection = function(){
      return getSelection();  
    };


    me.triggerLoadLines = function (){
      if( typeof me.triggerLoadLines.timer != 'undefined'){
        clearTimeout( me.triggerLoadLines.timer );
      }
      me.triggerLoadLines.timer = setTimeout(loadLines,500);
    };
    
};
//###############################################



//----------------------------------------------- CWA LIBS UI BROWSER
cwa.libs.ui.browser = function(){
 var me = this;

 /* PRIVATE MEMBERS */
 me.elRoot =    $('<div>');    
 var cbOnClose = function(){};    
 var cbOnSelect = function(){};
 
 var dialog = null;
 var datagrid = null;
 
 // Default Config
 var config = {
         title:'Browser',
         dialog:true,
         editor:null,
         datagrid:{
             parent:me.elRoot,
             limit:12,
             primaryKey:'id',
             get:'',
             del:'',
             actions:{
                 add:{label:"nouveau",activeOnSelect:"any",position:0,callback:function(){me.add();}},
                 edit:{label:"modifier",activeOnSelect:"one",position:1,callback:function(sel){me.edit(sel);}},
                 del:{label:"supprimer",activeOnSelect:"one",position:1,callback:function(sel){me.del(sel);}}
               },
             columns:[],
             orderBy:[],
             defaultOrderBy:''
         }
 }; 
 
 
 /* SETTERS */
 me.setCbOnClose = function(cb){
     if(typeof cb == 'function') cbOnClose = cb;
 };
 me.setCbOnSelect = function(cb){
     if(typeof cb == 'function') cbOnSelect = cb;
 };
 me.setConfig = function(c){
     config = $.extend(true,config,c);
 };
 /* GETTERS */
 me.getDatagrid = function(){
     return datagrid;
 };
 
 /* PUBLIC METHODS */
 me.load = function(){

     datagrid = new cwa.libs.ui.datagrid();
     datagrid.setCbOnSelect(cbOnSelect);
     datagrid.load(config.datagrid);
     
     if(config.dialog){
         dialog = new cwa.libs.ui.dialog();
         dialog.setTitle(config.title);
         dialog.load(me.elRoot);
         dialog.show();
     }
 };
 
 me.close = function(){
     if(config.dialog) dialog.close();
 };
 
 me.add = function(){
     var editor = new config.editor();
     editor.setCbOnClose(datagrid.triggerLoadLines);
     editor.load();
 };
 
 me.edit = function(sel){
     var editor = new config.editor();
     editor.setCbOnClose(datagrid.triggerLoadLines);
     editor.setPk(sel[config.datagrid.primaryKey]);
     editor.load();
 };
 
 me.del = function(sel){
     if(confirm("Etes vous certain de vouloir supprimer cet élément?\n\
     		     - Tout les éléments liés seront supprimés en cascade.\n\
     		     - Cette supression sera définitive.")){
         $.getJSON(config.datagrid.del,{Pk:sel[config.datagrid.primaryKey]},function(){
             datagrid.triggerLoadLines();
         });
     }
 };
 
 me.addExtraAction = function(actionName,action){
     config.datagrid.actions[actionName] = action;
 };
 
 me.delAction = function(actionName){
     delete config.datagrid.actions[actionName];
 };
 
 me.addExtraColumn = function(columnName,column){
     config.datagrid.columns[columnName] = column;
 };
 
 me.delColumn = function(columnName){
     delete config.datagrid.columns[columnName];
 };
};
//###############################################



//----------------------------------------------- CWA LIBS UI EDITOR
cwa.libs.ui.editor = function(){
  var me = this;
  
  /* PRIVATE MEMBERS */
  me.elRoot =    $('<div>');
  
  var cbOnClose = function(){};
  
  var dialog = null;
  var form = new cwa.libs.ui.form();
  
  var pk = null;
  
  // Default config
  var config = {
          title:'Form',
          dialog:true,
          form:{
              parent:me.elRoot,
              get:'',
              set:'',
              fields:{}
          }
      };
  
  /* SETTERS */
  me.setPk = function(value){
      pk = value;
  };
  me.setCbOnClose = function(cb){
      if(typeof cb == 'function') cbOnClose = cb;
  };
  me.setConfig = function(c){
      config = $.extend(true,config,c);
  };
  
  /* PUBLIC METHODS */
  me.load = function(){   
      
      form.setCbOnCancel(close);
      form.setCbOnSubmit(close);
      form.setPk(pk);
      form.load(config.form);

      if(config.dialog){
          dialog = new cwa.libs.ui.dialog();
          dialog.setTitle(config.title);
          dialog.load(me.elRoot);    
          dialog.show();
      }
  };
  
  
  var close = function(){
      cbOnClose();
      if(config.dialog) dialog.close();
  };
};
//###############################################




//----------------------------------------------- CWA LIBS UI HIERARCHICAL BROWSER
cwa.libs.ui.hierarchicalBrowser = function(){
    var me = this;

    /* PRIVATE MEMBERS */
    var elRoot = $('<div>');
    var elHBox = $('<div style="position:relative;padding:0;margin:0;">').appendTo(elRoot);
    var elWidget1 = $('<div style="position:absolute;top:0px;left:0px;height:100%;padding:0;margin:0;overflow:hidden">').appendTo(elHBox);
    var elSeparator = $('<div style="height:100%;" class="verticalSeparator">').appendTo(elHBox);
    var elWidget2 = $('<div style="position:absolute;top:0px;height:100%;padding:0;margin:0;overflow:hidden">').appendTo(elHBox);
    
    var dialog = null;
    var browser = new cwa.libs.ui.browser();
    var datagrid = new cwa.libs.ui.datagrid();
    
    // Default config
    var config = {
        title:'',
        dialog:true,
        height:403,
        browserWidth:300,
        datagridWidth:690,
        browser:{
            dialog:false,
            editor:null,
            datagrid:{
                parent:elWidget1,
                limit:12,
                primaryKey:'id',
                get:'',
                columns:[],
                orderBy:[],
                defaultOrderBy:''
            }
        },
        datagrid:{
            parent:elWidget2,
            limit:12,
            primaryKey:'id',
            get:'',
            actions:{
                add:{label:"ajouter",activeOnSelect:"any",activeOnFilter:['xtidGroup::=='],position:0,callback:function(){alert('Not implemented!');}},
                del:{label:"retirer",activeOnSelect:"multi",position:1,callback:function(sel){alert('Not implemented!');}}
              },
            selectable:"multi",
            columns:[],
            orderBy:[],
            defaultOrderBy:''
           },
        link:{
            browserField:'',
            datagridFilter:''
        }
    };
    
    /* SETTERS */
    me.setConfig = function(c){
        config = $.extend(true,config,c);
    };
    
    /* GETTERS */
    me.getBrowser = function(){
        return browser;  
      };
    me.getDatagrid = function(){
      return datagrid;  
    };
    
    /* PUBLIC METHODS */
    me.load = function(){
            
        browser.setCbOnSelect(updateDatagridFilter);
        browser.setConfig(config.browser);
        browser.load();
        
        datagrid.load(config.datagrid);
        
        var browserWidth = browser.getDatagrid().getWidth();
        var datagridWidth = datagrid.getWidth();
        
        elHBox.css({
            width:(browserWidth + datagridWidth + 10),
            height:config.height
        });
        elWidget1.css('width',browserWidth);
        elSeparator.css('left',browserWidth);
        elWidget2.css({
            left:browserWidth+10,
            width:datagridWidth
        });

        if(config.dialog){
            dialog = new cwa.libs.ui.dialog();
            dialog.setTitle(config.title);
            dialog.load(elRoot);
            dialog.show();
        }
    };
    
    var updateDatagridFilter = function(line){
        datagrid.setFilterValue(config.link.datagridFilter,line?line[config.link.browserField]:'');
    };
};
//###############################################



//----------------------------------------------- CWA UI AUTH MEMBERSHIP BROWSER
cwa.ui.Auth.MembershipBrowser = function(){
    cwa.libs.ui.hierarchicalBrowser.call(this);
    var me = this;

    me.setConfig({
        title:'Groupes d\'utilisateurs',
        browser:{
            editor:cwa.ui.Auth.GroupEditor,
            datagrid:{
                primaryKey:'id',
                get:'/app/Auth/GroupGetMine.json',
                del:'/app/Auth/GroupDel.json',
                columns:[
                         {
                             label:"Id",
                             fieldName:"id",
                             fieldType:"id",
                             width:50,
                             filters:[
                                      {type:"text",filter:"id::LIKE*"}
                                      ]
                         },
                         {
                             label:"Groupe",
                             fieldName:"name",
                             fieldType:"text",
                             width:280,
                             filters:[
                                      {type:"text",filter:"name::LIKE*"}
                                      ]
                         }
                         ],
                 orderBy:[
                          {label:"Id",orderBy:"id"},
                          {label:"Groupe",orderBy:"name"}
                          ],
                 defaultOrderBy:"name::ASC",
                 actions:{
                 }
            }
        },
        datagrid:{
            primaryKey:'id',
            get:'/app/Auth/MembershipGet.json',
              columns:[
                       {
                         label:"Id",
                         fieldName:"user->id",
                         fieldType:"id",
                         width:50,
                         filters:[
                           {type:"text",filter:"user->id::LIKE*"},
                           {type:"hidden",filter:"xtidGroup::=="}
                         ]
                       },
                       {
                         label:"Utilisateur",
                         fieldName:"user->getFullName()",
                         fieldType:"text",
                         width:200,
                         filters:[
                           {type:"text",filter:"user->firstname||user->lastname::LIKE*"}
                         ]
                       },
                       {
                         label:"Login",
                         fieldName:"user->login",
                         fieldType:"text",
                         width:200,
                         filters:[
                           {type:"text",filter:"user->login::LIKE*"}
                         ]
                       },
                     ],
                     orderBy:[
                       {label:"Id",orderBy:"user->id"},
                       {label:"Nom",orderBy:"user->lastname"},
                       {label:"Login",orderBy:"user->login"}
                     ],
                     defaultOrderBy:"user->lastname::ASC",
                     actions:{
                         add:{
                             callback:function(){
                                 var browser = new cwa.ui.Auth.UserBrowser();
                                 browser.addExtraAction('validate',{label:"valider",activeOnSelect:"one",position:-2,
                                         displayAs:"submit",
                                         callback:function(line){
                                             var data = {};
                                             data['values[xtidGroup]'] = me.getDatagrid().getFilterValue('xtidGroup::==');
                                             data['values[xtidUser]'] = line.id;
                                             $.getJSON('/app/Auth/MembershipSet.json',data,function(data){
                                                 if(data===true){
                                                     browser.close();
                                                     me.getDatagrid().triggerLoadLines();
                                                 }
                                                 else{
                                                     switch(data['errno']){
                                                         case 1062:
                                                             message = 'Cet utilisateur est déjà membre de ce groupe!';
                                                             break;
                                                         default:
                                                             message = 'Erreur inconnue ('+data['errno']+'), merci de contacter l\'administrateur.';
                                                     }
                                                     alert(message);
                                                 }
                                             });
                                 }});
                                 browser.addExtraAction('cancel',{label:"annuler",activeOnSelect:"any",position:-1,displayAs:"cancel",callback:function(){
                                     browser.close();
                                 }});
                                 browser.load();        
                             }
                         },
                         del:{
                             callback:function(selection){
                                 var msg = 'Voulez-vous retirer ces utilisateurs?\n\n';
                                 for(var i in selection){
                                     msg+= '-  ' + selection[i]['user->id'] + '  ';
                                     msg+= selection[i]['user->getFullName()'] + ' (';
                                     msg+= selection[i]['user->login'] + ' )\n\n';
                                 }
                                 if(confirm(msg)){
                                     var del = function(selection){
                                         var me2 = this;
                                         // Transform selection object in array
                                         me2.selection = [];
                                         for( var i in selection){
                                             me2.selection.push(selection[i]);
                                         }
                                         me2.delNext = function(){
                                             if(me2.selection.length){
                                                 var sel = me2.selection.shift();
                                                 var data = {Pk:sel['id']};
                                                 $.getJSON('/app/Auth/MembershipDel.json',data,function(data){
                                                     me2.delNext();
                                                 });  
                                             }
                                             else{
                                                 me.getDatagrid().triggerLoadLines();
                                             }
                                         };
                                         me2.delNext();
                                     }(selection);         
                                 }
                             }
                         }
                       }
           },
        link:{
            browserField:'id',
            datagridFilter:'xtidGroup::=='
        }
        
    });
};
//###############################################



//----------------------------------------------- CWA UI AUTHORIZATION BROWSER
cwa.ui.Auth.AuthorizationBrowser = function(){
  cwa.libs.ui.hierarchicalBrowser.call(this);
  var me = this;

  me.setConfig({
      title:'Autorisations',
      browser:{
          editor:cwa.ui.Auth.GroupEditor,
          datagrid:{
              get:'/app/Auth/GroupGet.json',
              del:'/app/Auth/GroupDel.json',
              columns:[
                       {
                           label:"Id",
                           fieldName:"id",
                           fieldType:"id",
                           width:50,
                           filters:[
                                    {type:"text",filter:"id::LIKE*"}
                                    ]
                       },
                       {
                           label:"Groupe",
                           fieldName:"name",
                           fieldType:"text",
                           width:280,
                           filters:[
                                    {type:"text",filter:"name::LIKE*"}
                                    ]
                       }
                       ],
               orderBy:[
                        {label:"Id",orderBy:"id"},
                        {label:"Groupe",orderBy:"name"}
                        ],
               defaultOrderBy:"name::ASC"
          }
      },
      datagrid:{
          get:'/app/Auth/AuthorizationGet.json',
            columns:[
                     {
                       label:"Id",
                       fieldName:"right->id",
                       fieldType:"id",
                       width:50,
                       filters:[
                         {type:"text",filter:"right->id::LIKE*"},
                         {type:"hidden",filter:"xtidGroup::=="}
                       ]
                     },
                     {
                       label:"Droit",
                       fieldName:"right->name",
                       fieldType:"text",
                       width:150,
                       filters:[
                         {type:"text",filter:"right->name::LIKE*"}
                       ]
                     },
                     {
                       label:"Description",
                       fieldName:"right->description",
                       fieldType:"text",
                       width:450,
                       filters:[
                         {type:"text",filter:"right->description::LIKE*"}
                       ]
                     },
                   ],
                   orderBy:[
                     {label:"Id",orderBy:"right->id"},
                     {label:"Nom",orderBy:"right->name"}
                   ],
                   defaultOrderBy:"right->name::ASC",
                   actions:{
                       add:{
                           callback:function(){
                               var browser = new cwa.ui.Auth.RightBrowser();
                               browser.addExtraAction('validate',{label:"valider",activeOnSelect:"one",position:-2,
                                       displayAs:"submit",
                                       callback:function(line){
                                           var data = {};
                                           data['values[xtidGroup]'] = me.getDatagrid().getFilterValue('xtidGroup::==');
                                           data['values[xtidRight]'] = line.id;
                                           $.getJSON('/app/Auth/AuthorizationSet.json',data,function(data){
                                               if(data===true){
                                                   browser.close();
                                                   me.getDatagrid().triggerLoadLines();
                                               }
                                               else{
                                                   switch(data['errno']){
                                                       case 1062:
                                                           message = 'Ce droit est déjà attribué à ce groupe!';
                                                           break;
                                                       default:
                                                           message = 'Erreur inconnue ('+data['errno']+'), merci de contacter l\'administrateur.';
                                                   }
                                                   alert(message);
                                               }
                                           });
                               }});
                               browser.addExtraAction('cancel',{label:"annuler",activeOnSelect:"any",position:-1,displayAs:"cancel",callback:function(){
                                   browser.close();
                               }});
                               browser.load();        
                           }
                       },
                       del:{
                           callback:function(selection){
                               var msg = 'Voulez-vous retirer ces droits?\n\n';
                               for(var i in selection){
                                   msg+= '-  ' + selection[i]['right->id'] + '  ';
                                   msg+= selection[i]['right->name'] + ' (';
                                   msg+= selection[i]['right->description'] + ' )\n\n';
                               }
                               if(confirm(msg)){
                                   var del = function(selection){
                                       var me2 = this;
                                       // Transform selection object in array
                                       me2.selection = [];
                                       for( var i in selection){
                                           me2.selection.push(selection[i]);
                                       }
                                       me2.delNext = function(){
                                           if(me2.selection.length){
                                               var sel = me2.selection.shift();
                                               var data = {Pk:sel['id']};
                                               $.getJSON('/app/Auth/AuthorizationDel.json',data,function(data){
                                                   me2.delNext();
                                               });  
                                           }
                                           else{
                                               me.getDatagrid().triggerLoadLines();
                                           }
                                       };
                                       me2.delNext();
                                   }(selection);         
                               }
                           }
                       }
                   }
         },
      link:{
          browserField:'id',
          datagridFilter:'xtidGroup::=='
      }
      
  });
};
//###############################################



//----------------------------------------------- CWA UI AUTH GROUPMANAGER BROWSER
cwa.ui.Auth.GroupManagerBrowser = function(){
  cwa.libs.ui.hierarchicalBrowser.call(this);
  var me = this;

  me.setConfig({
      title:'Gérants des groupes d\'utilisateurs',
      browser:{
          editor:cwa.ui.Auth.GroupEditor,
          datagrid:{
              primaryKey:'id',
              get:'/app/Auth/GroupGet.json',
              del:'/app/Auth/GroupDel.json',
              columns:[
                       {
                           label:"Id",
                           fieldName:"id",
                           fieldType:"id",
                           width:50,
                           filters:[
                                    {type:"text",filter:"id::LIKE*"}
                                    ]
                       },
                       {
                           label:"Groupe",
                           fieldName:"name",
                           fieldType:"text",
                           width:280,
                           filters:[
                                    {type:"text",filter:"name::LIKE*"}
                                    ]
                       }
                       ],
               orderBy:[
                        {label:"Id",orderBy:"id"},
                        {label:"Groupe",orderBy:"name"}
                        ],
               defaultOrderBy:"name::ASC",
               actions:{
               }
          }
      },
      datagrid:{
          primaryKey:'id',
          get:'/app/Auth/GroupManagerGet.json',
            columns:[
                     {
                       label:"Id",
                       fieldName:"user->id",
                       fieldType:"id",
                       width:50,
                       filters:[
                         {type:"text",filter:"user->id::LIKE*"},
                         {type:"hidden",filter:"xtidGroup::=="}
                       ]
                     },
                     {
                       label:"Utilisateur",
                       fieldName:"user->getFullName()",
                       fieldType:"text",
                       width:200,
                       filters:[
                         {type:"text",filter:"user->firstname||user->lastname::LIKE*"}
                       ]
                     },
                     {
                       label:"Login",
                       fieldName:"user->login",
                       fieldType:"text",
                       width:200,
                       filters:[
                         {type:"text",filter:"user->login::LIKE*"}
                       ]
                     },
                   ],
                   orderBy:[
                     {label:"Id",orderBy:"user->id"},
                     {label:"Nom",orderBy:"user->lastname"},
                     {label:"Login",orderBy:"user->login"}
                   ],
                   defaultOrderBy:"user->lastname::ASC",
                   actions:{
                       add:{
                           callback:function(){
                               var browser = new cwa.ui.Auth.UserBrowser();
                               browser.addExtraAction('validate',{label:"valider",activeOnSelect:"one",position:-2,
                                       displayAs:"submit",
                                       callback:function(line){
                                           var data = {};
                                           data['values[xtidGroup]'] = me.getDatagrid().getFilterValue('xtidGroup::==');
                                           data['values[xtidUser]'] = line.id;
                                           $.getJSON('/app/Auth/GroupManagerSet.json',data,function(data){
                                               if(data===true){
                                                   browser.close();
                                                   me.getDatagrid().triggerLoadLines();
                                               }
                                               else{
                                                   switch(data['errno']){
                                                       case 1062:
                                                           message = 'Cet utilisateur est déjà gérant de ce groupe!';
                                                           break;
                                                       default:
                                                           message = 'Erreur inconnue ('+data['errno']+'), merci de contacter l\'administrateur.';
                                                   }
                                                   alert(message);
                                               }
                                           });
                               }});
                               browser.addExtraAction('cancel',{label:"annuler",activeOnSelect:"any",position:-1,displayAs:"cancel",callback:function(){
                                   browser.close();
                               }});
                               browser.load();        
                           }
                       },
                       del:{
                           callback:function(selection){
                               var msg = 'Voulez-vous retirer ces gérants?\n\n';
                               for(var i in selection){
                                   msg+= '-  ' + selection[i]['user->id'] + '  ';
                                   msg+= selection[i]['user->getFullName()'] + ' (';
                                   msg+= selection[i]['user->login'] + ' )\n\n';
                               }
                               if(confirm(msg)){
                                   var del = function(selection){
                                       var me2 = this;
                                       // Transform selection object in array
                                       me2.selection = [];
                                       for( var i in selection){
                                           me2.selection.push(selection[i]);
                                       }
                                       me2.delNext = function(){
                                           if(me2.selection.length){
                                               var sel = me2.selection.shift();
                                               var data = {Pk:sel['id']};
                                               $.getJSON('/app/Auth/GroupManagerDel.json',data,function(data){
                                                   me2.delNext();
                                               });  
                                           }
                                           else{
                                               me.getDatagrid().triggerLoadLines();
                                           }
                                       };
                                       me2.delNext();
                                   }(selection);         
                               }
                           }
                       }
                     }
         },
      link:{
          browserField:'id',
          datagridFilter:'xtidGroup::=='
      }
      
  });
};
//###############################################



//----------------------------------------------- CWA UI AUTH RIGHT BROWSER
cwa.ui.Auth.RightBrowser = function(){
  cwa.libs.ui.browser.call(this);
  var me = this;    
  me.setConfig({
      title:'Liste des droits',
      editor:cwa.ui.Auth.RightEditor,
      datagrid:{
          get:'/app/Auth/RightGet.json',
          del:'/app/Auth/RightDel.json',
          columns:{
                   id:{
                       label:"Id",
                       fieldName:"id",
                       fieldType:"id",
                       width:50,
                       filters:[
                                {type:"text",filter:"id::LIKE*"}
                                ]
                   },
                   user:{
                       label:"Nom",
                       fieldName:"name",
                       fieldType:"text",
                       width:150,
                       filters:[
                                {type:"text",filter:"name::LIKE*"}
                                ]
                   },
                   login:{
                       label:"Description",
                       fieldName:"description",
                       fieldType:"text",
                       width:450,
                       filters:[
                                  {type:"text",filter:"description::LIKE*"}
                                  ]
                   }
          },
           orderBy:[
                    {label:"Id",orderBy:"id"},
                    {label:"Nom",orderBy:"name"}
                    ],
           defaultOrderBy:"name::ASC"
         }
  });
};
//###############################################




//----------------------------------------------- CWA UI AUTH RIGHT EDITOR
cwa.ui.Auth.RightEditor = function(){
    cwa.libs.ui.editor.call(this);
    var me = this;    
    me.setConfig({
        title:"Droit",
        form:{
            get:'/app/Auth/RightGetOne.json',
            set:'/app/Auth/RightSet.json',
            fields:{
                    name: {name:"name",type:"text",label:"Nom :"},
                    description: {name:"description",type:"mediumText",label:"Description :"}
            }
        }
    });
};
//###############################################



//----------------------------------------------- CWA UI AUTH GROUP BROWSER
cwa.ui.Auth.GroupBrowser = function(){
cwa.libs.ui.browser.call(this);
var me = this;    
me.setConfig({
    title:'Liste des groupes',
    editor:cwa.ui.Auth.GroupEditor,
    datagrid:{
        get:'/app/Auth/GroupGet.json',
        del:'/app/Auth/GroupDel.json',
        columns:{
                 id:{
                     label:"Id",
                     fieldName:"id",
                     fieldType:"id",
                     width:50,
                     filters:[
                              {type:"text",filter:"id::LIKE*"}
                              ]
                 },
                 user:{
                     label:"Nom",
                     fieldName:"name",
                     fieldType:"text",
                     width:150,
                     filters:[
                              {type:"text",filter:"name::LIKE*"}
                              ]
                 },
                 login:{
                     label:"Description",
                     fieldName:"description",
                     fieldType:"text",
                     width:450,
                     filters:[
                                {type:"text",filter:"description::LIKE*"}
                                ]
                 }
        },
         orderBy:[
                  {label:"Id",orderBy:"id"},
                  {label:"Nom",orderBy:"name"}
                  ],
         defaultOrderBy:"name::ASC"
       }
    });
};
//###############################################




//----------------------------------------------- CWA UI AUTH GROUP EDITOR
cwa.ui.Auth.GroupEditor = function(){
  cwa.libs.ui.editor.call(this);
  var me = this;    
  me.setConfig({
      title:"Groupe",
      form:{
          get:'/app/Auth/GroupGetOne.json',
          set:'/app/Auth/GroupSet.json',
          fields:{
                  name: {name:"name",type:"text",label:"Nom :"},
                  description: {name:"description",type:"mediumText",label:"Description :"}
          }
      }
  });
};
//###############################################



//----------------------------------------------- CWA UI AUTH USER BROWSER
cwa.ui.Auth.UserBrowser = function(){
    cwa.libs.ui.browser.call(this);
    var me = this;    
    me.setConfig({
        title:'Liste des utilisateurs',
        editor:cwa.ui.Auth.UserEditor,
        datagrid:{
            get:'/app/Auth/UserGet.json',
            del:'/app/Auth/UserDel.json',
            columns:{
                     id:{
                         label:"Id",
                         fieldName:"id",
                         fieldType:"id",
                         width:50,
                         filters:[
                                  {type:"text",filter:"id::LIKE*"}
                                  ]
                     },
                     user:{
                         label:"Utilisateur",
                         fieldName:"getFullName()",
                         fieldType:"text",
                         width:200,
                         filters:[
                                  {type:"text",filter:"firstname||lastname::LIKE*"}
                                  ]
                     },
                     login:{
                         label:"Login",
                         fieldName:"login",
                         fieldType:"text",
                         width:200,
                         filters:[
                                    {type:"text",filter:"login::LIKE*"}
                                    ]
                     }
            },
             orderBy:[
                      {label:"Id",orderBy:"id"},
                      {label:"Utilisateur",orderBy:"lastname"}
                      ],
             defaultOrderBy:"lastname::ASC"
           }
    });
};
//###############################################



//----------------------------------------------- CWA UI AUTH USER EDITOR
cwa.ui.Auth.UserEditor = function(){
    cwa.libs.ui.editor.call(this);
    var me = this;    
    me.setConfig({
        title:"Utilisateur",
        form:{
            get:'/app/Auth/UserGetOne.json',
            set:'/app/Auth/UserSet.json',
            fields:{
                    lastname: {name:"lastname",type:"text",label:"Nom :"},
                    firstname: {name:"firstname",type:"text",label:"Prénom :"},
                    emailAddress: {name:"emailAddress",type:"text",label:"Adresse e-mail :"},
                    login: {name:"login",type:"text",label:"Identifiant :"},
                    password: {name:"password",type:"text",label:"Mot de passe :"}
            }
        }
    });
};
//###############################################



//----------------------------------------------- CWA UI WEB SECTION BROWSER
cwa.ui.Web.SectionBrowser = function(){
  cwa.libs.ui.browser.call(this);
  var me = this;    
  me.setConfig({
      title:'Liste des pages',
      editor:cwa.ui.Web.SectionEditor,
      datagrid:{
          get:'/app/Web/SectionGet.json',
          del:'/app/Web/SectionDel.json',
          columns:{
                   id:{
                       label:"Id",
                       fieldName:"id",
                       fieldType:"id",
                       width:50,
                       filters:[
                                {type:"text",filter:"id::LIKE*"}
                                ]
                   },
                   title:{
                       label:"Titre",
                       fieldName:"title",
                       fieldType:"text",
                       width:200,
                       filters:[
                                {type:"text",filter:"title::LIKE*"}
                                ]
                   },
                   path:{
                       label:"Emplacement",
                       fieldName:"getPath()",
                       fieldType:"text",
                       width:400,
                       filters:[]
                   }
          },
           orderBy:[
                    {label:"Id",orderBy:"id"},
                    {label:"title",orderBy:"title"}
                    ],
           defaultOrderBy:"id::DESC"
         }
  });
};
//###############################################



//----------------------------------------------- CWA UI WEB SECTION EDITOR
cwa.ui.Web.SectionEditor = function(){
  cwa.libs.ui.editor.call(this);
  var me = this;    
  me.setConfig({
      title:"Page",
      form:{
          get:'/app/Web/SectionGetOne.json',
          set:'/app/Web/SectionSet.json',
          fields:{
                  title: {name:"title",type:"text",label:"Titre :",help:"Le titre de la page est utilisé dans l'url. Dans certains sites, il est aussi affiché comme titre du contenu de la page."},
                  description: {name:"description",type:"mediumText",label:"Description :",help:"La description est utilisée dans les listes de pages, ou les liens vers la page. Elle doit contenir une description succinte du contenu de la page."},
                  privateDescription: {name:"privateDescription",type:"mediumText",label:"Description privée :",help:"La description privée est destinée aux rédacteurs et webmasters du site. C'est une sorte de 'mémo' à propos de la page."},
                  wysiwygEditable: {name:"wysiwygEditable",type:"bool",label:"Edition graphique :",onLabel:'autorisée',offLabel:'interdite'},
                  color: {name:"color",type:"color",label:"Couleur repère :"},
                  isPrivate: {name:"isPrivate",type:"bool",label:"Accès :",onLabel:'controlé',offLabel:'libre'},
                  authRight: {name:"xgidAuthRight",type:"foreignSelector",label:"Droit d'accès :",nullable:true,editor:cwa.ui.Auth.RightBrowser},
                  owner: {name:"xgidOwner",type:"foreignSelector",label:"Propriétaire :",nullable:true,editor:cwa.ui.Auth.UserBrowser},
                  isLockedByOwner: {name:"isLockedByOwner",type:"bool",label:"Les autres peuvent\nmodifier la page :",onLabel:'non',offLabel:'oui'},
                  isRedirection: {name:"isRedirection",type:"bool",label:"Page virtuelle :",onLabel:'oui',offLabel:'non'},
                  redirectionUrl: {name:"redirectionUrl",type:"text",label:"URL :"},
                  redirectionTarget: {name:"redirectionTarget",type:"select",label:"Cible :"},
                  parent: {name:"xtidParent",type:"foreignSelector",label:"Page mère :",nullable:true,editor:cwa.ui.Web.SectionBrowser},
                  displayStatus: {name:"displayStatus",type:"selector",label:"Statut :",items:[{label:'Publié',value:'public'},{label:'Masqué',value:'private'}]}
                  
          },
          groups:{
              main:{label:"Principal",minWidth:91,fields:['title','description','privateDescription','parent','displayStatus']},
              html:{label:"Edition HTML",minWidth:112,fields:['wysiwygEditable','color']},
              virtual:{label:"Page virtuelle",minWidth:117,fields:['isRedirection','redirectionUrl','redirectionTarget']},
              owner:{label:"Droits de modification",minWidth:184,startNewLine:true,fields:['isLockedByOwner','owner']},
              access:{label:"Accès controlé",minWidth:156,fields:['isPrivate','authRight']}
          },
          defaultGroup:'main'
      }
  });
};
//###############################################



//----------------------------------------------- CPI UI HTMLEDITOR BESPIN INSERT





cwa.ui.HtmlEditor.BespinInsert = function(){

    var me = this;

    me.rootElement = $('<div>').hide().appendTo('body');

    me.cbOnClose = function(){};

    me.tabs = {
        MediaFile:{
            loaded:false,
            path:'MediaFile/Browser.html'
        },
        WebSection:{
            loaded:false,
            path:'Web/SectionBrowser.html'
        },
        WebLink:{
            loaded:false,
            path:'Web/LinkBrowser.html'
        }
    };

    me.load = function(cbOnClose){

        me.cbOnClose = cbOnClose;
        me.rootElement.append('\
            <div class="tabsScreen" style="width:100%">\
                <div class="toolbar">\
                    <button class="MediaFile">Fichiers</button>\
                    <button class="WebSection">Pages</button>\
                    <button class="WebLink">Liens externes</button>\
                </div>\
                <div class="tabs">\
                    <div class="MediaFile" style="display:none;width:100%;"><div class="waiting"></div></div>\
                    <div class="WebSection" style="display:none;width:100%"><div class="waiting"></div></div>\
                    <div class="WebLink" style="display:none;width:100%"><div class="waiting"></div></div>\
                </div>\
            </div>\
            ');
        $(me.rootElement).dialog({
            title:'Insérer',
            width:'auto',
            position:[
                    30,30
            ],
            modal:true,
            minHeight:20,
            resizable:false,
            draggable:true,
            dialogClass:'cpiWebApps ',
            autoOpen:false
        });

        $('button.MediaFile',me.rootElement).bind('click',function(event){
            me.openTab('MediaFile');
        });

        $('button.WebSection',me.rootElement).bind('click',function(event){
            me.openTab('WebSection');
        });

        $('button.WebLink',me.rootElement).bind('click',function(event){
            me.openTab('WebLink');
        });

        me.openTab('MediaFile');

        me.rootElement.dialog('open');
    };

    me.openTab = function(tabName){
        $('div.tabs > div',me.rootElement).hide();
        $('div.' + tabName,me.rootElement).show();
        me.loadTab(tabName);
    };

    me.loadTab = function(tabName){
        if(!me.tabs[tabName].loaded){
            cpiAjax.load(me.tabs[tabName].path,{
                selectorMode:true,
                dialogMode:'direct'
            },function(uid){
                $('#' + uid).bind('selectOneAndClose',function(event){
                    me.insert(tabName,$('#' + uid + '_selected_Fk').val());
                });
                $('div.' + tabName,me.rootElement).html($('#' + uid));
                $('#' + uid).show();
                me.tabs[tabName].loaded = true;
            });
        }

    };

    me.insert = function(tabName,Pk){
        me.rootElement.dialog('close');
        if(tabName == "MediaFile"){
            var dialog = new cwa.ui.HtmlEditor.MediaParams();
            dialog.load(function(type){
                cpiAjax.load('HtmlEditor/GetMediaProperties.json','Pk=' + Pk,function(data){
                    var result;
                    switch(type){
                        case 'MiniThumb':
                            result = '<img src="/app/MediaFile/MiniThumb.bin?Pk=' + Pk + '" width="'
                                    + data.miniThumbnailWidth + '" height="' + data.miniThumbnailHeight + '" />';
                            break;
                        case 'Thumb':
                            result = '<img src="/app/MediaFile/Thumb.bin?Pk=' + Pk + '" width="'
                            + data.thumbnailWidth + '" height="' + data.thumbnailHeight + '" />';
                            break;
                        case 'File':
                            result = '<img src="/app/MediaFile/File.bin?Pk=' + Pk + '" width="'
                            + data.fileWidth + '" height="' + data.fileHeight + '" />';
                            break;
                        case 'MiniThumbWithDownloadFile':
                            result = '<a href="/app/MediaFile/File.bin?Pk=' + Pk + '&download=true">'
                            + '<img src="/app/MediaFile/MiniThumb.bin?Pk=' + Pk + '" width="'
                            + data.miniThumbnailWidth + '" height="' + data.miniThumbnailHeight + '" />'
                            + '</a>';
                            break;
                        case 'ThumbWithDownloadFile':
                            result = '<a href="/app/MediaFile/File.bin?Pk=' + Pk + '&download=true">'
                            + '<img src="/app/MediaFile/Thumb.bin?Pk=' + Pk + '" width="'
                            + data.thumbnailWidth + '" height="' + data.thumbnailHeight + '" />'
                            + '</a>';
                            break;
                        default: // 'DownloadFile':
                            result = '<a href="/app/MediaFile/File.bin?Pk=' + Pk + '&download=true"></a>';
                            break;
                    }
                    me.cbOnClose(result);
                });
            });
        }
        else if(tabName=='WebSection'){
            cpiAjax.load('HtmlEditor/GetSectionProperties.json','Pk='+Pk,function(data){
                var result;
                result = '<a href="'+data.url+'" title="'+data.title+'">'+data.title+'</a>';
                me.cbOnClose(result);
            });
        }
        else if(tabName=='WebLink'){
            cpiAjax.load('HtmlEditor/GetLinkProperties.json','Pk='+Pk,function(data){
                var result;
                result = '<a href="'+data.url+'" title="'+data.title+'" target="'+data.target+'">'+data.title+'</a>';
                me.cbOnClose(result);
            });
        }
    };

};
//###############################################




//----------------------------------------------- CWA UI HTMLEDITOR MEDIAPARAMS
cwa.ui.HtmlEditor.MediaParams = function(){
    var me = this;
    
    me.rootElement = $('<div>');
    
    me.cbOnClose = function(){};
    
    me.load = function(cbOnClose){
        
        me.cbOnClose = cbOnClose;
        
        me.rootElement.appendTo('body');
        me.rootElement.append('\
            <div>\
                <p>Images:</p>\
                <label><input type="radio" name="Type" value="MiniThumb">Icone (petite taille).</label>\
                <label><input type="radio" name="Type" value="Thumb">Vignette (taille moyenne).</label>\
                <label><input type="radio" name="Type" value="File" checked="checked">Taille d\'origine.</label>\
                <p>Images avec lien:</p>\
                <label><input type="radio" name="Type" value="MiniThumbWithDownloadFile">Icone avec lien vers le fichier.</label>\
                <label><input type="radio" name="Type" value="ThumbWithDownloadFile">Vignette avec lien vers le fichier.</label>\
                <p>Liens:</p>\
                <label><input type="radio" name="Type" value="DownloadFile">Lien vers le fichier.</label>\
            </div>\
        ');
    
        me.rootElement.dialog({
            title:'Paramètres pour le fichier',
            width:'auto',
            modal:true,
            minHeight:20,
            resizable:false,
            draggable:true,
            dialogClass:'cpiWebApps '
        });
        
        me.rootElement.bind('dialogbeforeclose',function(){
            me.cbOnClose($('input:checked',me.rootElement).val());
        });

        $('input',me.rootElement).bind('change',function(){
            $(me.rootElement).dialog('close');
        });
        
    };
};



























