/*
 * Copyright (c) Perforce Software, Inc. 1997-2016
 * Licensed Materials - All Rights Reserved.
 */
function TextField(psheet, parent, name, text) {
   this.psheet = psheet;
   this.name = name;
   this.text = text;
   this.initText = text;
   this.labelId = "textfield_"+name;
   this.rootId = this.labelId+"root";
   this.inputId = this.labelId+"input";
   this.createTextField(parent);
   this.changeListeners = new IlvListenerList();
}

TextField.regularStyleClass = "textfield";
TextField.highlightedStyleClass = "textfield-highlighted";
TextField.changedStyleClass = "textfield-changed";
TextField.errorStyleClass = "textfield-error";

TextField.prototype.getName = function() {
  return this.name;
};

TextField.prototype.getText = function() {
  return this.getLabelText();
};

TextField.prototype.createTextField = function (parent) {
  parentNode = document.getElementById(parent);
  this.parent = parentNode;
  parentNode.innerHTML = "<span id='"+this.rootId+"'><span id='"+this.labelId+"' class='"+TextField.regularStyleClass+"'>"+this.text+"</span></span>";
  this.label = document.getElementById(this.labelId);   
  this.root = document.getElementById(this.rootId);   
  var textfield = this;
  
  this.label.onmouseover = function () { 
    if (textfield) {
      if (textfield.hasError())
        this.className=TextField.errorStyleClass;
      else if (textfield.hasChanged())
        this.className=TextField.changedStyleClass;      
      else this.className=TextField.highlightedStyleClass;
    }
  };
  
  this.label.onmouseout = function () { 
    if (textfield) {
      if (textfield.hasError())
        this.className=TextField.errorStyleClass;
      else if (textfield.hasChanged())
        this.className=TextField.changedStyleClass;      
      else this.className=TextField.regularStyleClass;
    }
  };
  
  parentNode.onclick = function () { 
    textfield.psheet.setSelected(this);
    textfield.showInput(); 
  };

};

TextField.prototype.addChangeListener = function(listener) {
  this.changeListeners.addListener(listener);
};


TextField.prototype.removeChangerListener = function(listener) {
  this.changeListeners.removeListener(listener);
};

TextField.prototype.changed = function(oldText, newText) {
  this.setError(false);
  this.textChanged = true;
  this.setLabelText(newText);
  this.text = newText;
  this.label.className=TextField.changedStyleClass;
  this.changeListeners.notify(this, oldText, newText);
};

TextField.prototype.revert = function() {
  this.text = this.initText;
  this.textError = false;
  this.setLabelText(this.text);
  this.textChanged = false;
  this.label.className=TextField.regularStyleClass;
};

TextField.prototype.showInput = function () {

  this.parentClickSave = this.parent.onclick;
  this.parent.onclick = null;
  this.parent.className = PropertySheet.ValueCellEditing; 

  var label = this.label;
  var root = this.root;
  var input;

  var text = this.getLabelText();

  input = document.createElement('input');
  input.id = this.inputId;
  input.style.display = "none";
  input.type = "text";
  input.name = "textbox";
  input.value = text;
  input.autoComplete = "off";  

  root.appendChild(input);
  var textfield = this;
  input.onblur = function() {textfield.onBlur();};
  input.onkeypress = function(e) {textfield.onKeyPress(e);};
  input.className=TextField.regularStyleClass;
  
  label.style.display="none";
  input.size = input.value.length == 0 ? 8 : input.value.length;
  this.oldText = input.value;
  input.style.display="inline";
  input.select();
  try {
    input.focus();
  } catch (e) {
  }
  
};

TextField.prototype.hasChanged = function() {
  return this.textChanged;
};

TextField.prototype.hasError = function() {
  return this.textError;
};

TextField.prototype.setError = function(value) {
  if (value) {
    this.textError = true;
    this.label.className = TextField.errorStyleClass;
  } else {
    this.textError = false;
    this.label.className = TextField.regularStyleClass;
  }
}

TextField.prototype.onBlur = function() {
  var input = document.getElementById(this.inputId);

  this.hideInput();
  
  var text =  input.value;
       
  if (this.oldText != text) {
    this.changed(this.oldText, text);
  }

};

TextField.prototype.setLabelText = function(text) {
  var label = this.label;
  if (label) {
    if(label.innerText) {
      label.innerText = text;
    } else {
      var txt = label.childNodes[0];
      if (!txt) {
        txt = document.createTextNode(text);          
        label.appendChild(txt);
      } else {
        txt.data = text;
      }
    }
  }
};

TextField.prototype.getLabelText = function() {
  var label = this.label;
  if (label) {
    if (label.innerText) {
      return label.innerText;
    } else {      
      var txt = label.childNodes[0];
      if (!txt) {
        return "";
      } else {
        return txt.data;
      }
    }
  }
  return null;
};


TextField.prototype.hideInput = function() {
  var label = this.label;
  var input = document.getElementById(this.inputId);
  var root = this.root;
    this.parent.className = PropertySheet.ValueCell;
  if(root.removeChild)
    root.removeChild(input);
  else
    input.style.display="none";
  label.style.display="inline";
  this.parent.onclick = this.parentClickSave;

};

TextField.prototype.onKeyPress = function(e) {
  var input = document.getElementById(this.inputId);
  var keyCode;
  if (e)
      keyCode = e.keyCode;
  else
      keyCode = event.keyCode;
  if (keyCode==13) {
    input.onblur();
    return false;
  } else if (keyCode==27) {
    input.value = this.oldText;
    return false;
  }
  return true;
};

function PropertySheet (ref, spanId) {
  this.ref = ref;
  this.spanId = spanId;
  this.tableId = this.spanId + "_table";
  this.commitId = this.tableId + "_commit";
  this.deletedProperties = [];
  this.addedProperties = [];
}

PropertySheet.Table = "psheet";
PropertySheet.TitleCell = "psheet-cell-title";
PropertySheet.NameCell = "psheet-cell";
PropertySheet.ValueCell = "psheet-cell-value";
PropertySheet.CellSelected = "psheet-cell-selected";
PropertySheet.Text = "psheet-text";

PropertySheet.prototype.setAbsoluteId = function (id) {
  this.absoluteId = id;
};

PropertySheet.prototype.setView = function (view) {
  this.view = view;
  this.updateSelectionManager();
};

PropertySheet.prototype.getView = function () {
  return this.view;
};

PropertySheet.prototype.setComponent = function (component) {
  this.component = component;
  this.updateSelectionManager();
}

PropertySheet.prototype.getComponent = function () {
  return this.component;
};

PropertySheet.prototype.updateSelectionManager = function () {
  if (this.view != null && this.component != null) {
    if (this.component == "table") {
      this.setSelectionManager(this.view.getTableView().getSelectionManager());
    } else if (this.component == "sheet") {
      this.setSelectionManager(this.view.getSheetView().getSelectionManager());
    }
  }
}

PropertySheet.prototype.setSelectionManager = function (selectionManager) {
  this.selectionManager = selectionManager;  
  var psheet = this;
  selectionManager.addSelectionChangedListener(
    function(selection) {
      psheet.displayPropertySheet(selection);
    }
  );
};

PropertySheet.prototype.addProperty = function () {
  var table = document.getElementById(this.tableId);
  if (table) {
    var name = prompt(jviews.messages.demo.ganttFacelet.propertySheet.enterNameOfNewProperty, "");
    if (name)  {             
      var found = false;
      var length = this.deletedProperties.length;
      for(var i=0; i<length && !found; i++) {
        var prop = this.deletedProperties[i];
        if (prop == name) {
          found = true;
        }
      }
      if (found) {
        //remove the property from the list
        this.deletedProperties.splice(i-1, 1);

        //show the deleted property
        var selectedNameCell = document.getElementById("name_"+name);
        var tr = selectedNameCell.parentNode;
        if (IlvBrowserInfo.instance.ie5up) {
          tr.style.display = "block";
        } else {
          tr.style.display = "table-row";
        }      

      } else {
          
        if (this.textfields[name]) {
          alert(jviews.messages.demo.ganttFacelet.propertySheet.propertyExistsError);
          return;
        }

               
        this.addedProperties[this.addedProperties.length] = name;
        var names = [];
        for(var i in this.textfields){
          names[names.length] = this.textfields[i].getName();
        }
        names = names.concat([name]).sort();
        var length = names.length;
        var found = false;
        for(var i=0; i<length && !found; i++) {
            if (names[i] == name) {
              found = true;
            }
        }
        
        var tr =table.insertRow(i);
        
        var td = document.createElement("td");
        td.className = PropertySheet.NameCell;
        td.id= "name_"+name;
        td.appendChild(document.createTextNode(name));
        td.onclick = function () {
          psheet.setSelected(this);
        };
        tr.appendChild(td);
        td = document.createElement("td");
        td.id = "value_"+name;
        td.className = PropertySheet.ValueCell;
        tr.appendChild(td);        
        this.textfields[name] = new TextField(this, td.id, name, "");
        var psheet = this;
        this.textfields[name].addChangeListener(function () {psheet.showCommitButtons();});
        this.textfields[name].setError(false);
        this.textfields[name].textChanged = true;
        this.showCommitButtons();
      }
    }
  }
}; 

PropertySheet.prototype.removeProperty = function () {
  if (this.selectedProperty) {
    this.deletedProperties[this.deletedProperties.length] = this.selectedProperty;
    var selectedNameCell = document.getElementById("name_"+this.selectedProperty);
    var tr = selectedNameCell.parentNode;
    tr.style.display = "none";
    this.showCommitButtons();
  }  
};

PropertySheet.prototype.setSelected = function (cell) {

  var oldSelectedNameCell = document.getElementById("name_"+this.selectedProperty);
  if (oldSelectedNameCell) {
    oldSelectedNameCell.className = PropertySheet.NameCell;   
  }
  if (cell) {        
    var property = cell.id.substring(cell.id.indexOf("_")+1, cell.id.length);
    this.selectedProperty = property;
    if (property) {
  
      this.selectedTextField = this.textfields[property];
      var selectedNameCell = document.getElementById("name_"+this.selectedProperty);
      selectedNameCell.className = PropertySheet.CellSelected;

    }
  } else {
    this.selectedProperty = null;
    this.selectedTextField = null;
  }
};


PropertySheet.prototype.displayPropertySheet = function (selection) {
 
  this.selection = selection;
  this.selectedProperty = null;
  this.deletedProperties = [];
  this.addedProperties = [];
 
  var p = "";
  
  try {

  if (selection.length == 1) {    
    var oldId = this.selectionId;
    this.selectionId = selection[0].getObjectID();
    p = "<table border='0' cellspacing='0' cellpadding='0'><tr><td>";
    p += "<table class='"+PropertySheet.Table+"' id='"+this.tableId+"'>";
    p += "<tr>";
    p += "<td colspan='2' class='"+PropertySheet.TitleCell+"'>";
    p+="<table width='100%'  border='0' cellspacing='0' cellpadding='0'><tr><td class='"+PropertySheet.Text+"' style='font-weight:bold'>"+jviews.messages.demo.ganttFacelet.propertySheet.dataProperties+"</td>";
    p += "<td align='right' class='"+PropertySheet.Text+"' id='addProperty' style='cursor:pointer'><span>+</span></td>";
    p += "<td align='right' class='"+PropertySheet.Text+"' id='removeProperty' style='cursor:pointer'><span>x</span></td></tr></table></td>";
    p += "</tr>";
    var names = selection[0].getObjectPropertyNames().sort();
    var length = names.length;
    var type = selection[0].getObjectType();
    for(var i=0; i<length; i++){
      var name = names[i];
      p += "<tr>";
      p += "<td class='"+PropertySheet.NameCell+"' id='name_"+name+"'>" + name + "</td>";
      p += "<td class='"+PropertySheet.ValueCell+"' id='value_"+name+"'></td>";
      p += "</tr>";
    }                   
    p +="</table>";
    p += "</td></tr><tr><td>";
    p +="<span id='"+this.commitId+"' style='display:none;'></span>";
    p += "</td></tr></table>";

    var span = document.getElementById(this.spanId);
    span.innerHTML = p;
    
    var addButton = document.getElementById("addProperty");
    var removeButton = document.getElementById("removeProperty");

    var psheet = this;
    addButton.onclick = function() {
      psheet.addProperty();
    };
    
    removeButton.onclick = function () {
      psheet.removeProperty();
    };
    //create textfields  
    this.commitButtonsShown = false;

    var oldtf = this.textfields;
    this.textfields = [];    
    for(var i=0; i<length; i++){
      var name = names[i];
      var value = this.format(type, name, selection[0].getObjectProperty(name));
      var nameCell = document.getElementById("name_"+name);
      nameCell.onclick = function () {
          psheet.setSelected(this);
      };
      var tf = new TextField(this, "value_"+name, name, value);
      var commit;
      // previously errored on same object? Let's give a chance to retry
      // so keep old value
      if (oldId == this.selectionId && oldtf && oldtf[name] && oldtf[name].hasError()) {              
        tf.changed(value, oldtf[name].text);
        tf.setError(true);
        commit = true;
      } 
      tf.addChangeListener(function () {psheet.showCommitButtons();});
      this.textfields[name] = tf;      
    }    
    this.createCommitButtons();
    // we have some pending errors let's show commit button
    if (commit)
      this.showCommitButtons();
  } else {
    var span = document.getElementById(this.spanId);
    span.innerHTML = "";
  }
  
  } catch (e) {
    alert(jviews.messages.demo.ganttFacelet.propertySheet.errorOccuredDuringRefresh+ e.toString());
  }

};

PropertySheet.prototype.showCommitButtons = function () {
   var span = document.getElementById(this.commitId);
   span.style.display = "block";      
   var okId = this.tableId + "_ok";
   var ok = document.getElementById(okId);
   ok.focus();
};

PropertySheet.prototype.hideCommitButtons = function () {
    var span = document.getElementById(this.commitId);
    span.style.display = "none";
};

PropertySheet.prototype.createCommitButtons = function () {
  var span = document.getElementById(this.commitId);
  var okId = this.tableId + "_ok";
  var koId = this.tableId + "_ko";
  var p = "<input type='button' id='"+okId+"' autocomplete='off' class='psheet-text' value='"+jviews.messages.demo.ganttFacelet.propertySheet.commitBtn+"' />";
  p += "<input type='button' id='"+koId+"' autocomplete='off' class='psheet-text' value='"+jviews.messages.demo.ganttFacelet.propertySheet.cancelBtn+"' />";
  span.innerHTML = p;
  var ok = document.getElementById(okId);
  var psheet = this;
  ok.onmouseup = function () {
    psheet.commitChanges();
  };  
  ok.onkeypress = function(e) {
     var keyCode;
     if (e) {
       keyCode = e.keyCode;
    } else {
      keyCode = event.keyCode;
    }
    if (keyCode==13) {
      this.onmouseup();
      return false;
    }
    return true;
  };
  
  var ko = document.getElementById(koId);
  ko.onmouseup = function () {
    psheet.cancelChanges();
  };    
  ko.onkeypress = function(e) {
    var keyCode;
     if (e) {
       keyCode = e.keyCode;
    } else {
      keyCode = event.keyCode;
    }
    if (keyCode==13) {
      this.onmouseup();
      return false;
    }
    return true;
  };
};

PropertySheet.prototype.cancelChanges = function () {
    for(var i in this.textfields){
      var tf = this.textfields[i];
      if (tf.hasChanged()) {
        tf.revert();
      }
      if (tf.hasError()) {
        tf.setError(false);
      }
    }
    var length = this.deletedProperties.length;
    for (var i=0; i<length; i++) {
      var p = this.deletedProperties[i];
      var selectedNameCell = document.getElementById("name_"+p);
      var tr = selectedNameCell.parentNode;
      if (IlvBrowserInfo.instance.ie5up) {
        tr.style.display = "block";
      } else {
        tr.style.display = "table-row";
      }
    }
    length = this.addedProperties.length;
    for (i=0; i<length; i++) {
      p = this.addedProperties[i];
      selectedNameCell = document.getElementById("name_"+p);
      if(selectedNameCell) {
        tr = selectedNameCell.parentNode;
        tr.parentNode.removeChild(tr);
      }
    }
    this.addedProperties = [];
    this.deletedProperties = [];
    this.hideCommitButtons();
};

PropertySheet.prototype.commitChanges = function () {
    var nbChanges = 0;
    var type = this.selection[0].getObjectType();
    for(var i in this.textfields){
      var tf = this.textfields[i];
      if (tf.hasChanged()) {
        nbChanges++;
        var value = this.parse(type, tf.getName(), tf.getText())
        this.selection[0].setObjectProperty(tf.getName(), value);
      }
    }
    var length = this.deletedProperties.length;
    for (var i=0; i<length; i++) {
      var p = this.deletedProperties[i];
      this.selection[0].setObjectProperty(p, null);
      nbChanges++;
    }
    if (nbChanges > 0) {
      this.selectionManager.psheet = this;
      this.selectionManager.commitSelectionProperties(true, error_handler);
    }
};

function error_handler(errors) {
  var psheet = this.psheet;
  // in any case erase flags 
  for (var i in psheet.textfields) {
    var tf = psheet.textfields[i];    
    if (tf.hasChanged()) {
      tf.textChanged = false; // no more change
      tf.label.className=TextField.regularStyleClass;
      tf.setError(false);      
    }    
  }
  // are there any errors?
  if (errors && errors.length > 0) {
    // simple case we just got one selected object => one possible error object
    // look for all failing properties 
    var propertyNames = errors[0].getObjectPropertyNames();
    for (var i = 0; i < propertyNames.length; i++) {
      var tf = psheet.textfields[propertyNames[i]];
      tf.textChanged = true;
      tf.setError(true);
      // was that a delete property? Let's show it again
      if (psheet.deletedProperties) {
        var length = psheet.deletedProperties.length;
        for (var j = 0; j < length; j++) {
          var p = psheet.deletedProperties[j];
          if (p == propertyNames[i]) {
            var selectedNameCell = document.getElementById("name_"+p);
            var tr = selectedNameCell.parentNode;
            if (IlvBrowserInfo.instance.ie5up) {
              tr.style.display = "block";
            } else {
              tr.style.display = "table-row";
            }
            psheet.deletedProperties.splice(j, 1);
            break;         
          }        
        }
      }
    }
  } 
}

/**
 * Formats a marshalled property value to a representation suitable for display.
 * This is the inverse operation of <code>format</code>.
 * The following should hold true:
 * <ul>
 *   <li><code>value == parse(type, name, format(type, name, value))</code></li>
 *   <li><code>x == format(type, name, parse(type, name, x))</code></li>
 * </ul>
 * Note that in some cases, such as with numbers and dates, accuracy is 
 * lost when formatting.
 * 
 * @param type   Type of the object containing the property.
 * @param name Name of the property.
 * @param value Value of the property.
 */
PropertySheet.prototype.format = function(type, name, value) {
  if (type=="activity" && (name == "endTime" || name == "startTime")) {
    return this.formatDate(value);
  }
  return value;  
}

/**
 * Parses a display value and returns the corresponding marshalled value.
 * This is the inverse operation of <code>format</code>.
 * The following should hold true:
 * <ul>
 *   <li><code>value == parse(type, name, format(type, name, value))</code></li>
 *   <li><code>x == format(type, name, parse(type, name, x))</code></li>
 * </ul>
 * Note that in some cases, such as with numbers and dates, accuracy is 
 * lost when formatting.
 * 
 * @param type Type of the object.
 * @param name Name of the property.
 * @param value Value of the property.
 */
PropertySheet.prototype.parse = function(type, name, value) {
  if (type=="activity" && (name == "endTime" || name == "startTime")) {
    return this.parseDate(value);
  }
  return value;  
}
/**
 * Formats a marshalled representation of a date into a formatted string 
 * suitable for display.
 * 
 * @param value  A <code>string</code> value representing the number of 
 *               milliseconds since the Unix epoch.
 * @return  A formatted <code>string</code> representation of the date, or an
 *          empty string when <i>date</i> is <code>null</code>.
 */
PropertySheet.prototype.formatDate = function(value) {
        return  value == null ? null :value;
}
/**
 * Parses an string representation of a date and returns the corresponding 
 * marshalled representation.
 * 
 * @param value  A <code>string</code> value representing a formatted date.
 * @return  A <code>string</code> representing the number of milliseconds since 
 *          the Unix epoch or <code>null</code> when <i>value</i> is an empty 
 *          string.
 */
PropertySheet.prototype.parseDate = function(value) {
          return value == "" ? null :value;
}