File:  [LON-CAPA] / modules / damieng / graphical_editor / loncapa_daxe / web / nodes / lcd_block.dart
Revision 1.24: download - view: text, annotated - select for diffs
Tue Feb 28 20:40:59 2017 UTC (7 years, 3 months ago) by damieng
Branches: MAIN
CVS tags: HEAD
updates following changes in Daxe Config API

/*
  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;

/**
 * Generic block element for LON-CAPA
 * Attributes can be edited directly in it.
 * Buttons can be used to collapse the element and the attribute fields.
 * Jaxe display type: 'lcdblock'.
 */
class LCDBlock extends DNBlock {
  
  bool simpleUI = false;
  bool displaySimpleButton = false;
  
  
  LCDBlock.fromRef(x.Element elementRef) : super.fromRef(elementRef) {
  }
  
  LCDBlock.fromNode(x.Node node, DaxeNode parent) : super.fromNode(node, parent) {
    // replace CDATA sections by text
    DaxeNode child = firstChild;
    while (child != null) {
      DaxeNode next = child.nextSibling;
      if (child is DNCData) {
        if (child.previousSibling is DNText && child.previousSibling.nodeValue == '\n')
          removeChild(child.previousSibling);
        if (child.nextSibling is DNText && child.nextSibling.nodeValue == '\n') {
          next = child.nextSibling.nextSibling;
          removeChild(child.nextSibling);
        }
        String text;
        if (child.firstChild != null)
          text = child.firstChild.nodeValue;
        else
          text = null;
        if (text != null)
          insertBefore(new DNText(text), child);
        removeChild(child);
      }
      child = next;
    }
    normalize();
  }
  
  /**
   * Returns true if the node can be displayed with a simple UI.
   * This is replaced by subclasses.
   * Subclasses can throw a SimpleUIException with a message explaining why
   * it is not possible to switch to simple UI.
   */
  bool simpleUIPossible() {
    return false;
  }
  
  /**
   * Returns true if the node can be displayed with a simple UI, false otherwise,
   * without throwing any Exception. Uses simpleUIPossible() and catches possible exceptions.
   */
  bool simpleUIPossibleNoThrow() {
    bool possible;
    try {
      possible = simpleUIPossible();
    } on Exception {
      possible = false;
    }
    return possible;
  }
  
  /**
   * Called when the button is used.
   * This can be replaced by subclasses.
   */
  void switchToSimpleUI() {
    // Replace the whole node, to keep the simpleUI status in the edit history
    // (to avoid problems when names and values have to be changed automatically)
    UndoableEdit edit = new UndoableEdit.compound(LCDStrings.get('switch_to_simple_ui'));
    Position pos = new Position(parent, parent.offsetOf(this));
    edit.addSubEdit(new UndoableEdit.removeNode(this));
    simpleUI = true;
    LCDBlock newNode = new DaxeNode.clone(this);
    simpleUI = false;
    edit.addSubEdit(new UndoableEdit.insertNode(pos, newNode));
    doc.doNewEdit(edit);
    page.scrollToNode(newNode);
  }
  
  /**
   * Called by default switchToSimpleUI() when switching to simple UI.
   * This can be replaced by subclasses.
   */
  void setupSimpleUI() {
  }
  
  /**
   * Called when the button is used.
   */
  void switchToAdvancedUI() {
    UndoableEdit edit = new UndoableEdit.compound(LCDStrings.get('switch_to_advanced_ui'));
    Position pos = new Position(parent, parent.offsetOf(this));
    edit.addSubEdit(new UndoableEdit.removeNode(this));
    simpleUI = false;
    LCDBlock newNode = new DaxeNode.clone(this);
    simpleUI = true;
    newNode.restrictedInserts = null;
    newNode.init();
    newNode.state = 1;
    // remove any empty hintgroup
    for (DaxeNode dn in newNode.childNodes) {
      if (dn is Hintgroup && dn.firstChild == null)
        newNode.removeChild(dn);
    }
    edit.addSubEdit(new UndoableEdit.insertNode(pos, newNode));
    doc.doNewEdit(edit);
    page.scrollToNode(newNode);
  }
  
  @override
  h.DivElement createHeaderDiv() {
    h.DivElement headerDiv = super.createHeaderDiv();
    
    // add simple/advanced switch to the default block header
    h.DivElement titleDiv = headerDiv.firstChild;
    h.SpanElement titleSpan = titleDiv.nodes[1];
    if (simpleUI) {
      h.ButtonElement advanced = new h.ButtonElement();
      advanced.classes.add('lcd-advanced');
      advanced.appendText(LCDStrings.get('advanced'));
      advanced.onClick.listen((h.MouseEvent event) {
        switchToAdvancedUI();
        page.updateAfterPathChange();
      });
      titleDiv.insertBefore(advanced, titleSpan);
    } else if (displaySimpleButton) {
      h.ButtonElement simple = new h.ButtonElement();
      simple.classes.add('lcd-advanced');
      simple.appendText(LCDStrings.get('simple'));
      simple.onClick.listen((h.MouseEvent event) {
        bool possible;
        String message = null;
        try {
          possible = simpleUIPossible();
          if (!possible)
            message = LCDStrings.get('simpleui_not_possible'); // default message
        } on SimpleUIException catch (ex) {
          message = ex.message; // precise message
          possible = false;
        }
        if (possible) {
          switchToSimpleUI();
          page.updateAfterPathChange();
        } else
          h.window.alert(message);
      });
      titleDiv.insertBefore(simple, titleSpan);
    }
    
    return headerDiv;
  }
  
  @override
  Position firstCursorPositionInside() {
    if (hasContent && !simpleUI)
      return(new Position(this, 0));
    else
      return(null);
  }
  
  @override
  Position lastCursorPositionInside() {
    if (hasContent && !simpleUI)
      return(new Position(this, offsetLength));
    else
      return(null);
  }
  
  @override
  x.Node toDOMNode(x.Document domDocument) {
    // do not output an empty hintgroup in simple UI
    DaxeNode hintgroup = null;
    DaxeNode next = null;
    if (simpleUI) {
      for (DaxeNode dn=firstChild; dn != null; dn=dn.nextSibling) {
        if (dn is Hintgroup && dn.firstChild == null) {
          hintgroup = dn;
          next = dn.nextSibling;
          break;
        }
      }
    }
    if (hintgroup != null)
      removeChild(hintgroup);
    x.Node dom = super.toDOMNode(domDocument);
    if (hintgroup != null)
      insertBefore(hintgroup, next);
    dom.setUserData('simpleUI', simpleUI, null);
    return dom;
  }
  
  @override
  h.TableRowElement attributeHTML(x.Element refAttr) {
    h.TableRowElement tr = super.attributeHTML(refAttr);
    
    // add <input type=color> to help picking colors
    String name = doc.cfg.attributeQualifiedName(this, ref, refAttr);
    h.TableCellElement td = tr.nodes[2];
    SimpleTypeControl attributeControl = attributeControls[name];
    String value = attributeControl.value;
    h.Element ht = td.firstChild;
    if (refAttr.getAttribute('type') == 'color-or-perl') {
      h.InputElement colorInput = new h.InputElement();
      colorInput.type = 'color';
      if (value.length == 7)
        colorInput.value = '#' + value.substring(1);
      colorInput.onChange.listen((h.Event e) => attributeControl.setValue(colorInput.value));
      td.append(colorInput);
      if (ht.firstChild is h.TextInputElement) {
        h.TextInputElement input = ht.firstChild;
        input.size = 8;
        input.style.width = 'initial';
        input.onKeyUp.listen((h.Event e) {
          // update the color input if a user types the color
          if (input.value.length == 7) {
            colorInput.value = '#' + input.value.substring(1);
          }
        });
      }
    }
    
    return(tr);
  }
  
}

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