-- $Id: sajax.lua,v 1.6 2009/03/16 22:53:10 carregal Exp $ local cgilua = require "cgilua" local table = require "table" local string = require "string" local os = require "os" local pairs, type, tostring, xpcall = pairs, type, tostring, xpcall local unpack = table.unpack or unpack local xavante = require "xavante" local httpd = require "xavante.httpd" local _M = {} local export_list = {} local request_uri if xavante and httpd then request_uri = function (funcname) return cgilua.mkabsoluteurl (cgilua.mkurlpath (cgilua.servervariable"SCRIPT_NAME" .. "/" .. funcname.. "?rs=rs")) end function _M.handle_client_request () return false end local function register_path (funcname) return cgilua.mkurlpath (cgilua.servervariable"SCRIPT_NAME" .. "/" .. funcname) end local function handle_prefix (req, res) res.headers ["Expires"] = "Mon, 26 Jul 1997 05:00:00 GMT" -- Date in the past res.headers ["Last-Modified"] = os.date ("!%a, %d %b %Y %H:%M:%S GMT") -- always modified res.headers ["Cache-Control"] = "no-cache, must-revalidate" -- HTTP/1.1 res.headers ["Pragma"] = "no-cache" -- HTTP/1.0 local qry = req.parsed_url.query or "" local rsargs = {} for rs_arg in string.gfind (qry, "rsargs%[%]=([^&=]*)&?") do table.insert (rsargs, rs_arg) end return rsargs end function _M.export (funcname, func) export_list[funcname] = func httpd.addHandler (nil, register_path (funcname), function (req, res) local isok, result = xpcall (function () return func (unpack (handle_prefix (req, res))) end, tostring) result = result or "" if isok then res.content = "+:" .. result else res.content = "-:" .. result end return res end) end else request_uri = function (funcname) local uri = "" if string.find (uri, "?") then uri = uri .. "&rs=" .. cgilua.urlcode.escape (funcname) else uri = uri .. "?rs=" .. cgilua.urlcode.escape (funcname) end return uri end function _M.handle_client_request () if not cgilua.QUERY.rs then return end -- Bust cache in the head cgilua.header ("Expires", "Mon, 26 Jul 1997 05:00:00 GMT") -- Date in the past cgilua.header ("Last-Modified", os.date ("!%a, %d %b %Y %H:%M:%S GMT")) -- always modified cgilua.header ("Cache-Control", "no-cache, must-revalidate") -- HTTP/1.1 cgilua.header ("Pragma", "no-cache") -- HTTP/1.0 local funcname = cgilua.QUERY.rs if not export_list[funcname] then cgilua.put (string.format ("-:%s not callable", funcname)) else local func = export_list[funcname] local rsargs = cgilua.QUERY["rsargs[]"] local result if not rsargs then result = func() elseif type (rsargs) == "string" then result = func (rsargs) elseif type (rsargs) == "table" then result = func (unpack (rsargs)) else return end cgilua.put ("+:") cgilua.put (result) end return true end function _M.export (funcname, func) export_list[funcname] = func end end local function get_common_js () return [[ // remote scripting library // released under BSD license // (c) copyright 2005 modernmethod, inc var sajax_debug_mode = false; function sajax_debug(text) { if (sajax_debug_mode) alert("RSD: " + text) } function sajax_init_object() { sajax_debug("rs_init_object() called..") var A; try { A=new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { try { A=new ActiveXObject("Microsoft.XMLHTTP"); } catch (oc) { A=null; } } if(!A && typeof XMLHttpRequest != "undefined") A = new XMLHttpRequest(); if (!A) sajax_debug("Could not create connection object."); return A; } function sajax_do_call(func_name, url, args) { var i, x, n; for (i = 0; i < args.length-1; i++) url = url + "&rsargs[]=" + escape(args[i]); url = url + "&rsrnd=" + new Date().getTime(); x = sajax_init_object(); x.open("GET", url, true); x.onreadystatechange = function() { if (x.readyState != 4) return; sajax_debug("received " + x.responseText); var status; var data; status = x.responseText.charAt(0); data = x.responseText.substring(2); if (status == "-") alert("Error: " + data); else args[args.length-1](data); } x.send(null); sajax_debug(func_name + " url = " + url); sajax_debug(func_name + " waiting.."); delete x; } function sajax_call(func_name, script, callback, args) { var url = script + "?rs=" + func_name; args.push(callback); sajax_do_call(func_name, url, args); } ]] end function _M.show_common_js() cgilua.put (get_common_js ()) end local function get_one_stub (funcname) local uri = request_uri (funcname) return (string.format ([[ // wrapper for %s function x_%s() { // count args; build URL sajax_do_call ("%s", "%s", x_%s.arguments); } ]], funcname, funcname, funcname, uri, funcname)) --[[ var i, x, n; var url = "%s"; var a = x_%s.arguments; for (i = 0; i < a.length-1; i++) url = url + "&rsargs[]=" + escape(a[i]); url = url + "&rsrnd=" + new Date().getTime(); x = rs_init_object(); x.open("GET", url, true); x.onreadystatechange = function() { if (x.readyState != 4) return; rs_debug("received " + x.responseText); var status; var data; status = x.responseText.charAt(0); data = x.responseText.substring(2); if (status == "-") alert("Error: " + callback_n); else a[a.length-1](data); } x.send(null); rs_debug("x_%s url = " + url); rs_debug("x_%s waiting.."); delete x; } ] ], funcname, funcname, uri, funcname, funcname, funcname)) --]] end local function show_one_stub (funcname) cgilua.put (get_one_stub (funcname)) end local js_has_been_shown = false function _M.get_javascript () local js = {} if not js_has_been_shown then table.insert (js, get_common_js ()) js_has_been_shown = true end for fn,_ in pairs (export_list) do table.insert (js, get_one_stub (fn)) end return (table.concat (js)) end function _M.show_javascript () cgilua.put (_M.get_javascript ()) end function _M.init () end return _M