Roxen.git / server / modules / js-support / scripts / roxen / modules / afs.js

version» Context lines:

Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:1:   /*global ROXEN, YAHOO */ + /* jshint indent: 2 */      /**    * Module interface to the new Action File System    *    * @module afs    * @class AFS    * @namespace ROXEN    * @static    */   ROXEN.AFS = function () {       // 1 - Log AFS calls and responses.    // 2 - Also log calls to tagged callbacks, i.e. the single-response    // callbacks passed to call().    // 3 - Also log calls to global and error callbacks. -  var debug_log = 0; +  var debug_log = parseInt(ROXEN.getQueryVariable(window.location.href, +  "__afs-debug"), 10);       var session = ROXEN.config.session;       /**    * AFS actions path prefix.    */    var actions_prefix = "/actions/";       /**    * AFS session variable name in action URLs.
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:79:    // Counter to produce unique tags.    var tag_count = 0;       // Set of callback functions that will receive all AFS responses.    var global_callbacks = [];       // Set of callback functions that get called when there are    // low-level AFS errors (connection errors or JSON format errors).    var error_callbacks = [];    +  // Set on init(). +  var is_initialized = false; +     function encode_afs_args (args)    {    var query = [];    var json_args = {}, got_json_args = false;       // Send strings as ordinary variables. Everything else is sent as    // json in the special __afs variable. That way it's possible to    // send variables that are parsed by the server outside the main    // afs query handler.   
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:111:       if (got_json_args)    query.push ("__afs=" + ROXEN.escapeURIComponent(YAHOO.lang.JSON.stringify (json_args)));       return query.join ("&");    }       function request_failure (resp)    {    ROXEN.log("ROXEN.AFS: connection error: " + -  resp.status + " " + resp.statusText + "\n"); +  resp.status + " " + resp.statusText);       for (var i = 0; i < error_callbacks.length; i++) {    var cb = error_callbacks[i];    if (debug_log > 2) -  ROXEN.log (" AFS calling error callback: " + cb.name + "\n"); +  ROXEN.log (" AFS calling error callback: " + cb.name); +  try {    cb (resp); -  +  } catch (err) { +  ROXEN.log ("AFS: error in callback " + cb.name + ": " + err); +  if (ROXEN.debug) +  throw err;    } -  +  }       // Forget all ongoing calls since we cannot hope to receive any    // useful responses to them anyway after this.    tagged_callbacks = {};    groups = {};       if (open_connections) {    open_connections = 0;    restart_poll (true);    }       connection_ok = false;    }       function json_parse_failure (err)    { -  ROXEN.log("ROXEN.AFS: JSON parse error: " + err + "\n"); +  ROXEN.log("ROXEN.AFS: JSON parse error: " + err);       for (var i = 0; i < error_callbacks.length; i++) {    var cb = error_callbacks[i];    if (debug_log > 2) -  ROXEN.log (" AFS calling error callback: " + cb.name + "\n"); +  ROXEN.log (" AFS calling error callback: " + cb.name);    // FIXME: Need a flag to tell it apart from a connection error? -  +  +  try {    cb (err); -  +  } catch (e) { +  ROXEN.log ("AFS: error in callback " + cb.name + ": " + e); +  if (ROXEN.debug) +  throw e;    } -  +  }       // Forget all ongoing calls since we cannot hope to receive any    // useful responses to them anyway after this.    tagged_callbacks = {};    groups = {};       if (open_connections) {    open_connections = 0;    restart_poll (true);    }       connection_ok = false;    }       function request_success (resp)    { -  var msgs, cb; +  var msgs, cb, ct; +  +  // YAHOO.util.Connect.asyncRequest follows redirect transparently and +  // we want to detect the case when this is a redirect to a login page. +  ct = resp.getResponseHeader["Content-Type"]; +  if (ct && !ct.match(/application\/json/)) { +  json_parse_failure(new Error("ContentTypeError")); +  return; +  } +     try {    msgs = YAHOO.lang.JSON.parse(resp.responseText);    }    catch (err) {    json_parse_failure (err);    return;    }    msgs._etag = resp.getResponseHeader.Etag || resp.getResponseHeader.ETag;       for (var i = 0; i < msgs.length; i++) {    var msg = msgs[i];    var tag = msg.tag;       if (tag) {    if (debug_log) -  ROXEN.log ("AFS response: " + msg.msg_type + ", tag " + tag + "\n"); +  ROXEN.log ("AFS response: " + msg.msg_type + ", tag " + tag);       var ent = tagged_callbacks[tag];    cb = ent && ent[0];    if (ent === undefined)    ROXEN.log ("ROXEN.AFS: Warning: Got AFS response with " + -  "unknown tag: " + msg.msg_type + "\n"); +  "unknown tag: " + msg.msg_type);    else if (cb == -1) {    if (debug_log > 1)    ROXEN.log (" AFS tagged callback was canceled");    delete tagged_callbacks[tag];    } else {    if (debug_log > 1) -  ROXEN.log (" AFS calling tagged callback: " + cb.name + "\n"); -  cb (msg); +  ROXEN.log (" AFS calling tagged callback: " + cb.name); +  if (ROXEN.debug) +  cb(msg); +  else { +  try { +  cb(msg); +  } catch (err) { +  ROXEN.log("AFS: error in callback " + cb.name + ": " + err); +  } +  }    // Assume no more than one response with a given tag. See    // also AFS.ClientSession.push_response.    delete tagged_callbacks[tag];    var group = ent[1];    if (group) {    var g = groups[group];    g.splice(g.indexOf(tag), 1);    }    }    }       else {    if (debug_log) -  ROXEN.log ("AFS response: " + msg.msg_type + "\n"); +  ROXEN.log ("AFS response: " + msg.msg_type);    }       for (var j = 0; j < global_callbacks.length; j++) {    cb = global_callbacks[j];    if (debug_log > 2) -  ROXEN.log (" AFS calling global callback: " + cb.name + "\n"); +  ROXEN.log (" AFS calling global callback: " + cb.name); +  try {    cb (msg); -  +  } catch (err) { +  ROXEN.log ("AFS: error in callback " + cb.name + ": " + err); +  if (ROXEN.debug) +  throw err;    }    } -  +  }       if (open_connections > 0) {    open_connections--;    if (!open_connections) restart_poll (false);    }       connection_ok = true;    }       /**
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:246:    * sent to the global callbacks only.    * @param {Object} scope Scope correction (optional).    * @param {String} group Group identifier (optional).    * @return {Object} Returns the connection object.    */    function call(action, args, fn, scope, group) {    return call_or_post(action, "GET", args, 0, fn, scope, group);    }       function post(action, args, postargs, fn, scope, group) { -  if (!ROXEN.isObject(postargs)) +  if (ROXEN.isString(postargs)) { +  // ID or name of a <form> tag that should be encoded +  YAHOO.util.Connect.setForm(postargs); +  postargs = undefined; +  } else if (!ROXEN.isObject(postargs)) {    postargs = { }; -  +  } +     var postdata = [ ]; -  +  if (postargs) {    var item = 0; -  for (var idx in postargs) { +  for (var idx in postargs) +  if (postargs.hasOwnProperty(idx)) {    postdata[item++] =    encodeURIComponent(idx) + "=" + encodeURIComponent(postargs[idx]);    } -  +  }    return call_or_post(action, "POST", args, postdata.join("&"), fn, scope);    }    -  +  function post_files(action, args, files, fn, scope, group) +  { +  // File should come from the browser's FileList API. We will use FormData +  // together with XHR to post directly to the server without involving YUI. +  +  // Prepare AFS call +  if (!ROXEN.isObject(args)) +  args = { }; +  if (scope) +  fn = ROXEN.bind(scope, fn); +  if (fn) { +  var tag = ++tag_count + ""; +  if (debug_log) +  ROXEN.log ("AFS call: " + action + " " + +  YAHOO.lang.JSON.stringify (args) + +  ", callback " + fn.name + ", tag " + tag); +  args.tag = tag; +  tagged_callbacks[tag] = [ fn, group ]; +  } else if (debug_log) { +  ROXEN.log ("AFS call: " + action + " " + +  YAHOO.lang.JSON.stringify (args)); +  } +  if (fn && group) { +  if (!groups[group]) groups[group] = [ ]; +  groups[group].push(args.tag); +  } +  +  args[session_var] = session; +  open_connections++; +  +  // Initiate transfer +  var url = actions_prefix + action + "?" + encode_afs_args(args); +  var fd = new FormData(); +  for (var i = 0; i < files.length; i++) { +  fd.append("upload-file-" + i, files[i], files[i].name); +  } +  var xhr = new XMLHttpRequest(); +  xhr.open("POST", url, true); +  xhr.onreadystatechange = function() { +  if (xhr.readyState === 4) { +  if (xhr.status === 200) { +  // Invoke success callback +  request_success(xhr); +  } else if (xhr.status === 0 || xhr.status >= 400) { +  // Invoke failure callback +  request_failure(xhr); +  } +  } +  }; +  xhr.send(fd); +  } +     function call_or_post(action, method, args, postdata, fn, scope, group) {    if (!ROXEN.isObject(args)) {    args = {};    }       if (scope)    fn = ROXEN.bind (scope, fn);       if (fn) {    var tag = ++tag_count+"";    if (debug_log)    ROXEN.log ("AFS call: " + action + " " +    YAHOO.lang.JSON.stringify (args) + -  ", callback " + fn.name + ", tag " + tag + "\n"); +  ", callback " + fn.name + ", tag " + tag);    args.tag = tag;    tagged_callbacks[tag] = [ fn, group ];    }    else {    if (debug_log)    ROXEN.log ("AFS call: " + action + " " +    YAHOO.lang.JSON.stringify (args));    }       args[session_var] = session;
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:370:    }    }       /**    * Adds a callback that will be called whenever there is a low-level    * AFS error; either a connection error or a syntax error in a JSON    * response.    *    * @param {Function} fn    * Callback function. It will be called with a single argument -  * which is either a YAHOO.util.Connect.asyncRequest failure -  * handler response, or an exception object thrown by +  * which is either a YAHOO.util.Connect.asyncRequest or XMLHttpRequest +  * failure handler response, or an exception object thrown by    * YAHOO.lang.JSON.parse.    * @param {Object} scope    * Optional scope correction.    * @return {Function}    * Returns the function actually added, which can be used in a later -  * call to remove_global_callback. +  * call to remove_error_callback.    */    function add_error_callback (fn, scope)    {    if (scope)    fn = ROXEN.bind (scope, fn);    error_callbacks.push (fn);    return fn;    }       /**
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:415:    {    if (poll_timeout == -1 && !ROXEN.isUndefined (response.poll_interval))    poll_delay = response.poll_interval;    }       function restart_poll (after_error)    {    if (poll_timeout >= -1 && !poll_delay_timeout_id) {    var delay = (after_error ? poll_error_delay : poll_delay) * 1000;    if (debug_log > 2) -  ROXEN.log ("AFS poll delay " + delay + " ms\n"); +  ROXEN.log ("AFS poll delay " + delay + " ms");    poll_delay_timeout_id =    window.setTimeout (function () {    poll_delay_timeout_id = undefined;    call ("poll",    {timeout : poll_timeout,    interval : poll_delay},    poll_callback);    }, delay);    }    }       /**    * (Re)initializes the AFS lib. All open connections are forgotten,    * and a poll action is started right away (provided polling is    * enabled).    *    * Global and error callbacks are not forgotten, and responses from    * old open connections can still be delivered to them (subject to    * change if necessary).    * -  * @param {String} afs_actions_path Path prefix for all AFS actions. -  * If not provided /actions/ is used. +  * @param {String} actions_prefix Path prefix for all AFS actions. +  * If not provided /actions/ is used. +  * @param {String} session_var Session variable name. Will be set to +  * "session_var" If not provided. +  * @param {Number} poll_timeout Poll timeout in seconds, or -1 for +  * auto mode (default), and -2 to disable.    */    function init(options)    {    if (options) { -  if (options["actions_prefix"]) -  actions_prefix = options["actions_prefix"]; -  if (options["session_var"]) -  session_var = options["session_var"]; -  if (options["poll_timeout"]) -  poll_timeout = options["poll_timeout"]; +  if (options.actions_prefix) +  actions_prefix = options.actions_prefix; +  if (options.session_var) +  session_var = options.session_var; +  if (options.poll_timeout) +  poll_timeout = options.poll_timeout;    }       if (debug_log) -  ROXEN.log ("AFS init\n"); +  ROXEN.log ("AFS init");       connection_ok = false;    open_connections = 0;    tagged_callbacks = {};       if (poll_delay_timeout_id) {    clearTimeout (poll_delay_timeout_id);    poll_delay_timeout_id = undefined;    }       if (poll_timeout >= -1 && !open_connections)    call ("poll",    {timeout : poll_timeout,    interval : poll_delay},    poll_callback); -  +  +  is_initialized = true;    }       /** -  +  * Will return true after init() has been called once. +  */ +  function initialized() { +  return is_initialized; +  } +  +  /**    * Makes an asynchronous GET request to any URL.    *    * Note that this really has nothing to do with AFS.    *    * @method request    * @param {String} url Requested URL.    * @param {Function} fn Callback function to run when done.    * fn's argument list is (result, status).    * status is AFS_REQUEST_SUCCESS or    * AFS_REQUEST_FAILURE.
Roxen.git/server/modules/js-support/scripts/roxen/modules/afs.js:491:    * @return {Object} Returns the connection object.    */    function request(url, fn, scope) {    var cb = {    cache: false,    success: function (o) {    fn.call(scope, o, "AFS_REQUEST_SUCCESS");    },    failure: function (o) {    ROXEN.log("ROXEN.AFS: connection error: " + -  o.status + " " + o.statusText + "\n"); +  o.status + " " + o.statusText);    fn.call(scope, o, "AFS_REQUEST_FAILURE");    }    };    return YAHOO.util.Connect.asyncRequest("GET", url, cb);    }       return {    call: call,    post: post, -  +  post_files: post_files,    abort: abort,    has_connection: has_connection,    add_global_callback: add_global_callback,    remove_global_callback: remove_global_callback,    add_error_callback: add_error_callback,    remove_error_callback: remove_error_callback,    init: init, -  +  initialized: initialized,    request: request    };   }();