// Generated by CoffeeScript 1.3.1

/*
#    klns.coffee
#    ~~~~~~~~~~~~~~~
#
#    Base Kaylee namespace declarations
#
#    :copyright: (c) 2012 by Zaur Nasibov.
#    :license: MIT, see LICENSE for more details.
*/


(function() {
  var Event, KayleeError, SESSION_DATA_ATTRIBUTE, WORKER_SCRIPT_URL, kl, on_action_received, on_node_registered, on_node_subscribed, on_node_unsubscibed, on_project_imported, on_task_completed, on_task_received, pj, util, worker_message_handler, _dom_include, _worker_include,
    __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
    __slice = [].slice,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };

  kl = {
    pj: {},
    config: {},
    AUTO_PROJECT_MODE: 0x2,
    MANUAL_PROJECT_MODE: 0x4,
    NO_SOLUTION: {
      '__klr__': 0x2
    },
    NOT_SOLVED: {
      '__klr__': 0x4
    }
  };

  pj = kl.pj;

  try {
    window.kl = kl;
  } catch (err) {

  }

  /*
  #    klutil.coffee
  #    ~~~~~~~~~~~~~
  #
  #    This is the base file of Kaylee client-side module.
  #    It contains the util and shared code used by Kaylee.
  #
  #    :copyright: (c) 2012 by Zaur Nasibov.
  #    :license: MIT, see LICENSE for more details.
  */


  util = {};

  kl.util = util;

  util.is_function = function(obj) {
    return typeof obj === 'function';
  };

  util.ends_with = function(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
  };

  Event = (function() {

    Event.name = 'Event';

    function Event(handler) {
      if (handler == null) {
        handler = null;
      }
      this.unbind = __bind(this.unbind, this);

      this.bind = __bind(this.bind, this);

      this.trigger = __bind(this.trigger, this);

      this.handlers = [];
      if (handler != null) {
        this.handlers.push(handler);
      }
      return;
    }

    Event.prototype.trigger = function() {
      var args, c, _i, _len, _ref;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      _ref = this.handlers;
      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
        c = _ref[_i];
        c.apply(null, args);
      }
    };

    Event.prototype.bind = function(handler) {
      this.handlers.push(handler);
    };

    Event.prototype.unbind = function(handler) {
      var t, _ref;
      if ((t = this.handlers.indexOf(handler)) > -1) {
        [].splice.apply(this.handlers, [t, t - t + 1].concat(_ref = [])), _ref;
      }
    };

    return Event;

  })();

  kl.Event = Event;

  KayleeError = (function(_super) {

    __extends(KayleeError, _super);

    KayleeError.name = 'KayleeError';

    function KayleeError() {
      KayleeError.__super__.constructor.apply(this, arguments);
    }

    return KayleeError;

  })(Error);

  kl.KayleeError = KayleeError;

  /*
  #    klajax.coffee
  #    ~~~~~~~~~~~~~
  #
  #    This is the base file of Kaylee client-side module.
  #    It contains a small AJAX library used in a browser's
  #    main JavaScript event loop.
  #
  #    :copyright: (c) 2013 by Zaur Nasibov.
  #    :license: MIT, see LICENSE for more details.
  */


  kl.ajax = function(url, method, data, success, fail) {
    var dl, key, req, val;
    if (success == null) {
      success = (function() {});
    }
    if (fail == null) {
      fail = (function() {});
    }
    req = new XMLHttpRequest();
    switch (method) {
      case "POST":
        if (!(data != null)) {
          data = {};
        }
        data = JSON.stringify(data);
        req.open('POST', url, true);
        req.setRequestHeader('Content-type', 'application/json; charset=utf-8');
        break;
      case "GET":
        if (data != null) {
          dl = [];
          for (key in data) {
            val = data[key];
            dl.push(key + '=' + encodeURIComponent(val));
          }
          url += '?' + dl.join('&');
        }
        req.open("GET", url, true);
    }
    req.onreadystatechange = function() {
      var response;
      if (req.readyState === 4) {
        if (req.status === 200 && (req.responseText != null)) {
          response = JSON.parse(req.responseText);
          if (response.error != null) {
            fail(response.error);
          } else {
            success(response);
          }
        } else if (!(response != null)) {
          fail('INVALID_STATE_ERR');
        } else {
          fail(response);
        }
      }
    };
    req.send(data);
  };

  kl.post = function(url, data, success, fail) {
    var _success;
    _success = function(resp_data) {
      if (resp_data.error != null) {
        return fail(resp_data.error);
      } else {
        return success(resp_data);
      }
    };
    kl.ajax(url, 'POST', data, _success, fail);
  };

  kl.get = function(url, data, success, fail) {
    var _success;
    if (arguments.length >= 2) {
      if (kl.util.is_function(data)) {
        fail = success;
        success = data;
        data = null;
      }
    }
    _success = function(resp_data) {
      if (resp_data.error != null) {
        return fail(resp_data.error);
      } else {
        return success(resp_data);
      }
    };
    kl.ajax(url, 'GET', data, _success, fail);
  };

  _dom_include = function(urls, success, fail) {
    var count, css, doc, failed, js, onerror, onload, sc, url, _i, _len;
    if (!(urls instanceof Array)) {
      urls = [urls];
    }
    count = urls.length;
    sc = 0;
    failed = false;
    for (_i = 0, _len = urls.length; _i < _len; _i++) {
      url = urls[_i];
      doc = document.getElementsByTagName('head')[0];
      onload = function() {
        sc += 1;
        if (sc === count && !failed) {
          return typeof success === "function" ? success() : void 0;
        }
      };
      onerror = function(msg) {
        failed = true;
        return typeof fail === "function" ? fail(msg) : void 0;
      };
      if (util.ends_with(url, '.js')) {
        js = document.createElement('script');
        js.setAttribute('type', 'text/javascript');
        js.setAttribute('src', url);
        js.onload = onload;
        js.onerror = onerror;
        doc.appendChild(js);
      } else if (util.ends_with(url, '.css')) {
        if (fail != null) {
          kl.exception('Kaylee is not able to invoke the fail()\
                    callback of kl.include(..) when loading stylesheets.');
          failed = true;
          return;
        }
        css = document.createElement("link");
        css.rel = 'stylesheet';
        css.type = 'text/css';
        css.href = url;
        css.onload = onload;
        doc.appendChild(css);
      }
    }
  };

  _worker_include = function(urls, success, fail) {
    var all_imported, error_message, url, _i, _len;
    if (!(urls instanceof Array)) {
      urls = [urls];
    }
    error_message = '';
    all_imported = true;
    for (_i = 0, _len = urls.length; _i < _len; _i++) {
      url = urls[_i];
      try {
        importScripts(url);
      } catch (error) {
        all_imported = false;
        error_message = "Error importing " + url + ": " + error.message;
        break;
      }
    }
    if (all_imported) {
      return typeof success === "function" ? success() : void 0;
    } else {
      return typeof fail === "function" ? fail(error_message) : void 0;
    }
  };

  kl.include = function(urls, success, fail) {
    if (typeof __DEFINE_WORKER !== "undefined" && __DEFINE_WORKER !== null) {
      return _worker_include(urls, success, fail);
    } else {
      return _dom_include(urls, success, fail);
    }
  };

  /*
  #    klbenchmark.coffee
  #    ~~~~~~~~~~~~~~~~~~
  #
  #    This is the base file of Kaylee client-side module.
  #    It contains a benchmark executer function.
  #
  #    :copyright: (c) 2012 by Zaur Nasibov.
  #    :license: MIT, see LICENSE for more details.
  */


  kl._test_node = function() {
    var bWorker;
    bWorker = !!window.Worker;
    if (!bWorker) {
      return {
        worker: false
      };
    } else {
      return {
        worker: true
      };
    }
  };

  /*
  #    kaylee.coffee
  #    ~~~~~~~~~~~~~
  #
  #    This is the base file of Kaylee client-side module.
  #
  #    :copyright: (c) 2012 by Zaur Nasibov.
  #    :license: MIT, see LICENSE for more details.
  */


  SESSION_DATA_ATTRIBUTE = '__kl_sd__';

  WORKER_SCRIPT_URL = (function(scripts) {
    var path, script;
    scripts = document.getElementsByTagName('script');
    script = scripts[scripts.length - 1];
    if (!(script.getAttribute.length != null)) {
      path = script.src;
    }
    path = script.getAttribute('src', -1);
    return path.slice(0, path.lastIndexOf('/') + 1 || 9e9) + 'klworker.js';
  })();

  kl._app = null;

  kl.api = {
    register: function() {
      kl.get("/kaylee/register", kl.node_registered.trigger, kl.server_error.trigger);
    },
    subscribe: function(name) {
      kl.post("/kaylee/apps/" + name + "/subscribe/" + kl.node_id, null, kl.node_subscribed.trigger, kl.server_error.trigger);
    },
    get_action: function() {
      kl.get("/kaylee/actions/" + kl.node_id, kl.action_received.trigger, kl.server_error.trigger);
    },
    send_result: function(result) {
      kl.post("/kaylee/actions/" + kl.node_id, result, (function(action_data) {
        kl.result_sent.trigger(result);
        return kl.action_received.trigger(action_data);
      }), kl._preliminary_server_error_handler);
    }
  };

  kl.register = function() {
    kl.api.register();
  };

  kl.subscribe = function(name) {
    kl._app = {
      name: name,
      config: null,
      mode: null,
      worker: null,
      subscribed: false,
      task: null,
      process_task: function() {}
    };
    kl.api.subscribe(name);
  };

  kl.get_action = function() {
    if (kl._app.subscribed === true) {
      kl.api.get_action();
    }
  };

  kl.send_result = function(data) {
    if (!(data != null)) {
      kl.error('Cannot send data: the value is empty.');
    }
    if (typeof data !== 'object') {
      kl.error('The returned result is not a JS object.');
    }
    if (SESSION_DATA_ATTRIBUTE in kl._app.task) {
      data[SESSION_DATA_ATTRIBUTE] = kl._app.task[SESSION_DATA_ATTRIBUTE];
    }
    kl.api.send_result(data);
    kl._app.task = null;
  };

  kl._message_to_worker = function(msg, data) {
    if (data == null) {
      data = {};
    }
    kl._app.worker.postMessage({
      'msg': msg,
      'data': data
    });
  };

  kl._preliminary_server_error_handler = function(err) {
    switch (err) {
      case 'INVALID_STATE_ERR':
        if (kl._app.subscribed === true) {
          this.get_action();
        }
    }
    kl.server_error.trigger(err);
  };

  on_node_registered = function(data) {
    var key, val, _ref;
    _ref = data.config;
    for (key in _ref) {
      val = _ref[key];
      kl.config[key] = val;
    }
    kl.node_id = data.node_id;
  };

  on_node_subscribed = function(config) {
    var app, include_urls, worker;
    app = kl._app;
    app.config = config;
    app.mode = config.__kl_project_mode__;
    switch (config.__kl_project_mode__) {
      case kl.AUTO_PROJECT_MODE:
        if (app.worker != null) {
          app.worker.terminate();
        }
        worker = new Worker(WORKER_SCRIPT_URL);
        app.worker = worker;
        worker.onmessage = function(e) {
          return worker_message_handler(e);
        };
        worker.onerror = function(e) {
          var msg;
          msg = "Line " + e.lineno + " in " + e.filename + ": " + e.message;
          return kl.error(msg);
        };
        kl._message_to_worker('import_project', {
          'kl_config': kl.config,
          'app_config': app.config
        });
        break;
      case kl.MANUAL_PROJECT_MODE:
        include_urls = [config.__kl_project_script_url__];
        if (config.__kl_project_styles__) {
          include_urls.push(config.__kl_project_styles__);
        }
        kl.include(include_urls, function() {
          return pj.init(app.config);
        });
        break;
      default:
        kl.error('Unknown Kaylee Project mode');
    }
  };

  on_node_unsubscibed = function(data) {
    var _ref;
    if ((_ref = kl._app.worker) != null) {
      _ref.terminate();
    }
    kl._app = null;
    kl.pj = null;
  };

  on_project_imported = function() {
    kl._app.subscribed = true;
    switch (kl._app.mode) {
      case kl.AUTO_PROJECT_MODE:
        kl._app.process_task = function(data) {
          return kl._message_to_worker('process_task', data);
        };
        break;
      case kl.MANUAL_PROJECT_MODE:
        kl._app.process_task = pj.process_task;
    }
    kl.get_action();
  };

  on_action_received = function(action) {
    switch (action.action) {
      case 'task':
        kl.task_received.trigger(action.data);
        break;
      case 'unsubscribe':
        kl.node_unsubscibed.trigger(action.data);
        break;
      default:
        kl.error("Unknown action: " + action.action);
    }
  };

  on_task_received = function(task) {
    kl._app.task = task;
    kl._app.process_task(task);
  };

  on_task_completed = function(result) {
    if ((kl._app != null) && (kl._app.task != null) && kl._app.subscribed === true) {
      kl.send_result(result);
    }
  };

  worker_message_handler = function(event) {
    var data, mdata, msg;
    data = event.data;
    msg = data.msg;
    mdata = data.data;
    switch (msg) {
      case '__kl_log__':
        kl.log(mdata);
        break;
      case '__kl_error__':
        kl.error(mdata);
        break;
      case 'project_imported':
        kl.project_imported.trigger();
        break;
      case 'task_completed':
        kl.task_completed.trigger(mdata);
    }
  };

  kl.log = function(msg) {
    console.log('Kaylee: ' + msg);
    return kl.message_logged.trigger(msg);
  };

  kl.error = function(msg) {
    kl.log("ERROR: " + msg);
    throw new kl.KayleeError(msg);
  };

  kl.node_registered = new Event(on_node_registered);

  kl.node_subscribed = new Event(on_node_subscribed);

  kl.node_unsubscibed = new Event(on_node_unsubscibed);

  kl.project_imported = new Event(on_project_imported);

  kl.action_received = new Event(on_action_received);

  kl.task_received = new Event(on_task_received);

  kl.task_completed = new Event(on_task_completed);

  kl.result_sent = new Event();

  kl.message_logged = new Event();

  kl.server_error = new Event();

}).call(this);
