<html>
  <head>
    <title>JSON-RPC Browser</title>
    <script type="application/x-javascript" src="{% url jsonrpc_browser %}?f=mochikit.js"></script>
    <script type="application/x-javascript" src="{% url jsonrpc_browser %}?f=interpreter.js"></script>
    <script type="application/x-javascript">
    METHOD_NAMES = {{method_names_str|safe}};
    
    function nextSibling(E) {
      do {
        E = E.nextSibling;
      } while (E && E.nodeType != 1);
      return E;
    }
    
    function previousSibling(E) {
      do {
        E = E.previousSibling;
      } while (E && E.nodeType != 1);
      return E;
    }
    
    Accordion = function(container, options) {
      this.showAccordion = null;
      this.currentAccordion = null;
      this.effets = [];
      this.animating = false;
      this.container = container;
      this.options = merge(Accordion.defaultOptions, options || {});
      this.duration = ((11 - this.options.resizeSpeed) * 0.15);
      
      var c = $(container);
      if (!c) throw (container + " doesnt exist");
      
      var self = this;
      var accordions = $$(format("#{0} .{1}", container, this.options.classNames.toggle));
      forEach(accordions,
        function(_accordion) {
          connect(_accordion, self.options.onEvent, function() { self.activate(_accordion); });
          var opts;
          if (self.options.direction == 'horizontal') {
            opts = {width: '0px'};
          } else {
            opts = {height: '0px'};
          }
          update(opts, {display: 'none'});
          self.currentAccordion = nextSibling(_accordion);
          setStyle(self.currentAccordion, opts);
        }
      );
    }
    
    Accordion.defaultOptions = {
      resizeSpeed: 8,
      classNames: {
        toggle: 'accordion_toggle',
        toggleActive: 'accordion_toggle_active',
        content: 'accordion_content'
      },
      defaultSize: {
        height: null,
        width: null
      },
      direction: 'vertical',
      onEvent: 'onclick'
    }
    
    Accordion.prototype.activate = function(accordion) {
      if (this.animating) return false;
      this.effects = [];
      this.currentAccordion = nextSibling(accordion);
      setStyle(this.currentAccordion, {display: 'block'});
      addElementClass(previousSibling(this.currentAccordion), 
        this.options.classNames.toggleActive);
      if (this.options.direction == 'horizontal') {
        this.scaling = {scaleX: true, scaleY: false};
      } else {
        this.scaling = {scaleX: false, scaleY: true};
      }
      if (this.currentAccordion == this.showAccordion) {
        this.deactivate();
      } else {
        this._handleAccordion();
      }
    }
    
    Accordion.prototype.deactivate = function() {
      var self = this;
      var opts = {
        duration: this.duration,
        transition: 'sinoidal',
        queue: {position: 'end', scope: 'accordionAnimation'},
        scaleContent: false,
        scaleMode: {
          oringnalHeight: this.options.defaultSize.height 
                            ? this.options.defaultSize.height 
                            : this.currentAccordion.scrollHeight,
          originalWidth: this.options.defaultSize.height
                            ? this.options.defaultSize.width
                            : this.currentAccordion.scrollWidth
        },
        afterFinish: function() {
          setStyle(self.showAccordion, {height: 'auto', display: 'none'});
          self.showAccordion = null;
          self.animating = false;
        }
      }
      update(opts, this.scaling);
      removeElementClass(previousSibling(this.showAccordion), this.options.classNames.toggleActive);
      new Scale(this.showAccordion, 0, opts);
    }
    
    Accordion.prototype._handleAccordion = function() {
      var self = this;
      var opts = {
        sync: true,
        scaleFrom: 0,
        scaleContent: false,
        transition: 'sinoidal',
        scaleMode: {
          oringnalHeight: this.options.defaultSize.height 
                ? this.options.defaultSize.height : this.currentAccordion.scrollHeight,
          originalWidth: this.options.defaultSize.height
                ? this.options.defaultSize.width : this.currentAccordion.scrollWidth
        }
      };
      update(opts, this.scaling);
      this.effects.push(new Scale(this.currentAccordion, 100, opts));
      if (this.showAccordion) {
        removeElementClass(previousSibling(this.showAccordion), this.options.classNames.toggleActive);
        opts = {
          sync: true,
          scaleContent: false,
          transition: 'sinoidal'
        }
        update(opts, this.scaling);
        this.effects.push(new Scale(this.showAccordion, 0, opts));
      }
      new Parallel(this.effects, {
        duration: this.duration,
        queue: {position: 'end', scope: 'accordionAnimation'},
        beforeStart: function() { self.animating = true; },
        afterFinish: function() {
          if (self.showAccordion) {
            setStyle(self.showAccordion, {display: 'none'});
          }
          setStyle(self.currentAccordion, {height: 'auto'});
          self.showAccordion = self.currentAccordion;
          self.animating = false;
        }
      });
    }
    
    function jsonMethod(name) {
      return function() {
        var req = serializeJSON({
          'id': 'jsonrpc',
          'params': flattenArguments(arguments),
          'method': name,
          'jsonrpc': '1.0'
        });
        writeln('Requesting ->');
        writeln(SPAN({'class': 'json_req'}, req));
        var d = doXHR('{% url jsonrpc_mountpoint %}', {
          'method': 'POST',
          'sendContent': req,
          'mimeType': 'application/x-javascript'
        });
        d.addCallback(function (req) {
          writeln('Got ->');
          writeln(SPAN({'class': 'json_req'}, req.responseText));
          req = evalJSONRequest(req);
          window.json_result = req;
          window.json_error = null;
          return req;
        });
        d.addErrback(function (err) {
          writeln('Error ->');
          req_err = err;
          try {
            err = evalJSONRequest(err.req);
            if (!err)
              throw('');
          } catch (e) { err = req_err; }
          writeln(SPAN({'class': 'json_req'}, 'STATUS: ' + req_err.number),
                  BR(), 
                  SPAN({'class': 'json_req'}, repr(err)));
          window.json_result = null;
          window.json_error = err;
          return err;
        })
        return d;
      }
    }
    // init method accordion
    addLoadEvent(
      function() {
        new Accordion('method_accordion');
      });
    // bind jsonrpc methods under window.jsonrpc
    addLoadEvent(
      function() {
        window.json_result = null;
        window.json_error = null;
        forEach(METHOD_NAMES,
          function(method) {
            var part = 'jsonrpc';
            var parts = method.split('.');
            var ns = window;
            do {
              if (typeof ns[part] == 'undefined')
                ns[part] = {};
              ns = ns[part];
              part = parts.shift();
            } while (parts.length);
            ns[part] = jsonMethod(method);
          });
      });
    </script>
    <style>
      body {
        font-family: utopia, 'lucidamac bold', 'lucida grande', arial, sans-serif;
        background-color: #464646;
      }
      #header {
        font-family: Georgia, "Times New Roman", times, serif;
        font-size: 2em;
        width: 600px;
        height: 40px;
        margin: 10 auto;
        color: white;
      }
      #container {
        -webkit-border-radius:10px;
        background-color: #fefff1;
        width: 600px;
        /*height: 600px;*/
        margin: 0 auto;
        padding: 10px;
      }
      #container h2 { padding: 0; margin: 0 0 5px 0; }
      textarea.textbox {
          font-family: Monaco, "lucida console", Courier;
          border: 1px solid #CCCCCC;
          font-size: .60em; 
          padding: 2px 4px;
          margin-top: .3em;
      }

      input.textbox {
          font-family: Monaco, "lucida console", Courier;
          border: 1px solid #CCCCCC;
          font-size: .60em; 
          padding: 2px 4px;
          margin-top: .3em;
      }

      #interpreter_area {
          display: block;
          border: 1px solid #CCCCCC;
          padding: 2px 4px;
          margin-top: .3em;
          width: 590px;
          height: 300px;
          overflow: auto;
      }

      #interpreter_output {
          display: inline;
          font-family: Monaco, "lucida console", Courier;
          font-size: .60em;
      }

      #interpreter_output span {
          white-space: -moz-pre-wrap; /* Mozilla */
          white-space: -o-pre-wrap; /* Opera 7 */
          white-space: pre-wrap; /* CSS 2.1 */
          white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
          word-wrap: break-word; /* IE */
          wrap-option: emergency; /* CSS 3 */
      }

      input.textbox:focus { background-color: #FFFEE3; }

      .code { color: blue; }
      .data { color: black; }
      .error { color: red; }
      .banner { color: green; }
      .invisible { display: none; }
      
      .accordion_toggle {
  			display: block;
  			height: 30px;
  			width: 580px;
  			background-color: #a9d06a;
  			padding: 0 10px 0 10px;
  			line-height: 30px;
  			color: #ffffff;
  			font-weight: normal;
  			text-decoration: none;
  			outline: none;
  			font-size: 12px;
  			color: #000000;
  			border-bottom: 1px solid #cde99f;
  			cursor: pointer;
  			margin: 0 0 0 0;
  		}

  		.accordion_toggle_active {
  			background-color: #0070b0;
  			color: #ffffff;
  			border-bottom: 1px solid #003c5f;
  		}

  		.accordion_content {
  			background-color: #ffffff;
  			color: #444444;
  			overflow: hidden;
  		}
			
      /*div.collapsable h3 {
        font-size: .9em;
        margin: 0 0 4px 0;
        padding: 1px 0 1px 0;
        background-color: #d9ffd9;
        border-width: 1px 0 1px 0;
        border-color: #00bd3a;
        border-style: solid;
      }*/
      div.collapsable table {
        font-size: 12px;
        margin-left: 6px;
      }
      div.collapsable th { text-align: left; padding: 0px; margin: 0px; }
      div.collapsable td { font-family: Monaco, "lucida console", Courier; padding: 0px; margin: 0px; }
      
      span.json_req { color: #d04e00; display: inline-block; margin: 0; padding: 0 0 0 5px; }
      
      ul.interpreter_help { padding: 0 0 0 20px; margin: 0; font-size: 12px; }
      ul.interpreter_help li {padding: 0; margin: 0; }
      div.footer { margin-top: 10px; margin-bottom: 10px; font-size: 11px; color: white; text-align: center; }
      div.footer a, div.footer a:visited, div.footer a:hover, div.footer a:active { color: #9affab; }
      
      span.type { color: rgb(96,45,12); font-weight: bold; }
    </style>
  </head>
  <body>
    <div id="header">JSON-RPC Browser</div>
    <div id="container">
      <h2>Methods</h2>
      <div id="method_accordion" class="collapsable">
        {% for method in methods %}
        <h3 class="accordion_toggle">{{method.name}}</h3>
        <div class="accordion_content">
          <table><tbody valign="top">
            <tr><th>signature</th><td>
    {{method.name}}({% for param in method.params %}{{param.name}}{% if not forloop.last %}, {% endif %}{% endfor %})</td></tr>
            <tr><th>arguments</th><td>
              {% if method.params %}
              {% for param in method.params %}
                {{param.name}} : <span class="type">{{param.type}}</span>
                {% if not forloop.last %}<br /> {% endif %}
              {% endfor %}
              {% else %}
              <em>None Specified</em>
              {% endif %}
            </td></tr>
            <tr><th>returns</th><td><span class="type">{{method.return.type}}</span></td></tr>
            <tr><th>idempotent</th><td>{{method.idempotent}}</td></tr>
            <tr><th>summary</th><td>{{method.summary}}</td></tr>
            </tbody>
          </table>
        </div>
        {% endfor %}
      </div><br />
      <h2>Console</h2>
      <form id="interpreter_form" autocomplete="off"> 
        <div id="interpreter_area"> 
          <div id="interpreter_output"></div> 
        </div> 
        <div id="oneline"> 
          <input id="interpreter_text" name="input_text" type="text" class="textbox" style="width:600px;" /> 
        </div> 
        <div id="multiline" style="display:none"> 
          <textarea id="interpreter_textarea" name="input_textarea" type="text" class="textbox" cols="97" rows="10"></textarea> 
          <br /> 
        </div> 
      </form>
      <ul class="interpreter_help">
        <li>JSON-RPC methods are exposed through the <strong><code>jsonrpc</code></strong> global.</li>
        <li>Call a method just as you would a javascript one: <code>jsonrpc.jsonrpc.test('arg1')</code></li>
        <li>Calling a method returns a Deferred (see the <a href="http://www.mochikit.com/doc/html/MochiKit/Async.html">MochiKit docs</a>). 
          As soon as the deferred returns the result is available by the global variables <strong><code>json_result</code></strong> or <strong><code>json_error</code></strong></li>
        <li>Use <code>dir</code> to explore the namespace: <code>dir(jsonrpc)</code></li>
      </ul>
    </div>
    <div class="footer">Powered by <a href="http://djangoproject.com/">Django</a>, 
      <a href="http://github.com/samuraisam/django-json-rpc">django-json-rpc</a> and <a href="http://mochikit.com/">MochiKit</a>
  </body>
</html>