
<erl>


box(Str) ->
    {'div',[{class,"box"}],
     {pre,[], yaws_api:htmlize(Str)}}.

tbox(T) ->
    box(lists:flatten(io_lib:format("~p",[T]))).


ssi(File) ->
    {'div',[{class,"box"}],
     {pre,[],
      {ssi, File,[],[]}}}.

ss(A, File) ->
    {ok, B} = file:read_file(
                filename:join([A#arg.docroot, File])),
    box(binary_to_list(B)).



out(A) ->
    [{ssi, "TAB.inc", "%%",[{"json_intro", "choosen"}]},
     {ehtml,
      {'div', [{id, "entry"}],

      [{h1, [], "AJAX through JSON RPC"},

        {p, [],
         {i, [],
          ["Note: this documentation used to refer to the module "
           "'yaws_jsonrpc', but that module was deprecated in favor of "
           "'yaws_rpc', which handles JSON RPC, haXe and SOAP remoting. "
           "For more specific information about SOAP, refer to ",
           {a, [{href, "/soap_intro.yaws"}], "the SOAP page."}]}},
       {p, [],
        ["The Yaws JSON-RPC binding is a way to have JavaScript code in the "
         "browser evaluate a remote procedure call (RPC) in the Yaws server. "
         "JSON itself as described at ",
         {a, [{href, "https://www.json.org/"}], "https://www.json.org/ "},
         "is basically a simple marshaling format which can be used "
         "from a variety of different programming languages, and "
         "naturally it's completely straightforward to implement "
         "in JavaScript itself. JSON-RPC version 2.0, the version Yaws "
         "supports, is described here:"]},
       {p, [],
        [{a, [{href,
         "https://groups.google.com/group/json-rpc/web/json-rpc-2-0"}],
         "https://groups.google.com/group/json-rpc/web/json-rpc-2-0"}]},
       {p, [],
        "The Yaws JSON-RPC implementation consist of JavaScript clients and a "
        "server side library that must be explicitly invoked by Erlang "
        "code in a .yaws page, appmod, etc."},

       {p,[],
        "It is not particularly easy to show and explain an AJAX setup "
        "through JSON-RPC, but here is an attempt:"
        },
       {p,[],
        "First we have an HTML page which:"},
       {ol, [],
        [
         {li,[],{p,[],
                 ["Includes the client side of the JSON library. "
                  "The library is included in the Yaws distribution "
                  "and it is found under ",
                  {a,
                   [{href,
                     "https://github.com/erlyaws/yaws/blob/master/www/jsolait/jsolait.js"}],
                   "\"www/jsolait/jsolait.js\""}, "."]}},
         {li,[],{p,[],"Second, the HTML code defines the name of a method, "
                 "i.e. the name of a server-side function that shall be "
                 "called by the client side JavaScript code."}},
         {li,[],{p,[],"Finally the HTML code defines a FORM that's "
                 "used to invoke the RPC. This is just a really simple "
                 "example, really any JavaScript code can invoke any RPC in "
                 "more interesting scenarios than submitting a form."}}]},

       {p, [], "The HTML code appears as shown below:"},
       ss(A,"json_sample.html"),
       {p, [], ["This HTML code resides in file ",
                {a,[{href, "json_sample.html"}], "json_sample.html"},
                " and it is the HTML code that is the AJAX GUI."]},
       {p, [], "Following that we need to take a look at json_sample.yaws "
        " (shown below), which is the \"serviceURL\" according to "
        "the JavaScript code. This code defines the function to be "
        "called. Remember that the JavaScript code defined one method, "
        "called \"test1\"; this information will be passed to the "
        "serviceURL. The code looks like:"},
       ss(A, "json_sample.yaws"),

       {p,[], "The two important lines on the server side are"},
       {ol,[],
        [
         {li,[],
          {pre,[],"yaws_rpc:handler_session(A2, {sample_mod, counter})."}},
         {li,[],
          {pre,[],"counter([{ip, IP}] = _State, {call, test1, Value} = _Request, Session)"}}]},

       {p,[],
        ["The first line tells Yaws to forward all JSON-RPC methods to the "
         " \"counter\" function in the \"sample_mod\" module. "
         "The second line is the head of the counter function that will be "
         "called when the client invokes a method called 'test1'. We would "
         "duplicate this line with a different name than 'test1' for each RPC "
         "function we wish to implement. Note that the first atom in the "
         "request tuple will either be 'call' or 'notification' to indicate "
         "the type of request. As per the ",
         {a,[{href,"https://groups.google.com/group/json-rpc/web/json-rpc-2-0"}],
          "JSON-RPC 2.0 specification"},
         ", a 'call' is a regular request-reply while a 'notification' is a "
         "one-way message that does not have a corresponding reply."]},

       {p,[],"On the client side we have"},

       box("
var methods = [ \"test1\" ];
var jsonrpc = imprt(\"jsonrpc\");
var service = new jsonrpc.ServiceProxy(serviceURL, methods);
"),

      {p,[],"This registers the Yaws page with the JSON-RPC handler and "
            "gives it a list of methods that the Yaws page can satisfy. "
            "In this case, the only method called 'test1'."},

      {p, [],
"When we wish to return structured data, we simply let "
"the user-defined RPC function return JSON structures such as "},

box("{struct, [{field1, \"foo\"}, {field2, \"bar\"}]} "),
{p, [], " for a structure and "},
box("{array, [\"foo\", \"bar\"]}"),
{p, [],"for an array. We can nest arrays and structs in each other."},

       {p, [],
        "Finally, we must stress that this example is extremely simple. "
        "In order to build a proper AJAX application in Yaws, a lot of "
        "client side  work is required, all Yaws provides is the basic "
        "mechanism whereby the client side JavaScript code can RPC the "
        "web server for data which can be subsequently used to populate "
        "the DOM. Also required to build a good AJAX application is "
        "good knowledge of how the DOM in the browser works"},

       {p, [],
        ["The yaws_rpc:handler will also call: M:F(cookie_expire) "
         "which is expected to return a proper Cookie expire string. This "
         "makes it possible to setup the Cookie lifetime. If this callback "
         "function is non-existent, the default behaviour is to not set a "
         "cookie expiration time, i.e., it will live for this session only."]},

       {h3, [], "One more example "},

       {p, [],
        ["Here is yet another example, stolen from ",
         {a,
          [{href,"https://www.redhoterlang.com/entry/ac061493b201e3d1b4490cdc3f911068"}],
          "Tobbe's blog."}
         ]},
        {h4, [], "Setup the DOM"},
        {p, [], "In the file ''ex1.html'' we create the DOM with a little HTML and add some JavaScript that will talk with the Erlang server side."},
        box("

<html>
<head>
<script type=\"application/javascript\"
           src=\"/jquery-1.2.3.js\"></script>
</head>
<body>

<script language=\"javascript\" type=\"application/javascript\">

function ex1(what) {
   $.getJSON(\"/ex1.yaws\",
            {'op': \"ex1\", 'what': what},
            function(x) {
              do_ex1(what, x)
            });
}

function do_ex1(what, x) {
  jQuery.each(x, doit);
}

function doit() {
  $('#'+this.who).html(this.what);
}

</script>

<button onclick=\"ex1('one')\">Update one!</button>
<button onclick=\"ex1('two')\">Update two!</button>
<button onclick=\"ex1('three')\">Update three!</button>

<div id=\"one\">This is one</div>
<div id=\"two\">This is two</div>
<div id=\"three\">This is three</div>

</body>
</html>
"),
       {h4, [], "The erlang server side"},
       {p, [], "This is the code that needs to be installed and "
               "execute on the server side. It nicely illustrates how "
               "to return JSON structs to the client. "},
       box("
-module(ex1).
-export([out/1]).

out(A) ->
    L = yaws_api:parse_query(A),
    dispatch(lkup(\"op\", L, false), A, L).

dispatch(\"ex1\", A, L) ->
    ex1(A, L).

ex1(_A, L) ->
    J = json2:encode(array(what(lkup(\"what\", L, false)))),
    return_json(J).

what(\"one\")   -> one();
what(\"two\")   -> one() ++ two();
what(\"three\") -> one() ++ two() ++ three().

array(L) -> {array, L}.

one()   -> obj(\"one\").
two()   -> obj(\"two\").
three() -> obj(\"three\").

obj(M) ->
    obj(M, \"r\").

%%%
%%% How ::= \"r\" | \"a\"  , r=replace, a=append
%%%
obj(M, How) ->
    C = now2str(),
    [{struct,
      [{\"who\", M},
       {\"how\", How},
       {\"what\", C ++\" \"++M++\" content\"}]}].

return_json(Json) ->
    {content,
    \"application/json; charset=iso-8859-1\",
    Json}.

now2str() ->
    {A,B,C} = erlang:now(),
    i2l(A)++\"-\"++i2l(B)++\"-\"++i2l(C).

i2l(I) when is_integer(I) -> integer_to_list(I);
i2l(L) when is_list(L)    -> L.

lkup(Key, List, Def) ->
    case lists:keysearch(Key, 1, List) of
    {value,{_,Value}} -> Value;
    _                 -> Def
    end.

") ,
     {h2, [], "The json library"},
     {p, [],
      ["The Yaws JSON library contains 3 simple functions, "
       " one for encoding and two for decoding. See source code ",
       {a,
        [{href,
          "https://github.com/erlyaws/yaws/blob/master/src/json2.erl"}],
        "json2.erl"},
       " for detailed instructions on usage."]}

      ]}},

     {ssi, "END2",[],[]}
    ].

</erl>
