<!-- Copyright 2007-2008 Michael E. Hayden. All rights reserved. -->

<!-- Dynamic test program for Remote Objects -->
<!-- 6/05/08:
    - Modified for invoke of class methods
    - Invoke button refactored into a class for use with class or instance -->

<library>

<dataset name="ds0"/>

<!-- create the remote_server to use -->
<include href="./remote_server.lzx"/>

<!-- Class invoke_button handles invoking
        remote class or instance methods -->
<class name="invoke_button" extends="button">
  <!-- the remote_class or remote_instance object to do the invoke -->
  <attribute name="invoker" value="null" type="expression" />
  <!-- the object with method and params inputs -->
  <attribute name="input" value="null" type="expression" />
  <attribute name="dataobject" value="null" type="expression" />
  <attribute name="dataoptions" value="null" type="expression" />
  <handler name="oninit">
    this.setAttribute("text", 'Invoke');
  </handler>
  <method event="onclick">
    // check if we need to set dataobject and dataoptions
    if (canvas.classes.top_ctl.data.dataon) {
      this.dataobject = ds0;
      var do_txt = canvas.classes.top_ctl.dataoptions.getText();
      if (do_txt != "") {
        var dov = JSON.parse(do_txt);
Debug.write("Dataoptions:");
Debug.inspect(dov);
        this.dataoptions = dov;
      }
    } else {
      this.dataobject = null;
      this.dataoptions = null;
    }
    var p = parse_args(this.input.params.getText());
//Debug.write("Inspect p:")
//Debug.inspect(p);
    this.invoker.invoke(this.input.method.getText(), p, this);
    if (!this.invoker.invoke(this.input.method.getText(), p, this)) {
      Debug.write("invoker could not invoke!");
    }
  </method>
  <method event="onreturn" args="ret">
    Debug.write("Got onreturn:");
    Debug.inspect(ret);
//      Debug.write("Inspect ds0:");
//      Debug.inspect(ds0);
  </method>
  <method event="onreturnerror" args="emsg">
    Debug.write("Got onreturnerror:", emsg);
  </method>
</class>

<!-- Class to instantiate remote class objects and display UI components to
       allow invoking methods on the instances created. Attributes:
       - rclass is the remote class object to instantiate
       - next_x is the x coordinate to use to display the instance
-->
<class name="instantiater" extends="view">
  <attribute name="rclass" type="expression" />
  <attribute name="next_x" value="${0}" type="expression"/>
  
   <view name="cl_name">  <!-- the views representing the Remote Class -->
    <simplelayout axis="y"/>
    <text fontsize="14" fgcolor="yellow">
      <handler name="oninit">
        this.setAttribute("text", classroot.rclass.name);
      </handler>
    </text>
    <view name="lc">
      <simplelayout axis="x"/>
      <view name="labels">
        <simplelayout axis="y"/>
        <text name="l_m" text="Method:" fontsize="14" fgcolor="yellow" />
        <text name="l_p" text="Params:" fontsize="14" fgcolor="yellow" />
      </view>
      <view name="class">
        <simplelayout axis="y"/>
        <edittext name="method" width="116" text=""/>
        <edittext name="params" width="116" text=""/>
        <view name="class_buttons">
          <simplelayout axis="x"/>
          <invoke_button invoker="$once{classroot.rclass}" input="$once{parent.parent}" />
          <button>
            <handler name="oninit">
              this.setAttribute("text", 'New');
            </handler>
            <handler name="onclick">
              classroot.instantiate();
            </handler>
          </button>
        </view>
      </view>
    </view>
  </view>
  
  <method name="instantiate">
Debug.write("instantiater: rclass.name:", rclass.name);
    var p = parse_args(this.cl_name.lc.class.params.getText());
Debug.inspect(p);
    var n = get_inst_name(this.rclass.name);
    var rem_inst = new remote_instance(canvas.classes,
     {name: n, remote_class: this.rclass, createargs: p});
    var del = new LzDelegate(this , "instance_loaded");
    del.register(rem_inst, "onload");
  </method>
  <method name="instance_loaded" args="rem_inst">
    // attach invoker button to the instantiater
    var n = 'inv_' + rem_inst.name;
    var t = rem_inst.name + ': ' + rem_inst._object_id;
    if (this.next_x == 0) {
      var xval = this.x + this.width;
    } else {
      xval = this.next_x;
    }
    this.next_x = xval + 150;
    var inr = new rinstance(this, {name: n, text: t, rinst: rem_inst, x: xval});
  </method>
</class>

<!-- Class to represent a remote object instance and allow invocation
       of methods with a parameter list separated by commas
       Attributes:
       - rinst is a reference to the remote instance object used to invoke methods
       - text is the label string to show class name, instance number and object ID
-->
<class name="rinstance" extends="view">
  <attribute name="rinst" type="expression" />
  <attribute name="text" type="string"/>
  <handler name="oninit">
    this.label.setAttribute("text", this.text);
  </handler>
  <simplelayout axis="y"/>
  <text name="label" fontsize="14" fgcolor="yellow"/>
  <edittext name="method" width="150" text=""/>
  <edittext name="params" width="150" text=""/>
  <invoke_button invoker="$once{classroot.rinst}" input="$once{parent}" />
</class>

<!-- this view displays a top row set of controls followed by classes loaded under it
        vertically. Instances are created horizontally to the right of each loaded class -->
<view name="classes" x="5" y="52">
  <simplelayout axis="y" />
  <text fontsize="14" fgcolor="yellow">Remote Class Name: </text>
  <view name="top_ctl">
    <simplelayout axis="x" />
    <edittext width="150" name="rcn" text=""/>
    <button>Load
      <method event="onclick">
        var rcn = parent.rcn.getText();
        var rcnode = canvas.classes.searchImmediateSubnodes("name", rcn);
        if (rcnode) {
          // we have a remote_class by that name, see if any methods are loaded
          if (rcnode.class_methods.length > 0) {
            Debug.write("Class", rcn, "already loaded!");
            return;
          } else { // just try to load it again until we get some methods
            rcnode.load();
            return;
          }
        }
        // create the remote class object
        var rem_class = new remote_class(canvas.classes,
          {name: rcn, class_name: rcn, remote_server: canvas.rs_robj, autoload: false});
        // set up delegates
        var del = new LzDelegate(this , "class_loaded");
        del.register(rem_class, "onload");
        del = new LzDelegate(this , "class_error");
        del.register(rem_class, "onerror");
        // load the remote class
        rem_class.load();
      </method>
      <method name="class_loaded" args="rem_class">
        // attach instantiater button to the classes view
        var n = 'inr_' + rem_class.name;
        var t = 'New ' + rem_class.name;
        var inr = new instantiater(canvas.classes,
          {name: n, text: t, rclass: rem_class});
      </method>
      <method name="class_error" args="emsg">
        Debug.write("Class load error:", emsg);
        canvas.classes
      </method>
    </button>
    <view width="10"/>
    <edittext name="dataoptions" width="150" text=""/>
    <button name="data" >Data On
      <attribute name="dataon" type="boolean" value="false"/>
      <method event="onclick">
        if (this.dataon) {
Debug.write("set data off");
          this.dataon = false;
          this.setAttribute("text", "Data On");  // label button to turn on later
          
        } else {
Debug.write("set data on");
          this.dataon = true;
          this.setAttribute("text", "Data Off");  // label button to turn off later
        }
      </method>
    </button>
    <button><b>i</b>
      <method event="onclick">
        Debug.write("Inspect ds0:");
        Debug.inspect(ds0);
      </method>
    </button>
  </view>
</view>
<!-- labels for data controls -->
<text x="218" y="52" fontsize="14" fgcolor="yellow">Dataoptions: </text>
<!--<text x="480" y="52" fontsize="14" fgcolor="yellow">Component: </text> -->

<script><![CDATA[
dataon = false;

class_instances = {};
// create an instance name from a class name
function get_inst_name(cn) {
  if (class_instances[cn] == null) {
    class_instances[cn] = 1;
  } else {
    class_instances[cn]  += 1;
  }
// drop class name since it is too long and can be found in instantiater
//  return(cn + '_' + class_instances[cn].toString());
  return('#' + class_instances[cn].toString());
}

// parse arguments and return an array containing them
// removes white space and parses integers and floats
function parse_args(ps) {
  if (ps == "") {return new Array();} //empty array
  var sps = ps.split(',');
//Debug.write("parse_args:");
//Debug.inspect(sps);
  var rp = [];
  var n = 0;
  var pv = null;
  for (var i = 0; i < sps.length; i++) {
    var ts = trim_space(sps[i]);
//Debug.write("sps[i]:", ts);
    if (is_number_char(ts.charAt(0))) {
      if (has_float_char(ts)) {
        pv = parseFloat(ts)
      } else {
        pv = parseInt(ts)
      }
    } else {
      pv = ts;
    }
//Debug.write("pv:", pv);
    rp[n] = pv;
    n += 1;
  }
  return(rp);
}

function is_number_char(c) {
  var numchar = ['+', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.'];
  for (var i = 0; i < numchar.length; i++) {
    if (c == numchar[i]) {return(true)}
  }
  return(false);
}

function has_float_char(ns) {
  var floatchar = ['e', '.'];
  for (var i = 0; i < floatchar.length; i++) {
    if (ns.indexOf(floatchar[i]) >= 0) {return(true)}
  }
  return(false);
}

function trim_space(s) {
// laszlo does not support regular expressions
//  return(s.replace(/(^\s*|\s*$)/, ""));
  var sa = s.split(' ');
  return sa.join("");
}
]]>
</script>

</library>