File:  [LON-CAPA] / modules / damieng / graphical_editor / loncapa_daxe / web / nodes / organic_response.dart
Revision 1.9: download - view: text, annotated - select for diffs
Mon Mar 27 17:35:05 2017 UTC (7 years, 2 months ago) by damieng
Branches: MAIN
CVS tags: HEAD
avoid adding a missing textline to a response when opening a document missing it (using advanced response in that case); added more messages related to hintgroup, textline and textfield to explain why the switch to simple UI fails

/*
  This file is part of LONCAPA-Daxe.

  LONCAPA-Daxe is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  LONCAPA-Daxe is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Daxe.  If not, see <http://www.gnu.org/licenses/>.
*/

part of loncapa_daxe;

/**
 * Display for organicresponse, with a possible simple UI.
 * Jaxe display type: 'organicresponse'.
 */
class OrganicResponse extends LCDBlock {
  
  static List<String> simpleUIAttributes = ['options','molecule','answer','jmeanswer','width','id'];
  
  OrganicResponse.fromRef(x.Element elementRef) : super.fromRef(elementRef) {
    displaySimpleButton = true;
  }
  
  OrganicResponse.fromNode(x.Node node, DaxeNode parent) : super.fromNode(node, parent) {
    displaySimpleButton = true;
    if (simpleUIPossibleNoThrow()) {
      bool node_simpleUI = node.getUserData('simpleUI');
      if (node_simpleUI == null || node_simpleUI)
        setupSimpleUI();
    }
  }
  
  @override
  bool simpleUIPossible() {
    for (DaxeAttr att in attributes) {
      if (!simpleUIAttributes.contains(att.name)) {
        throw new SimpleUIException('organicresponse: ' + LCDStrings.get('attribute_problem') + ' ' + att.name);
      }
    }
    int nbTextline = 0;
    int nbHintgroup = 0;
    for (DaxeNode dn=firstChild; dn!= null; dn=dn.nextSibling) {
      if (dn is Textline) {
        if (!dn.simpleUIPossible())
          return false;
        nbTextline++;
      } else if (dn is Hintgroup) {
        //if (!dn.simpleUIPossible())
        //  return false;
        // accept hintgroup even if simple UI is not possible for it
        nbHintgroup++;
      } else if (dn.nodeType != DaxeNode.TEXT_NODE) {
        String title = doc.cfg.elementTitle(dn.ref);
        throw new SimpleUIException(LCDStrings.get('element_prevents') + ' ' + dn.nodeName +
            (title != null ? " ($title)" : ''));
      } else if (dn.nodeValue.trim() != '') {
        return false;
      }
    }
    if (nbTextline != 1)
      throw new SimpleUIException(LCDStrings.get('one_textline'));
    if (nbHintgroup > 1)
      throw new SimpleUIException(LCDStrings.get('one_hintgroup_max'));
    return true;
  }
  
  @override
  h.Element html() {
    if (!simpleUI)
      return super.html();
    h.DivElement div = new h.DivElement();
    div.id = "$id";
    div.classes.add('dn');
    if (!valid)
      div.classes.add('invalid');
    div.classes.add('dnblock');
    
    h.DivElement headerDiv = createHeaderDiv();
    div.append(headerDiv);
    
    if (state != 2) {
      // for hintgroup
      h.DivElement contents = new h.DivElement();
      contents.id = 'contents-' + id;
      contents.classes.add('indent');
      contents.classes.add('dnblock-content');
      setStyle(contents);
      DaxeNode dn = firstChild;
      while (dn != null) {
        if (dn.nodeType != DaxeNode.ELEMENT_NODE || dn.nodeName != 'textline')
          contents.append(dn.html());
        dn = dn.nextSibling;
      }
      if (lastChild == null || lastChild.nodeType == DaxeNode.TEXT_NODE)
        contents.appendText('\n');
      div.append(contents);
    } else {
      headerDiv.classes.add('without-content-afterwards');
    }
    return(div);
  }
  
  @override
  h.Element createAttributesHTML() {
    h.Element attHTML = super.createAttributesHTML();
    if (!simpleUI || attHTML == null)
      return attHTML;
    // add the fake attribute (textline readonly)
    if (state == 0) {
      h.TableElement table = attHTML;
      for (DaxeNode dn in childNodes) {
        if (dn is Textline)
          table.append(dn.html());
      }
      return table;
    } else if (state == 1) {
      h.DivElement attDiv = attHTML;
      for (DaxeNode dn=firstChild; dn!= null; dn=dn.nextSibling) {
        if (dn is Textline && dn.getAttribute('readonly') != null) {
          attDiv.append(new h.Text(" "));
          h.Element att_name = new h.SpanElement();
          att_name.attributes['class'] = 'attribute_name';
          att_name.text = LCDStrings.get('readonly_field');
          attDiv.append(att_name);
          attDiv.append(new h.Text("="));
          h.Element att_val = new h.SpanElement();
          att_val.attributes['class'] = 'attribute_value';
          att_val.text = dn.getAttribute('readonly');
          attDiv.append(att_val);
        }
      }
      return attDiv;
    } else
      return null;
  }
  
  @override
  h.TableRowElement attributeHTML(x.Element refAttr) {
    // add another column, use it to add an edit button for certain fields
    h.TableRowElement tr = super.attributeHTML(refAttr);
    String name = doc.cfg.attributeName(refAttr);
    if (name == 'answer' || name == 'molecule') {
      h.TableCellElement td = tr.lastChild;
      if (name == 'answer')
        td.rowSpan = 2;
      h.ButtonElement bEdit = new h.ButtonElement();
      bEdit.attributes['type'] = 'button';
      bEdit.text = LCDStrings.get('edit_with_jsme');
      bEdit.onClick.listen((h.MouseEvent event) {
        String jmeString;
        if (name == 'answer')
          jmeString = attributeControls['jmeanswer'].getValue();
        else if (name == 'molecule')
          jmeString = attributeControls['molecule'].getValue();
        if (jmeString == '')
          jmeString = null;
        JSMEDialog dlg;
        dlg = new JSMEDialog(() {
          if (name == 'answer') {
            String smiles = dlg.getSMILES();
            String jmeString = dlg.getJMEString();
            LinkedHashMap<String, DaxeAttr> attmap = getAttributesMapCopy();
            attmap['answer'] = new DaxeAttr('answer', smiles);
            attmap['jmeanswer'] = new DaxeAttr('jmeanswer', jmeString);
            doc.doNewEdit(new UndoableEdit.changeAttributes(this, new List.from(attmap.values)));
          } else if (name == 'molecule') {
            String jmeString = dlg.getJMEString();
            doc.doNewEdit(new UndoableEdit.changeAttribute(this, new DaxeAttr('molecule', jmeString)));
          }
        }, jmeString:jmeString);
        dlg.show();
      });
      td.append(bEdit);
      tr.append(td);
    } else if (name == 'jmeanswer') {
      h.TableCellElement td = tr.lastChild;
      td.remove();
    } else if (name == 'options' && (getAttribute('options') == null || !getAttribute('options').contains('\$'))) {
      h.TableCellElement td = tr.lastChild;
      td.remove();
      td = tr.lastChild;
      td.remove();
      td = new h.TableCellElement();
      td.colSpan = 2;
      td.classes.add('expand');
      List<String> optionList = parseOptions();
      List<String> optionNames = ['autoez', 'multipart', 'nostereo', 'reaction', 'number'];
      for (String option in optionNames) {
        h.LabelElement label = new h.LabelElement();
        h.CheckboxInputElement cb = new h.CheckboxInputElement();
        cb.checked = optionList.contains(option);
        cb.onChange.listen((h.Event event) {
          List<String> newList = new List.from(parseOptions());
          if (newList.contains(option))
            newList.remove(option);
          else
            newList.add(option);
          doc.doNewEdit(new UndoableEdit.changeAttribute(this,
              new DaxeAttr('options', newList.join(',')), updateDisplay:false));
        });
        label.append(cb);
        label.appendText(' ');
        label.appendText(LCDStrings.get("option_$option"));
        td.append(label);
        td.append(new h.BRElement());
      }
      tr.append(td);
      
    }
    return tr;
  }
  
  List<String> parseOptions() {
    String options = getAttribute('options');
    if (options == null || options.trim() == '')
      return new List<String>();
    List<String> list = options.split(',');
    for (int i=0; i<list.length; i++) {
      list[i] = list[i].trim();
    }
    return list;
  }
  
  @override
  void updateHTMLAfterChildrenChange(List<DaxeNode> changed) {
    updateHTML(); // because DaxeNode's update does not handle fake attributes
  }
  
  @override
  void updateAttributes() {
    updateHTML(); // TODO: optimize for speed
  }
  
  void setupRestrictions() {
    for (int i=0; i<attRefs.length; i++) {
      x.Element refAttr = attRefs[i];
      if (!simpleUIAttributes.contains(doc.cfg.attributeName(refAttr))) {
        attRefs.removeAt(i);
        i--;
      }
    }
    restrictedInserts = [];
  }
  
  @override
  void setupSimpleUI() {
    simpleUI = true;
    setupRestrictions();
    fixChildrenForSimpleUI();
    state = 0;
  }
  
  @override
  void newNodeCreationUI(ActionFunction okfct) {
    setupSimpleUI();
    okfct();
  }
  
  Textline newTextline() {
    List<x.Element> textlineRefs = doc.cfg.elementReferences('textline');
    x.Element textlineRef = doc.cfg.findSubElement(ref, textlineRefs);
    Textline textline = NodeFactory.create(textlineRef);
    textline.state = 1;
    return textline;
  }
  
  Hintgroup newHintgroup() {
    List<x.Element> hintgroupRefs = doc.cfg.elementReferences('hintgroup');
    x.Element hintgroupRef = doc.cfg.findSubElement(ref, hintgroupRefs);
    Hintgroup hintgroup = new Hintgroup.fromRef(hintgroupRef);
    hintgroup.state = 1;
    return hintgroup;
  }
  
  /**
   * This inserts a textline, hintgroup if missing.
   */
  void fixChildrenForSimpleUI() {
    LCDBlock textline = null;
    Hintgroup hintgroup = null;
    for (DaxeNode dn in childNodes) {
      if (dn.nodeType == DaxeNode.ELEMENT_NODE) {
        if (dn.nodeName == 'textline')
          textline = dn;
        else if (dn.nodeName == 'hintgroup')
          hintgroup = dn;
      }
    }
    if (textline == null) {
      textline = newTextline();
      appendChild(textline);
    }
    textline.userCannotRemove = true;
    if (hintgroup == null) {
      hintgroup = newHintgroup();
      appendChild(hintgroup);
    } else
      hintgroup.setupSimpleUI();
    hintgroup.userCannotRemove = true;
  }
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>