<commit_before>var http = require('http');
var url = require('url');
var Stream = require('stream');
var Frame = require('./frame');
var Builder = require('./builder');
var runner = require('./runner');

var Argo = function(_http) {
  this._router = {};
  this._routerKeys = [];
  this.builder = new Builder();
  this._http = _http || http;

  var that = this;
  var incoming = this._http.IncomingMessage.prototype;

  if (!incoming._argoModified) {
    var _addHeaderLine = incoming._addHeaderLine;

    incoming._addHeaderLine = function(field, value) {
      this._rawHeaderNames = this._rawHeaderNames || {};
      this._rawHeaderNames[field.toLowerCase()] = field;

      _addHeaderLine.call(this, field, value);
    };

    incoming.body = null;
    incoming.getBody = that._getBody();
    incoming._argoModified = true;
  }

  var serverResponse = this._http.ServerResponse.prototype;
  if (!serverResponse._argoModified) {
    serverResponse.body = null;
    serverResponse.getBody = that._getBody();

    serverResponse._argoModified = true;
  }
};

Argo.prototype._getBody = function() {
  var that = this;
  return function(callback) {
    if (this.body) {
      callback(null, this.body);
      return;
    }
    var buf = [];
    var len = 0;

    this.on('data', function(chunk) {
      buf.push(chunk);
      len += chunk.length;
    });

    this.on('end', function() {
      var body;
      if (buf.length && Buffer.isBuffer(buf[0])) {
        body = new Buffer(len);
        var i = 0;
        buf.forEach(function(chunk) {
          chunk.copy(body, i, 0, chunk.length);
          i += chunk.length;
        });
      } else if (buf.length) {
        body = buf.join('');
      }

      this.body = body;

      callback(null, body);
    });
  };
};

Argo.prototype.include = function(mod) {
  var p = mod.package(this);
  p.install();
  return this;
};

Argo.prototype.listen = function(port) {
  runner.listen(this, port);
  return this;
};

Argo.prototype.use = function(middleware) {
  if (middleware.package) {
    return this.include(middleware);
  }
  this.builder.use(middleware);
  return this;
};

Argo.prototype.target = function(url) {
  return this.use(function(addHandler) {
    addHandler('request', function(env, next) {
      env.target.url = url + (env.request.url || '');
      next(env);
    });
  });
};

Argo.prototype.embed = function() {
  this.buildCore();

  this.builder.run(this._target);
  this.builder.use(function(addHandler) {
    addHandler('response', { affinity: 'sink' }, function(env, next) {
      console.log(env.toString());
      if (env.argo.oncomplete) {
        env.argo.oncomplete(env);
      };
    });
  });

  return this.builder.build();
}

Argo.prototype.buildCore = function() {
  var that = this;

  that.builder.use(function(addHandler) {
    addHandler('request', function(env, next) {
      env.argo._http = that._http;
      next(env);
    });
  });

  var hasRoutes = false;
  for (var prop in that._router) {
    if (!hasRoutes && that._router.hasOwnProperty(prop)) {
      hasRoutes = true;
    }
  }

  if (hasRoutes) {
    that.builder.use(function addRouteHandlers(handlers) {
     that._route(that._router, handlers);
    });
  }
};

Argo.prototype.build = function() {
  var that = this;

  that.buildCore();

  that.builder.run(that._target);

  // response ender
  that.builder.use(function(handle) {
    handle('response', { affinity: 'sink' }, function(env, next) {
      if (env.response.body) {
        var body = env.response.body;
        if (typeof body === 'string') {
          env.response.setHeader('Content-Length', body ? body.length : 0);
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end(body);
        } else if (body instanceof Stream) {
          env.response.writeHead(env.response.statusCode, env.response.headers);
          body.pipe(env.response);
        } else if (typeof body === 'object') {
          body = new Buffer(JSON.stringify(body), 'utf-8');
          if (!env.response.getHeader('Content-Type')) {
            env.response.setHeader('Content-Type', 'application/json; charset=UTF-8');
          }
          env.response.setHeader('Content-Length', body ? body.length : 0);
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end(body.toString('utf-8'));
        }
      } else {
        var contentLength = env.response.getHeader('Content-Length');
        if (contentLength == '0') {
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end();
        } else if (env.target.response) {
          env.target.response.getBody(function(err, body) {
            env.response.setHeader('Content-Length', body ? body.length : 0);
            env.response.writeHead(env.response.statusCode, env.response.headers);
            env.response.end(body);
          });
        } else {
          env.response.setHeader('Content-Length', '0');
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end();
        }
      }
    });
  });

  return that.builder.build();
};

Argo.prototype.call = function(env) {
  var app = this.build();
  return app.flow(env);
}

Argo.prototype.route = function(path, options, handlers) {
  if (typeof(options) === 'function') {
    handlers = options;
    options = {};
  }

  options.methods = options.methods || ['*'];
  if (!this._router[path]) {
    this._router[path] = {};
  }

  var that = this;
  options.methods.forEach(function(method) {
    that._router[path][method.toLowerCase()] = handlers;
  });

  that._routerKeys.push(path);
  that._routerKeys.sort(function(a, b) {
    if (a.length > b.length) {
      return -1;
    } else if (a.length < b.length) {
      return 1;
    }

    return 0;
  });

  return this;
};

var methods = {
  'get': 'GET',
  'post': 'POST',
  'put': 'PUT',
  'del': 'DELETE',
  'head': 'HEAD',
  'options': 'OPTIONS',
  'trace': 'TRACE'
};

Object.keys(methods).forEach(function(method) {
  Argo.prototype[method] = function(path, options, handlers) {
    if (typeof(options) === 'function') {
      handlers = options;
      options = {};
    }
    options.methods = [methods[method]];
    return this.route(path, options, handlers);
  };
});

Argo.prototype.map = function(path, options, handler) {
  if (typeof(options) === 'function') {
    handler = options;
    options = {};
  }

  options.methods = options.methods || ['*'];
  if (!this._router[path]) {
    this._router[path] = {};
  }

  function generateHandler(path, handler) {
    var argo = new Argo();
    handler(argo);

    var app = argo.embed();

    return function(addHandler) {
      addHandler('request', function mapHandler(env, next) {
        env.argo.frames = env.argo.frames || [];

        var frame = new Frame();
        frame.routed = env.argo._routed;
        frame.routedResponseHandler = env.argo._routedResponseHandler;

        env.argo._routed = false;
        env.argo._routedResponseHandler = null;

        if (env.request.url[env.request.url.length - 1] === '/') {
          env.request.url = env.request.url.substr(0, env.request.url.length - 1);
        }

        if (env.argo.frames.length) {
          pathLength = 0;
          env.argo.frames.forEach(function(frame) {
            pathLength += frame.routeUri.length;
          });
        }

        frame.routeUri = path || '/';

        env.request.url = env.request.url.substr(frame.routeUri.length);
        env.request.url = env.request.url || '/';

        // TODO: See if this can work in a response handler here.

        if (env.argo.oncomplete) {
          frame.oncomplete = env.argo.oncomplete;
        }

        env.argo.currentFrame = frame;
        env.argo.frames.push(frame);

        env.argo.oncomplete = function(env) {
          var frame = env.argo.frames.pop();

          env.argo._routed = frame.routed;
          env.argo._routedResponseHandler = frame.routedResponseHandler;
          env.request.url = frame.routeUri + env.request.url;
          env.argo.oncomplete = frame.oncomplete;

          next(env);
        };

        app.flow(env);
      });
    };
  };

  return this.route(path, options, generateHandler(path, handler));
};

Argo.prototype._pipeline = function(name) {
  return this.builder.pipelineMap[name];
};

Argo.prototype._addRouteHandlers = function(handlers) {
  return function add(name, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts;
      opts = null;
    }

    if (name === 'request') {
      handlers.request = cb;
    } else if (name === 'response') {
      handlers.response = cb;
    }
  };
};

function RouteHandlers() {
  this.request = null;
  this.response = null;
}

Argo.prototype._routeRequestHandler = function(router) {
  var that = this;
  return function routeRequestHandler(env, next) {
    env.argo._routed = false;

    var search = env.request.url;

    var routerKey;
    if (search === '/' && that._router['/']) {
      routerKey = '/';
    } else {
      that._routerKeys.forEach(function(key) {
        if (!routerKey && key !== '*' && search.search(key) !== -1 && key !== '/') {
          routerKey = key;
        }
      });
    }

    if (!routerKey && that._router['*']) {
      routerKey = '*';
    }

    if (routerKey &&
        (!router[routerKey][env.request.method.toLowerCase()] &&
         !router[routerKey]['*'])) {
      env.response.statusCode = 405;
      next(env);
      return;
    }

    if (routerKey &&
        (router[routerKey][env.request.method.toLowerCase()] ||
         router[routerKey]['*'])) {
      env.argo._routed = true;

      var method = env.request.method.toLowerCase();
      var fn = router[routerKey][method] ? router[routerKey][method]
        : router[routerKey]['*'];

      var handlers = new RouteHandlers();
      fn(that._addRouteHandlers(handlers));

      env.argo._routedResponseHandler = handlers.response || null;

      if (handlers.request) {
        handlers.request(env, next);
      } else {
        next(env);
        return;
      }
    }

    if (!env.argo._routed) {
      next(env);
    }
  };
};

Argo.prototype._routeResponseHandler = function(router) {
  var that = this;
  return function routeResponseHandler(env, next) {
    if (!env.argo._routed) {
      if (env.response.statusCode !== 405) {
        env.response.statusCode = 404;
      }

      next(env);
      return;
    }

    if (env.argo._routedResponseHandler) {
      env.argo._routedResponseHandler(env, next);
      return;
    } else {
      next(env);
      return;
    }
  };
};

Argo.prototype._route = function(router, handle) {
  handle('request', this._routeRequestHandler(router));
  handle('response', { affinity: 'hoist' }, this._routeResponseHandler(router));
};

Argo.prototype._target = function(env, next) {
  if (env.response._headerSent || env.target.skip) {
    next(env);
    return;
  }
  var start = +Date.now();

  if (env.target && env.target.url) {
    var options = {};
    options.method = env.request.method || 'GET';

    // TODO: Make Agent configurable.
    options.agent = new env.argo._http.Agent();
    options.agent.maxSockets = 1024;

    var parsed = url.parse(env.target.url);
    options.hostname = parsed.hostname;
    options.port = parsed.port || 80;
    options.path = parsed.path;

    options.headers = env.request.headers;
    options.headers['Connection'] = 'keep-alive';
    options.headers['Host'] = options.hostname;

    if (parsed.auth) {
      options.auth = parsed.auth;
    }

    var req = env.argo._http.request(options, function(res) {
      for (var key in res.headers) {
        var headerName = res._rawHeaderNames[key] || key;
        env.response.setHeader(headerName, res.headers[key]);
      }

      env.target.response = res;

      if (next) {
        var duration = (+Date.now() - start);
        next(env);
      }
    });

    env.request.getBody(function(err, body) {
      if (body) {
        req.write(body);
      }

      req.end();
    });
  } else {
    next(env);
  }
};

module.exports = function(_http) { return new Argo(_http) };
<commit_msg>Removing a rogue console.log.<commit_after>var http = require('http');
var url = require('url');
var Stream = require('stream');
var Frame = require('./frame');
var Builder = require('./builder');
var runner = require('./runner');

var Argo = function(_http) {
  this._router = {};
  this._routerKeys = [];
  this.builder = new Builder();
  this._http = _http || http;

  var that = this;
  var incoming = this._http.IncomingMessage.prototype;

  if (!incoming._argoModified) {
    var _addHeaderLine = incoming._addHeaderLine;

    incoming._addHeaderLine = function(field, value) {
      this._rawHeaderNames = this._rawHeaderNames || {};
      this._rawHeaderNames[field.toLowerCase()] = field;

      _addHeaderLine.call(this, field, value);
    };

    incoming.body = null;
    incoming.getBody = that._getBody();
    incoming._argoModified = true;
  }

  var serverResponse = this._http.ServerResponse.prototype;
  if (!serverResponse._argoModified) {
    serverResponse.body = null;
    serverResponse.getBody = that._getBody();

    serverResponse._argoModified = true;
  }
};

Argo.prototype._getBody = function() {
  var that = this;
  return function(callback) {
    if (this.body) {
      callback(null, this.body);
      return;
    }
    var buf = [];
    var len = 0;

    this.on('data', function(chunk) {
      buf.push(chunk);
      len += chunk.length;
    });

    this.on('end', function() {
      var body;
      if (buf.length && Buffer.isBuffer(buf[0])) {
        body = new Buffer(len);
        var i = 0;
        buf.forEach(function(chunk) {
          chunk.copy(body, i, 0, chunk.length);
          i += chunk.length;
        });
      } else if (buf.length) {
        body = buf.join('');
      }

      this.body = body;

      callback(null, body);
    });
  };
};

Argo.prototype.include = function(mod) {
  var p = mod.package(this);
  p.install();
  return this;
};

Argo.prototype.listen = function(port) {
  runner.listen(this, port);
  return this;
};

Argo.prototype.use = function(middleware) {
  if (middleware.package) {
    return this.include(middleware);
  }
  this.builder.use(middleware);
  return this;
};

Argo.prototype.target = function(url) {
  return this.use(function(addHandler) {
    addHandler('request', function(env, next) {
      env.target.url = url + (env.request.url || '');
      next(env);
    });
  });
};

Argo.prototype.embed = function() {
  this.buildCore();

  this.builder.run(this._target);
  this.builder.use(function(addHandler) {
    addHandler('response', { affinity: 'sink' }, function(env, next) {
      if (env.argo.oncomplete) {
        env.argo.oncomplete(env);
      };
    });
  });

  return this.builder.build();
}

Argo.prototype.buildCore = function() {
  var that = this;

  that.builder.use(function(addHandler) {
    addHandler('request', function(env, next) {
      env.argo._http = that._http;
      next(env);
    });
  });

  var hasRoutes = false;
  for (var prop in that._router) {
    if (!hasRoutes && that._router.hasOwnProperty(prop)) {
      hasRoutes = true;
    }
  }

  if (hasRoutes) {
    that.builder.use(function addRouteHandlers(handlers) {
     that._route(that._router, handlers);
    });
  }
};

Argo.prototype.build = function() {
  var that = this;

  that.buildCore();

  that.builder.run(that._target);

  // response ender
  that.builder.use(function(handle) {
    handle('response', { affinity: 'sink' }, function(env, next) {
      if (env.response.body) {
        var body = env.response.body;
        if (typeof body === 'string') {
          env.response.setHeader('Content-Length', body ? body.length : 0);
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end(body);
        } else if (body instanceof Stream) {
          env.response.writeHead(env.response.statusCode, env.response.headers);
          body.pipe(env.response);
        } else if (typeof body === 'object') {
          body = new Buffer(JSON.stringify(body), 'utf-8');
          if (!env.response.getHeader('Content-Type')) {
            env.response.setHeader('Content-Type', 'application/json; charset=UTF-8');
          }
          env.response.setHeader('Content-Length', body ? body.length : 0);
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end(body.toString('utf-8'));
        }
      } else {
        var contentLength = env.response.getHeader('Content-Length');
        if (contentLength == '0') {
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end();
        } else if (env.target.response) {
          env.target.response.getBody(function(err, body) {
            env.response.setHeader('Content-Length', body ? body.length : 0);
            env.response.writeHead(env.response.statusCode, env.response.headers);
            env.response.end(body);
          });
        } else {
          env.response.setHeader('Content-Length', '0');
          env.response.writeHead(env.response.statusCode, env.response.headers);
          env.response.end();
        }
      }
    });
  });

  return that.builder.build();
};

Argo.prototype.call = function(env) {
  var app = this.build();
  return app.flow(env);
}

Argo.prototype.route = function(path, options, handlers) {
  if (typeof(options) === 'function') {
    handlers = options;
    options = {};
  }

  options.methods = options.methods || ['*'];
  if (!this._router[path]) {
    this._router[path] = {};
  }

  var that = this;
  options.methods.forEach(function(method) {
    that._router[path][method.toLowerCase()] = handlers;
  });

  that._routerKeys.push(path);
  that._routerKeys.sort(function(a, b) {
    if (a.length > b.length) {
      return -1;
    } else if (a.length < b.length) {
      return 1;
    }

    return 0;
  });

  return this;
};

var methods = {
  'get': 'GET',
  'post': 'POST',
  'put': 'PUT',
  'del': 'DELETE',
  'head': 'HEAD',
  'options': 'OPTIONS',
  'trace': 'TRACE'
};

Object.keys(methods).forEach(function(method) {
  Argo.prototype[method] = function(path, options, handlers) {
    if (typeof(options) === 'function') {
      handlers = options;
      options = {};
    }
    options.methods = [methods[method]];
    return this.route(path, options, handlers);
  };
});

Argo.prototype.map = function(path, options, handler) {
  if (typeof(options) === 'function') {
    handler = options;
    options = {};
  }

  options.methods = options.methods || ['*'];
  if (!this._router[path]) {
    this._router[path] = {};
  }

  function generateHandler(path, handler) {
    var argo = new Argo();
    handler(argo);

    var app = argo.embed();

    return function(addHandler) {
      addHandler('request', function mapHandler(env, next) {
        env.argo.frames = env.argo.frames || [];

        var frame = new Frame();
        frame.routed = env.argo._routed;
        frame.routedResponseHandler = env.argo._routedResponseHandler;

        env.argo._routed = false;
        env.argo._routedResponseHandler = null;

        if (env.request.url[env.request.url.length - 1] === '/') {
          env.request.url = env.request.url.substr(0, env.request.url.length - 1);
        }

        if (env.argo.frames.length) {
          pathLength = 0;
          env.argo.frames.forEach(function(frame) {
            pathLength += frame.routeUri.length;
          });
        }

        frame.routeUri = path || '/';

        env.request.url = env.request.url.substr(frame.routeUri.length);
        env.request.url = env.request.url || '/';

        // TODO: See if this can work in a response handler here.

        if (env.argo.oncomplete) {
          frame.oncomplete = env.argo.oncomplete;
        }

        env.argo.currentFrame = frame;
        env.argo.frames.push(frame);

        env.argo.oncomplete = function(env) {
          var frame = env.argo.frames.pop();

          env.argo._routed = frame.routed;
          env.argo._routedResponseHandler = frame.routedResponseHandler;
          env.request.url = frame.routeUri + env.request.url;
          env.argo.oncomplete = frame.oncomplete;

          next(env);
        };

        app.flow(env);
      });
    };
  };

  return this.route(path, options, generateHandler(path, handler));
};

Argo.prototype._pipeline = function(name) {
  return this.builder.pipelineMap[name];
};

Argo.prototype._addRouteHandlers = function(handlers) {
  return function add(name, opts, cb) {
    if (typeof opts === 'function') {
      cb = opts;
      opts = null;
    }

    if (name === 'request') {
      handlers.request = cb;
    } else if (name === 'response') {
      handlers.response = cb;
    }
  };
};

function RouteHandlers() {
  this.request = null;
  this.response = null;
}

Argo.prototype._routeRequestHandler = function(router) {
  var that = this;
  return function routeRequestHandler(env, next) {
    env.argo._routed = false;

    var search = env.request.url;

    var routerKey;
    if (search === '/' && that._router['/']) {
      routerKey = '/';
    } else {
      that._routerKeys.forEach(function(key) {
        if (!routerKey && key !== '*' && search.search(key) !== -1 && key !== '/') {
          routerKey = key;
        }
      });
    }

    if (!routerKey && that._router['*']) {
      routerKey = '*';
    }

    if (routerKey &&
        (!router[routerKey][env.request.method.toLowerCase()] &&
         !router[routerKey]['*'])) {
      env.response.statusCode = 405;
      next(env);
      return;
    }

    if (routerKey &&
        (router[routerKey][env.request.method.toLowerCase()] ||
         router[routerKey]['*'])) {
      env.argo._routed = true;

      var method = env.request.method.toLowerCase();
      var fn = router[routerKey][method] ? router[routerKey][method]
        : router[routerKey]['*'];

      var handlers = new RouteHandlers();
      fn(that._addRouteHandlers(handlers));

      env.argo._routedResponseHandler = handlers.response || null;

      if (handlers.request) {
        handlers.request(env, next);
      } else {
        next(env);
        return;
      }
    }

    if (!env.argo._routed) {
      next(env);
    }
  };
};

Argo.prototype._routeResponseHandler = function(router) {
  var that = this;
  return function routeResponseHandler(env, next) {
    if (!env.argo._routed) {
      if (env.response.statusCode !== 405) {
        env.response.statusCode = 404;
      }

      next(env);
      return;
    }

    if (env.argo._routedResponseHandler) {
      env.argo._routedResponseHandler(env, next);
      return;
    } else {
      next(env);
      return;
    }
  };
};

Argo.prototype._route = function(router, handle) {
  handle('request', this._routeRequestHandler(router));
  handle('response', { affinity: 'hoist' }, this._routeResponseHandler(router));
};

Argo.prototype._target = function(env, next) {
  if (env.response._headerSent || env.target.skip) {
    next(env);
    return;
  }
  var start = +Date.now();

  if (env.target && env.target.url) {
    var options = {};
    options.method = env.request.method || 'GET';

    // TODO: Make Agent configurable.
    options.agent = new env.argo._http.Agent();
    options.agent.maxSockets = 1024;

    var parsed = url.parse(env.target.url);
    options.hostname = parsed.hostname;
    options.port = parsed.port || 80;
    options.path = parsed.path;

    options.headers = env.request.headers;
    options.headers['Connection'] = 'keep-alive';
    options.headers['Host'] = options.hostname;

    if (parsed.auth) {
      options.auth = parsed.auth;
    }

    var req = env.argo._http.request(options, function(res) {
      for (var key in res.headers) {
        var headerName = res._rawHeaderNames[key] || key;
        env.response.setHeader(headerName, res.headers[key]);
      }

      env.target.response = res;

      if (next) {
        var duration = (+Date.now() - start);
        next(env);
      }
    });

    env.request.getBody(function(err, body) {
      if (body) {
        req.write(body);
      }

      req.end();
    });
  } else {
    next(env);
  }
};

module.exports = function(_http) { return new Argo(_http) };
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

/**
 * View interface used to populate an InsideOutBox object.
 *
 * All views must implement this interface (directly or via duck typing).
 */
top.InsideOutBoxView = {
    /**
     * Retrieves the parent object for a given child object.
     */
    getParentObject: function(child) {},

    /**
     * Retrieves a given child node.
     *
     * If both index and previousSibling are pased, the implementation
     * may assume that previousSibling will be the return for getChildObject
     * with index-1.
     */
    getChildObject: function(parent, index, previousSibling) {},

    /**
     * Renders the HTML representation of the object. Should return an HTML
     * object which will be displayed to the user.
     */
    createObjectBox: function(object, isRoot) {}
};

/**
 * Creates a tree based on objects provided by a separate "view" object.
 *
 * Construction uses an "inside-out" algorithm, meaning that the view's job is first
 * to tell us the ancestry of each object, and secondarily its descendants.
 */
top.InsideOutBox = function(view, box)
{
    this.view = view;
    this.box = box;

    this.rootObject = null;

    this.rootObjectBox = null;
    this.selectedObjectBox = null;
    this.highlightedObjectBox = null;

    this.onMouseDown = bind(this.onMouseDown, this);
    box.addEventListener("mousedown", this.onMouseDown, false);
};

InsideOutBox.prototype =
{
    destroy: function()
    {
        this.box.removeEventListener("mousedown", this.onMouseDown, false);
    },

    highlight: function(object)
    {
        var objectBox = this.createObjectBox(object);
        this.highlightObjectBox(objectBox);
        return objectBox;
    },

    openObject: function(object)
    {
        var firstChild = this.view.getChildObject(object, 0);
        if (firstChild)
            object = firstChild;

        var objectBox = this.createObjectBox(object);
        this.openObjectBox(objectBox);
        return objectBox;
    },

    openToObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        this.openObjectBox(objectBox);
        return objectBox;
    },

    select: function(object, makeBoxVisible, forceOpen, noScrollIntoView)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("insideOutBox.select object:", object);
        var objectBox = this.createObjectBox(object);
        this.selectObjectBox(objectBox, forceOpen);
        if (makeBoxVisible)
        {
            this.openObjectBox(objectBox);
            if (!noScrollIntoView)
                scrollIntoCenterView(objectBox);
        }
        return objectBox;
    },

    expandObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        if (objectBox)
            this.expandObjectBox(objectBox);
    },

    contractObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        if (objectBox)
            this.contractObjectBox(objectBox);
    },

    highlightObjectBox: function(objectBox)
    {
        if (this.highlightedObjectBox)
        {
            removeClass(this.highlightedObjectBox, "highlighted");

            var highlightedBox = this.getParentObjectBox(this.highlightedObjectBox);
            for (; highlightedBox; highlightedBox = this.getParentObjectBox(highlightedBox))
                removeClass(highlightedBox, "highlightOpen");
        }

        this.highlightedObjectBox = objectBox;

        if (objectBox)
        {
            setClass(objectBox, "highlighted");

            var highlightedBox = this.getParentObjectBox(objectBox);
            for (; highlightedBox; highlightedBox = this.getParentObjectBox(highlightedBox))
                setClass(highlightedBox, "highlightOpen");

           scrollIntoCenterView(objectBox);
        }
    },

    selectObjectBox: function(objectBox, forceOpen)
    {
        var isSelected = this.selectedObjectBox && objectBox == this.selectedObjectBox;
        if (!isSelected)
        {
            removeClass(this.selectedObjectBox, "selected");
            dispatch([Firebug.A11yModel], 'onObjectBoxUnselected', [this.selectedObjectBox]);
            this.selectedObjectBox = objectBox;

            if (objectBox)
            {
                setClass(objectBox, "selected");

                // Force it open the first time it is selected
                if (forceOpen)
                    this.toggleObjectBox(objectBox, true);
            }
        }
        dispatch([Firebug.A11yModel], 'onObjectBoxSelected', [objectBox]);
    },

    openObjectBox: function(objectBox)
    {
        if (objectBox)
        {
            // Set all of the node's ancestors to be permanently open
            var parentBox = this.getParentObjectBox(objectBox);
            var labelBox;
            for (; parentBox; parentBox = this.getParentObjectBox(parentBox))
            {
                setClass(parentBox, "open");
                labelBox = getChildByClass(parentBox.firstChild, 'nodeLabelBox');
                if (labelBox)
                    labelBox.setAttribute('aria-expanded', 'true')
            }
        }
    },

    expandObjectBox: function(objectBox)
    {
        var nodeChildBox = this.getChildObjectBox(objectBox);
        if (!nodeChildBox)
            return;

        if (!objectBox.populated)
        {
            var firstChild = this.view.getChildObject(objectBox.repObject, 0);
            this.populateChildBox(firstChild, nodeChildBox);
        }
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', 'true');
        setClass(objectBox, "open");
    },

    contractObjectBox: function(objectBox)
    {
        removeClass(objectBox, "open");
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', 'false');
    },

    toggleObjectBox: function(objectBox, forceOpen)
    {
        var isOpen = hasClass(objectBox, "open");
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', isOpen);
        if (!forceOpen && isOpen)
            this.contractObjectBox(objectBox);

        else if (!isOpen)
            this.expandObjectBox(objectBox);
    },

    getNextObjectBox: function(objectBox)
    {
        return findNext(objectBox, isVisibleTarget, false, this.box);
    },

    getPreviousObjectBox: function(objectBox)
    {
        return findPrevious(objectBox, isVisibleTarget, true, this.box);
    },

    /**
     * Creates all of the boxes for an object, its ancestors, and siblings.
     */
    createObjectBox: function(object)
    {
        if (!object)
            return null;

        if(object.nodeType == 3)
            object = object.parentNode;

        this.rootObject = this.getRootNode(object);

        // Get or create all of the boxes for the target and its ancestors
        var objectBox = this.createObjectBoxes(object, this.rootObject);

        if (FBTrace.DBG_HTML)
            FBTrace.sysout("\n----\ninsideOutBox.createObjectBox for object="+formatNode(object)+" got objectBox="+formatNode(objectBox), objectBox);
        if (!objectBox)
            return null;
        else if (object == this.rootObject)
            return objectBox;
        else
            return this.populateChildBox(object, objectBox.parentNode);
    },

    /**
     * Creates all of the boxes for an object, its ancestors, and siblings up to a root.
     */
    createObjectBoxes: function(object, rootObject)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("\n----\ninsideOutBox.createObjectBoxes("+formatNode(object)+", "+formatNode(rootObject)+")\n");
        if (!object)
            return null;

        if (object == rootObject)
        {
            if (!this.rootObjectBox || this.rootObjectBox.repObject != rootObject)
            {
                if (this.rootObjectBox)
                {
                    try {
                        this.box.removeChild(this.rootObjectBox);
                    } catch (exc) {
                        if (FBTrace.DBG_HTML)
                            FBTrace.sysout(" this.box.removeChild(this.rootObjectBox) FAILS "+this.box+" must not contain "+this.rootObjectBox+"\n");
                    }
                }

                this.highlightedObjectBox = null;
                this.selectedObjectBox = null;
                this.rootObjectBox = this.view.createObjectBox(object, true);
                this.box.appendChild(this.rootObjectBox);
            }
            if (FBTrace.DBG_HTML)
            {
                FBTrace.sysout("insideOutBox.createObjectBoxes("+formatNode(object)+","+formatNode(rootObject)+") rootObjectBox: "
                                            +this.rootObjectBox, object);
            }
            return this.rootObjectBox;
        }
        else
        {
            var parentNode = this.view.getParentObject(object);

            if (FBTrace.DBG_HTML)
            {
                FBTrace.sysout("insideOutBox.createObjectBoxes getObjectPath(object) ", getObjectPath(object, this.view))
                FBTrace.sysout("insideOutBox.createObjectBoxes view.getParentObject("+formatNode(object)+")=parentNode: "+formatNode(parentNode), parentNode);
            }

            var parentObjectBox = this.createObjectBoxes(parentNode, rootObject);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes createObjectBoxes("+formatNode(parentNode)+","+formatNode(rootObject)+"):parentObjectBox: "+formatNode(parentObjectBox), parentObjectBox);
            if (!parentObjectBox)
                return null;

            var parentChildBox = this.getChildObjectBox(parentObjectBox);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes getChildObjectBox("+formatNode(parentObjectBox)+")= parentChildBox: "+formatNode(parentChildBox)+"\n");
            if (!parentChildBox)
                return null;

            var childObjectBox = this.findChildObjectBox(parentChildBox, object);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes findChildObjectBox("+formatNode(parentChildBox)+","+formatNode(object)+"): childObjectBox: "+formatNode(childObjectBox), childObjectBox);
            return childObjectBox
                ? childObjectBox
                : this.populateChildBox(object, parentChildBox);
        }
    },

    findObjectBox: function(object)
    {
        if (!object)
            return null;

        if (object == this.rootObject)
            return this.rootObjectBox;
        else
        {
            var parentNode = this.view.getParentObject(object);
            var parentObjectBox = this.findObjectBox(parentNode);
            if (!parentObjectBox)
                return null;

            var parentChildBox = this.getChildObjectBox(parentObjectBox);
            if (!parentChildBox)
                return null;

            return this.findChildObjectBox(parentChildBox, object);
        }
    },

    appendChildBox: function(parentNodeBox, repObject)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            return objectBox;

        objectBox = this.view.createObjectBox(repObject);
        if (objectBox)
        {
            var childBox = this.getChildObjectBox(parentNodeBox);
            childBox.appendChild(objectBox);
        }
        return objectBox;
    },

    insertChildBoxBefore: function(parentNodeBox, repObject, nextSibling)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            return objectBox;

        objectBox = this.view.createObjectBox(repObject);
        if (objectBox)
        {
            var siblingBox = this.findChildObjectBox(childBox, nextSibling);
            childBox.insertBefore(objectBox, siblingBox);
        }
        return objectBox;
    },

    removeChildBox: function(parentNodeBox, repObject)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            childBox.removeChild(objectBox);
    },

    populateChildBox: function(repObject, nodeChildBox)  // We want all children of the parent of repObject.
    {
        if (!repObject)
            return null;

        var parentObjectBox = nodeChildBox.parentNode;
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("+++insideOutBox.populateChildBox("+(repObject.localName?repObject.localName:repObject)+") parentObjectBox.populated "+parentObjectBox.populated+"\n");
        if (parentObjectBox.populated)
            return this.findChildObjectBox(nodeChildBox, repObject);

        var lastSiblingBox = this.getChildObjectBox(nodeChildBox);
        var siblingBox = nodeChildBox.firstChild;
        var targetBox = null;

        var view = this.view;

        var targetSibling = null;
        var parentNode = view.getParentObject(repObject);
        for (var i = 0; 1; ++i)
        {
            targetSibling = view.getChildObject(parentNode, i, targetSibling);
            if (!targetSibling)
                break;

            // Check if we need to start appending, or continue to insert before
            if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
                lastSiblingBox = null;

            if (!siblingBox || siblingBox.repObject != targetSibling)
            {
                var newBox = view.createObjectBox(targetSibling);
                if (newBox)
                {
                    if (lastSiblingBox)
                        nodeChildBox.insertBefore(newBox, lastSiblingBox);
                    else
                        nodeChildBox.appendChild(newBox);
                }

                siblingBox = newBox;
            }

            if (targetSibling == repObject)
                targetBox = siblingBox;

            if (siblingBox && siblingBox.repObject == targetSibling)
                siblingBox = siblingBox.nextSibling;
        }

        if (targetBox)
            parentObjectBox.populated = true;
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("---insideOutBox.populateChildBox("+(repObject.localName?repObject.localName:repObject)+") targetBox "+targetBox+"\n");

        return targetBox;
    },

    getParentObjectBox: function(objectBox)
    {
        var parent = objectBox.parentNode ? objectBox.parentNode.parentNode : null;
        return parent && parent.repObject ? parent : null;
    },

    getChildObjectBox: function(objectBox)
    {
        return getChildByClass(objectBox, "nodeChildBox");
    },

    findChildObjectBox: function(parentNodeBox, repObject)
    {
        for (var childBox = parentNodeBox.firstChild; childBox; childBox = childBox.nextSibling)
        {
            if (FBTrace.DBG_HTML)
                FBTrace.sysout(
                    "insideOutBox.findChildObjectBox "
                    +(childBox.repObject == repObject?"match ":"no match ")
                    +" childBox.repObject: " + (childBox.repObject && (childBox.repObject.localName || childBox.repObject))
                    +" repObject: " +(repObject && (repObject.localName || repObject))+"\n", childBox);
            if (childBox.repObject == repObject)
                return childBox;
        }
    },

    getRootNode: function(node)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("insideOutBox.getRootNode for ");
        while (1)
        {
            if (FBTrace.DBG_HTML)
                FBTrace.sysout(node.localName+" < ");
            var parentNode = this.view.getParentObject(node);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout((parentNode?" (parent="+parentNode.localName+")":" (null parentNode)"+"\n"));

            if (!parentNode)
                return node;
            else
                node = parentNode;
        }
        return null;
    },

    // ********************************************************************************************

    onMouseDown: function(event)
    {
        var hitTwisty = false;
        for (var child = event.target; child; child = child.parentNode)
        {
            if (hasClass(child, "twisty"))
                hitTwisty = true;
            else if (child.repObject)
            {
                if (hitTwisty)
                    this.toggleObjectBox(child);
                break;
            }
        }
    }
};

// ************************************************************************************************
// Local Helpers

function isVisibleTarget(node)
{
    if (node.repObject && node.repObject.nodeType == 1)
    {
        for (var parent = node.parentNode; parent; parent = parent.parentNode)
        {
            if (hasClass(parent, "nodeChildBox")
                && !hasClass(parent.parentNode, "open")
                && !hasClass(parent.parentNode, "highlightOpen"))
                return false;
        }
        return true;
    }
}

function formatNode(object)
{
    if (object)
        return (object.localName ? object.localName : object);
    else
        return "(null object)";
}

function getObjectPath(element, aView)
{
    var path = [];
    for (; element; element = aView.getParentObject(element))
        path.push(element);

    return path;
}

}});
<commit_msg>IOBox logging and constant updates<commit_after>/* See license.txt for terms of usage */

FBL.ns(function() { with (FBL) {

/**
 * View interface used to populate an InsideOutBox object.
 *
 * All views must implement this interface (directly or via duck typing).
 */
top.InsideOutBoxView = {
    /**
     * Retrieves the parent object for a given child object.
     */
    getParentObject: function(child) {},

    /**
     * Retrieves a given child node.
     *
     * If both index and previousSibling are pased, the implementation
     * may assume that previousSibling will be the return for getChildObject
     * with index-1.
     */
    getChildObject: function(parent, index, previousSibling) {},

    /**
     * Renders the HTML representation of the object. Should return an HTML
     * object which will be displayed to the user.
     */
    createObjectBox: function(object, isRoot) {}
};

/**
 * Creates a tree based on objects provided by a separate "view" object.
 *
 * Construction uses an "inside-out" algorithm, meaning that the view's job is first
 * to tell us the ancestry of each object, and secondarily its descendants.
 */
top.InsideOutBox = function(view, box)
{
    this.view = view;
    this.box = box;

    this.rootObject = null;

    this.rootObjectBox = null;
    this.selectedObjectBox = null;
    this.highlightedObjectBox = null;

    this.onMouseDown = bind(this.onMouseDown, this);
    box.addEventListener("mousedown", this.onMouseDown, false);
};

InsideOutBox.prototype =
{
    destroy: function()
    {
        this.box.removeEventListener("mousedown", this.onMouseDown, false);
    },

    highlight: function(object)
    {
        var objectBox = this.createObjectBox(object);
        this.highlightObjectBox(objectBox);
        return objectBox;
    },

    openObject: function(object)
    {
        var firstChild = this.view.getChildObject(object, 0);
        if (firstChild)
            object = firstChild;

        var objectBox = this.createObjectBox(object);
        this.openObjectBox(objectBox);
        return objectBox;
    },

    openToObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        this.openObjectBox(objectBox);
        return objectBox;
    },

    select: function(object, makeBoxVisible, forceOpen, noScrollIntoView)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("insideOutBox.select object:", object);
        var objectBox = this.createObjectBox(object);
        this.selectObjectBox(objectBox, forceOpen);
        if (makeBoxVisible)
        {
            this.openObjectBox(objectBox);
            if (!noScrollIntoView)
                scrollIntoCenterView(objectBox);
        }
        return objectBox;
    },

    expandObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        if (objectBox)
            this.expandObjectBox(objectBox);
    },

    contractObject: function(object)
    {
        var objectBox = this.createObjectBox(object);
        if (objectBox)
            this.contractObjectBox(objectBox);
    },

    highlightObjectBox: function(objectBox)
    {
        if (this.highlightedObjectBox)
        {
            removeClass(this.highlightedObjectBox, "highlighted");

            var highlightedBox = this.getParentObjectBox(this.highlightedObjectBox);
            for (; highlightedBox; highlightedBox = this.getParentObjectBox(highlightedBox))
                removeClass(highlightedBox, "highlightOpen");
        }

        this.highlightedObjectBox = objectBox;

        if (objectBox)
        {
            setClass(objectBox, "highlighted");

            var highlightedBox = this.getParentObjectBox(objectBox);
            for (; highlightedBox; highlightedBox = this.getParentObjectBox(highlightedBox))
                setClass(highlightedBox, "highlightOpen");

           scrollIntoCenterView(objectBox);
        }
    },

    selectObjectBox: function(objectBox, forceOpen)
    {
        var isSelected = this.selectedObjectBox && objectBox == this.selectedObjectBox;
        if (!isSelected)
        {
            removeClass(this.selectedObjectBox, "selected");
            dispatch([Firebug.A11yModel], 'onObjectBoxUnselected', [this.selectedObjectBox]);
            this.selectedObjectBox = objectBox;

            if (objectBox)
            {
                setClass(objectBox, "selected");

                // Force it open the first time it is selected
                if (forceOpen)
                    this.toggleObjectBox(objectBox, true);
            }
        }
        dispatch([Firebug.A11yModel], 'onObjectBoxSelected', [objectBox]);
    },

    openObjectBox: function(objectBox)
    {
        if (objectBox)
        {
            // Set all of the node's ancestors to be permanently open
            var parentBox = this.getParentObjectBox(objectBox);
            var labelBox;
            for (; parentBox; parentBox = this.getParentObjectBox(parentBox))
            {
                setClass(parentBox, "open");
                labelBox = getChildByClass(parentBox.firstChild, 'nodeLabelBox');
                if (labelBox)
                    labelBox.setAttribute('aria-expanded', 'true')
            }
        }
    },

    expandObjectBox: function(objectBox)
    {
        var nodeChildBox = this.getChildObjectBox(objectBox);
        if (!nodeChildBox)
            return;

        if (!objectBox.populated)
        {
            var firstChild = this.view.getChildObject(objectBox.repObject, 0);
            this.populateChildBox(firstChild, nodeChildBox);
        }
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', 'true');
        setClass(objectBox, "open");
    },

    contractObjectBox: function(objectBox)
    {
        removeClass(objectBox, "open");
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', 'false');
    },

    toggleObjectBox: function(objectBox, forceOpen)
    {
        var isOpen = hasClass(objectBox, "open");
        var labelBox = getChildByClass(objectBox.firstChild, 'nodeLabelBox');
        if (labelBox)
            labelBox.setAttribute('aria-expanded', isOpen);
        if (!forceOpen && isOpen)
            this.contractObjectBox(objectBox);

        else if (!isOpen)
            this.expandObjectBox(objectBox);
    },

    getNextObjectBox: function(objectBox)
    {
        return findNext(objectBox, isVisibleTarget, false, this.box);
    },

    getPreviousObjectBox: function(objectBox)
    {
        return findPrevious(objectBox, isVisibleTarget, true, this.box);
    },

    /**
     * Creates all of the boxes for an object, its ancestors, and siblings.
     */
    createObjectBox: function(object)
    {
        if (!object)
            return null;

        if(object.nodeType == Node.TEXT_NODE)
            object = object.parentNode;

        this.rootObject = this.getRootNode(object);

        // Get or create all of the boxes for the target and its ancestors
        var objectBox = this.createObjectBoxes(object, this.rootObject);

        if (FBTrace.DBG_HTML)
            FBTrace.sysout("\n----\ninsideOutBox.createObjectBox for object="+formatNode(object)+" got objectBox="+formatNode(objectBox), objectBox);
        if (!objectBox)
            return null;
        else if (object == this.rootObject)
            return objectBox;
        else
            return this.populateChildBox(object, objectBox.parentNode);
    },

    /**
     * Creates all of the boxes for an object, its ancestors, and siblings up to a root.
     */
    createObjectBoxes: function(object, rootObject)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("\n----\ninsideOutBox.createObjectBoxes("+formatNode(object)+", "+formatNode(rootObject)+")\n");
        if (!object)
            return null;

        if (object == rootObject)
        {
            if (!this.rootObjectBox || this.rootObjectBox.repObject != rootObject)
            {
                if (this.rootObjectBox)
                {
                    try {
                        this.box.removeChild(this.rootObjectBox);
                    } catch (exc) {
                        if (FBTrace.DBG_HTML)
                            FBTrace.sysout(" this.box.removeChild(this.rootObjectBox) FAILS "+this.box+" must not contain "+this.rootObjectBox+"\n");
                    }
                }

                this.highlightedObjectBox = null;
                this.selectedObjectBox = null;
                this.rootObjectBox = this.view.createObjectBox(object, true);
                this.box.appendChild(this.rootObjectBox);
            }
            if (FBTrace.DBG_HTML)
            {
                FBTrace.sysout("insideOutBox.createObjectBoxes("+formatNode(object)+","+formatNode(rootObject)+") rootObjectBox: "
                                            +this.rootObjectBox, object);
            }
            return this.rootObjectBox;
        }
        else
        {
            var parentNode = this.view.getParentObject(object);

            if (FBTrace.DBG_HTML)
            {
                FBTrace.sysout("insideOutBox.createObjectBoxes getObjectPath(object) ", getObjectPath(object, this.view))
                FBTrace.sysout("insideOutBox.createObjectBoxes view.getParentObject("+formatNode(object)+")=parentNode: "+formatNode(parentNode), parentNode);
            }

            var parentObjectBox = this.createObjectBoxes(parentNode, rootObject);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes createObjectBoxes("+formatNode(parentNode)+","+formatNode(rootObject)+"):parentObjectBox: "+formatNode(parentObjectBox), parentObjectBox);
            if (!parentObjectBox)
                return null;

            var parentChildBox = this.getChildObjectBox(parentObjectBox);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes getChildObjectBox("+formatNode(parentObjectBox)+")= parentChildBox: "+formatNode(parentChildBox)+"\n");
            if (!parentChildBox)
                return null;

            var childObjectBox = this.findChildObjectBox(parentChildBox, object);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout("insideOutBox.createObjectBoxes findChildObjectBox("+formatNode(parentChildBox)+","+formatNode(object)+"): childObjectBox: "+formatNode(childObjectBox), childObjectBox);
            return childObjectBox
                ? childObjectBox
                : this.populateChildBox(object, parentChildBox);
        }
    },

    findObjectBox: function(object)
    {
        if (!object)
            return null;

        if (object == this.rootObject)
            return this.rootObjectBox;
        else
        {
            var parentNode = this.view.getParentObject(object);
            var parentObjectBox = this.findObjectBox(parentNode);
            if (!parentObjectBox)
                return null;

            var parentChildBox = this.getChildObjectBox(parentObjectBox);
            if (!parentChildBox)
                return null;

            return this.findChildObjectBox(parentChildBox, object);
        }
    },

    appendChildBox: function(parentNodeBox, repObject)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            return objectBox;

        objectBox = this.view.createObjectBox(repObject);
        if (objectBox)
        {
            var childBox = this.getChildObjectBox(parentNodeBox);
            childBox.appendChild(objectBox);
        }
        return objectBox;
    },

    insertChildBoxBefore: function(parentNodeBox, repObject, nextSibling)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            return objectBox;

        objectBox = this.view.createObjectBox(repObject);
        if (objectBox)
        {
            var siblingBox = this.findChildObjectBox(childBox, nextSibling);
            childBox.insertBefore(objectBox, siblingBox);
        }
        return objectBox;
    },

    removeChildBox: function(parentNodeBox, repObject)
    {
        var childBox = this.getChildObjectBox(parentNodeBox);
        var objectBox = this.findChildObjectBox(childBox, repObject);
        if (objectBox)
            childBox.removeChild(objectBox);
    },

    populateChildBox: function(repObject, nodeChildBox)  // We want all children of the parent of repObject.
    {
        if (!repObject)
            return null;

        var parentObjectBox = nodeChildBox.parentNode;
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("+++insideOutBox.populateChildBox("+(repObject.localName?repObject.localName:repObject)+") parentObjectBox.populated "+parentObjectBox.populated+"\n");
        if (parentObjectBox.populated)
            return this.findChildObjectBox(nodeChildBox, repObject);

        var lastSiblingBox = this.getChildObjectBox(nodeChildBox);
        var siblingBox = nodeChildBox.firstChild;
        var targetBox = null;

        var view = this.view;

        var targetSibling = null;
        var parentNode = view.getParentObject(repObject);
        for (var i = 0; 1; ++i)
        {
            targetSibling = view.getChildObject(parentNode, i, targetSibling);
            if (!targetSibling)
                break;

            // Check if we need to start appending, or continue to insert before
            if (lastSiblingBox && lastSiblingBox.repObject == targetSibling)
                lastSiblingBox = null;

            if (!siblingBox || siblingBox.repObject != targetSibling)
            {
                var newBox = view.createObjectBox(targetSibling);
                if (newBox)
                {
                    if (lastSiblingBox)
                        nodeChildBox.insertBefore(newBox, lastSiblingBox);
                    else
                        nodeChildBox.appendChild(newBox);
                }

                siblingBox = newBox;
            }

            if (targetSibling == repObject)
                targetBox = siblingBox;

            if (siblingBox && siblingBox.repObject == targetSibling)
                siblingBox = siblingBox.nextSibling;
        }

        if (targetBox)
            parentObjectBox.populated = true;
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("---insideOutBox.populateChildBox("+(repObject.localName?repObject.localName:repObject)+") targetBox "+targetBox+"\n");

        return targetBox;
    },

    getParentObjectBox: function(objectBox)
    {
        var parent = objectBox.parentNode ? objectBox.parentNode.parentNode : null;
        return parent && parent.repObject ? parent : null;
    },

    getChildObjectBox: function(objectBox)
    {
        return getChildByClass(objectBox, "nodeChildBox");
    },

    findChildObjectBox: function(parentNodeBox, repObject)
    {
        for (var childBox = parentNodeBox.firstChild; childBox; childBox = childBox.nextSibling)
        {
            if (FBTrace.DBG_HTML)
                FBTrace.sysout(
                    "insideOutBox.findChildObjectBox "
                    +(childBox.repObject == repObject?"match ":"no match ")
                    +" childBox.repObject: " + (childBox.repObject && (childBox.repObject.localName || childBox.repObject))
                    +" repObject: " +(repObject && (repObject.localName || repObject))+"\n", childBox);
            if (childBox.repObject == repObject)
                return childBox;
        }
    },

    getRootNode: function(node)
    {
        if (FBTrace.DBG_HTML)
            FBTrace.sysout("insideOutBox.getRootNode for ", node);
        while (1)
        {
            if (FBTrace.DBG_HTML)
                FBTrace.sysout(node.localName+" < ", node);
            var parentNode = this.view.getParentObject(node);
            if (FBTrace.DBG_HTML)
                FBTrace.sysout((parentNode?" (parent="+parentNode.localName+")":" (null parentNode)"+"\n"), parentNode);

            if (!parentNode)
                return node;
            else
                node = parentNode;
        }
        return null;
    },

    // ********************************************************************************************

    onMouseDown: function(event)
    {
        var hitTwisty = false;
        for (var child = event.target; child; child = child.parentNode)
        {
            if (hasClass(child, "twisty"))
                hitTwisty = true;
            else if (child.repObject)
            {
                if (hitTwisty)
                    this.toggleObjectBox(child);
                break;
            }
        }
    }
};

// ************************************************************************************************
// Local Helpers

function isVisibleTarget(node)
{
    if (node.repObject && node.repObject.nodeType == Node.ELEMENT_NODE)
    {
        for (var parent = node.parentNode; parent; parent = parent.parentNode)
        {
            if (hasClass(parent, "nodeChildBox")
                && !hasClass(parent.parentNode, "open")
                && !hasClass(parent.parentNode, "highlightOpen"))
                return false;
        }
        return true;
    }
}

function formatNode(object)
{
    if (object)
        return (object.localName ? object.localName : object);
    else
        return "(null object)";
}

function getObjectPath(element, aView)
{
    var path = [];
    for (; element; element = aView.getParentObject(element))
        path.push(element);

    return path;
}

}});
<|endoftext|>


<commit_before>// FIXME draw drag box
// FIXME works for View2D only

goog.provide('ol.interaction.DragZoom');

goog.require('goog.asserts');
goog.require('ol.Size');
goog.require('ol.View2D');
goog.require('ol.control.DragBox');
goog.require('ol.extent');
goog.require('ol.interaction.ConditionType');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.condition');

/**
 * @define {number} Hysterisis pixels.
 */
ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS = 8;

/**
 * @const {number}
 */
ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED =
    ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS *
    ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS;

/**
 * @constructor
 * @extends {ol.interaction.Drag}
 * @param {ol.interaction.DragZoomOptions=} opt_options Options.
 */
ol.interaction.DragZoom = function(opt_options) {

  goog.base(this);

  var options = goog.isDef(opt_options) ? opt_options : {};

  /**
   * @private
   * @type {ol.interaction.ConditionType}
   */
  this.condition_ = goog.isDef(options.condition) ?
      options.condition : ol.interaction.condition.shiftKeyOnly;

  /**
   * @type {ol.control.DragBox}
   * @private
   */
  this.dragBox_ = null;

};
goog.inherits(ol.interaction.DragZoom, ol.interaction.Drag);

/**
 * @inheritDoc
 */
ol.interaction.DragZoom.prototype.handleDragEnd =
    function(mapBrowserEvent) {
  this.dragBox_.setMap(null);
  this.dragBox_ = null;
  if (this.deltaX * this.deltaX + this.deltaY * this.deltaY >=
      ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED) {
    var map = mapBrowserEvent.map;
    var extent = ol.extent.boundingExtent(
        [this.startCoordinate, mapBrowserEvent.getCoordinate()]);
    map.withFrozenRendering(function() {
      // FIXME works for View2D only
      var view = map.getView();
      goog.asserts.assertInstanceof(view, ol.View2D);
      var mapSize = /** @type {ol.Size} */ (map.getSize());
      view.fitExtent(extent, mapSize);
      // FIXME we should preserve rotation
      view.setRotation(0);
    });
  }
};

/**
 * @inheritDoc
 */
ol.interaction.DragZoom.prototype.handleDragStart =
    function(mapBrowserEvent) {
  var browserEvent = mapBrowserEvent.browserEvent;
  if (browserEvent.isMouseActionButton() && this.condition_(browserEvent)) {
    this.dragBox_ = new ol.control.DragBox({
      map: mapBrowserEvent.map,
      startCoordinate: this.startCoordinate
    });
    return true;
  } else {
    return false;
  }
};
<commit_msg>Call setMap instead of passing map to control<commit_after>// FIXME draw drag box
// FIXME works for View2D only

goog.provide('ol.interaction.DragZoom');

goog.require('goog.asserts');
goog.require('ol.Size');
goog.require('ol.View2D');
goog.require('ol.control.DragBox');
goog.require('ol.extent');
goog.require('ol.interaction.ConditionType');
goog.require('ol.interaction.Drag');
goog.require('ol.interaction.condition');

/**
 * @define {number} Hysterisis pixels.
 */
ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS = 8;

/**
 * @const {number}
 */
ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED =
    ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS *
    ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS;

/**
 * @constructor
 * @extends {ol.interaction.Drag}
 * @param {ol.interaction.DragZoomOptions=} opt_options Options.
 */
ol.interaction.DragZoom = function(opt_options) {

  goog.base(this);

  var options = goog.isDef(opt_options) ? opt_options : {};

  /**
   * @private
   * @type {ol.interaction.ConditionType}
   */
  this.condition_ = goog.isDef(options.condition) ?
      options.condition : ol.interaction.condition.shiftKeyOnly;

  /**
   * @type {ol.control.DragBox}
   * @private
   */
  this.dragBox_ = null;

};
goog.inherits(ol.interaction.DragZoom, ol.interaction.Drag);

/**
 * @inheritDoc
 */
ol.interaction.DragZoom.prototype.handleDragEnd =
    function(mapBrowserEvent) {
  this.dragBox_.setMap(null);
  this.dragBox_ = null;
  if (this.deltaX * this.deltaX + this.deltaY * this.deltaY >=
      ol.SHIFT_DRAG_ZOOM_HYSTERESIS_PIXELS_SQUARED) {
    var map = mapBrowserEvent.map;
    var extent = ol.extent.boundingExtent(
        [this.startCoordinate, mapBrowserEvent.getCoordinate()]);
    map.withFrozenRendering(function() {
      // FIXME works for View2D only
      var view = map.getView();
      goog.asserts.assertInstanceof(view, ol.View2D);
      var mapSize = /** @type {ol.Size} */ (map.getSize());
      view.fitExtent(extent, mapSize);
      // FIXME we should preserve rotation
      view.setRotation(0);
    });
  }
};

/**
 * @inheritDoc
 */
ol.interaction.DragZoom.prototype.handleDragStart =
    function(mapBrowserEvent) {
  var browserEvent = mapBrowserEvent.browserEvent;
  if (browserEvent.isMouseActionButton() && this.condition_(browserEvent)) {
    this.dragBox_ = new ol.control.DragBox({
      startCoordinate: this.startCoordinate
    });
    this.dragBox_.setMap(mapBrowserEvent.map);
    return true;
  } else {
    return false;
  }
};
<|endoftext|>


<commit_before>'use strict';

var taskNode = require('./taskNode.js');
var Task = require('./task.js');

var root = null;

var init = function() {
  if (!root) {
    root = taskNode.createRoot();
  }
};
module.exports.init = init;

var findNode = function(name, nodeToSearch) {
  if (!nodeToSearch) {
    nodeToSearch = root;
  }
  var result = null;
  root.walk(function(node, parent, depth) {
    if (!result && name === node.name) {
      result = node;
    }
  });
  return result;
};
module.exports.findNode = findNode;

var getTask = function(names, parent) {
  if (!parent) {
    parent = root;
  }
  var fn = function(names, parent) {
    if (names.length === 0) {
      return parent;
    } else {
      var node = null;
      for (var index in parent.children) {
        var child = parent.children[index];
        if (!node && child.name === names[0]) {
          node = child;
        }
      }
      if (!node) {
        return null;
      } else {
        return fn(names.slice(1), node);
      }
    }
  };
  return fn(names, parent);
};
module.exports.getTask = getTask;

var wrapTask = function(name, cmd) {
  var fn = function() {
    var task = new Task(name, cmd);
    task.on('error', function(err) {
      console.log(err);
      throw err;
    });
    task.run();
  };
  return fn;
};

var registerTask = function(names, task, sortIndex) {
  if (typeof names === 'string') {
    names = [names];
  }
  if (!sortIndex) {
    sortIndex = 0;
  }
  var fn = function(names, parent) {
    var name = names[0];
    if (names.length === 1) {
      parent.addChild(name, wrapTask(name, task), sortIndex);
    } else {
      var node = findNode(name, parent);
      if (!node) {
        node = parent.addChild(name, null, sortIndex);
      }
      fn(names.slice(1), node);
    }
  };
  fn(names, root);
};
module.exports.registerTask = registerTask;

var walk = function(node, callback) {
  if (typeof node === 'function' && !callback) {
    callback = node;
    node = root;
  }
  node.walk(callback);
};
module.exports.walk = walk;

var prettyPrint = function(node) {
  if (!node) {
    node = root;
  }
  var acc = '';
  var makeSpaces = function(depth) {
    var length = (depth - 1) * 4;
    return new Array(length + 1).join(' ');
  };
  node.walk(function(node, parent, depth) {
    if (depth > 0) {
      //var parentInfo = parent ? '(' + parent.name + ')' : '';
      var parentInfo = '';
      var prefix = node.task ? '|--> ' : '';
      acc += makeSpaces(depth) + prefix + node.name + parentInfo + '\n';
    }
  });
  console.log(acc);
};
module.exports.prettyPrint = prettyPrint;
<commit_msg>making a commit teehee<commit_after>'use strict';

var taskNode = require('./taskNode.js');
var Task = require('./task.js');

var root = null;

var init = function() {
  if (!root) {
    root = taskNode.createRoot();
  }
};
module.exports.init = init;

var findNode = function(name, nodeToSearch) {
  if (!nodeToSearch) {
    nodeToSearch = root;
  }
  var result = null;
  root.walk(function(node, parent, depth) {
    if (!result && name === node.name) {
      result = node;
    }
  });
  return result;
};
module.exports.findNode = findNode;

var getTask = function(names, parent) {
  if (!parent) {
    parent = root;
  }
  var fn = function(names, parent) {
    if (names.length === 0) {
      return parent;
    } else {
      var node = null;
      for (var index in parent.children) {
        var child = parent.children[index];
        if (!node && child.name === names[0]) {
          node = child;
        }
      }
      if (!node) {
        return null;
      } else {
        return fn(names.slice(1), node);
      }
    }
  };
  return fn(names, parent);
};
module.exports.getTask = getTask;

var wrapTask = function(name, cmd) {
  var fn = function(callback) {
    var task = new Task(name, cmd);
    task.run(callback);
  };
  return fn;
};

var registerTask = function(names, task, sortIndex) {
  if (typeof names === 'string') {
    names = [names];
  }
  if (!sortIndex) {
    sortIndex = 0;
  }
  var fn = function(names, parent) {
    var name = names[0];
    if (names.length === 1) {
      parent.addChild(name, wrapTask(name, task), sortIndex);
    } else {
      var node = findNode(name, parent);
      if (!node) {
        node = parent.addChild(name, null, sortIndex);
      }
      fn(names.slice(1), node);
    }
  };
  fn(names, root);
};
module.exports.registerTask = registerTask;

var walk = function(node, callback) {
  if (typeof node === 'function' && !callback) {
    callback = node;
    node = root;
  }
  node.walk(callback);
};
module.exports.walk = walk;

var prettyPrint = function(node) {
  if (!node) {
    node = root;
  }
  var acc = '';
  var makeSpaces = function(depth) {
    var length = (depth - 1) * 4;
    return new Array(length + 1).join(' ');
  };
  node.walk(function(node, parent, depth) {
    if (depth > 0) {
      //var parentInfo = parent ? '(' + parent.name + ')' : '';
      var parentInfo = '';
      var prefix = node.task ? '|--> ' : '';
      acc += makeSpaces(depth) + prefix + node.name + parentInfo + '\n';
    }
  });
  console.log(acc);
};
module.exports.prettyPrint = prettyPrint;
<|endoftext|>


<commit_before>'use strict';

let util = require('util'),
    Promise = require('bluebird'),
    _ = require('underscore'),
    qidx = require('quadtile-index'),
    checkType = require('@kartotherian/input-validator'),
    Err = require('@kartotherian/err');

let core;

module.exports = function tiles(cor, router) {
    core = cor;

    // get tile
    router.get('/:src(' + core.Sources.sourceIdReStr + ')/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w]+)', requestHandler);
    router.get('/:src(' + core.Sources.sourceIdReStr + ')/:z(\\d+)/:x(\\d+)/:y(\\d+)@:scale([\\.\\d]+)x.:format([\\w]+)', requestHandler);
};

/**
 * Web server (express) route handler to get requested tile
 * @param req request object
 * @param res response object
 * @param next will be called if request is not handled
 */
function requestHandler(req, res, next) {

    let source,
        params = req && req.params,
        start = Date.now();

    return Promise.try(() => {
        source = core.getPublicSource(params.src);

        if (!_.contains(source.formats, params.format)) {
            throw new Err('Format %s is not known', params.format).metrics('err.req.format');
        }

        params.z = core.validateZoom(params.z, source);
        params.scale = core.validateScale(params.scale, source);

        params.x = checkType.strToInt(params.x);
        params.y = checkType.strToInt(params.y);
        if (!qidx.isValidCoordinate(params.x, params.z) || !qidx.isValidCoordinate(params.y, params.z)) {
            throw new Err('x,y coordinates are not valid, or not allowed for this zoom').metrics('err.req.coords');
        }

        let opts;
        if (params.format !== 'pbf') {
            opts = {format: params.format};
            if (params.scale) {
                opts.scale = params.scale;
            }
        }

        return core.getTitleWithParamsAsync(source.getHandler(), params.z, params.x, params.y, opts);

    }).spread((data, dataHeaders) => {
        core.setResponseHeaders(res, source, dataHeaders);

        if (params.format === 'json') {
            // Allow JSON to be shortened to simplify debugging
            res.json(filterJson(req.query, data));
        } else {
            res.send(data);
        }

        let mx = util.format('req.%s.%s.%s', params.src, params.z, params.format);
        if (params.scale) {
            // replace '.' with ',' -- otherwise grafana treats it as a divider
            mx += '.' + (params.scale.toString().replace('.', ','));
        }
        core.metrics.endTiming(mx, start);
    }).catch(err => core.reportRequestError(err, res)).catch(next);
}

function filterJson(query, data) {
    if ('summary' in query) {
        data = _(data).reduce((memo, layer) => {
            memo[layer.name] = {
                features: layer.features.length,
                jsonsize: JSON.stringify(layer).length
            };
            return memo;
        }, {});
    } else if ('nogeo' in query) {
        // Recursively remove all "geometry" fields, replacing them with geometry's size
        let filter = (val, key) => {
            if (key === 'geometry') {
                return val.length;
            } else if (_.isArray(val)) {
                return _.map(val, filter);
            } else if (_.isObject(val)) {
                _.each(val, (v, k) => {
                    val[k] = filter(v, k);
                });
            }
            return val;
        };
        data = _.map(data, filter);
    }
    return data;
}
<commit_msg>Force 8-bit PNGs for tile requests<commit_after>'use strict';

let util = require('util'),
    Promise = require('bluebird'),
    _ = require('underscore'),
    qidx = require('quadtile-index'),
    checkType = require('@kartotherian/input-validator'),
    Err = require('@kartotherian/err');

let core;

module.exports = function tiles(cor, router) {
    core = cor;

    // get tile
    router.get('/:src(' + core.Sources.sourceIdReStr + ')/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w]+)', requestHandler);
    router.get('/:src(' + core.Sources.sourceIdReStr + ')/:z(\\d+)/:x(\\d+)/:y(\\d+)@:scale([\\.\\d]+)x.:format([\\w]+)', requestHandler);
};

/**
 * Web server (express) route handler to get requested tile
 * @param req request object
 * @param res response object
 * @param next will be called if request is not handled
 */
function requestHandler(req, res, next) {

    let source,
        params = req && req.params,
        start = Date.now();

    return Promise.try(() => {
        source = core.getPublicSource(params.src);

        if (!_.contains(source.formats, params.format)) {
            throw new Err('Format %s is not known', params.format).metrics('err.req.format');
        }

        params.z = core.validateZoom(params.z, source);
        params.scale = core.validateScale(params.scale, source);

        params.x = checkType.strToInt(params.x);
        params.y = checkType.strToInt(params.y);
        if (!qidx.isValidCoordinate(params.x, params.z) || !qidx.isValidCoordinate(params.y, params.z)) {
            throw new Err('x,y coordinates are not valid, or not allowed for this zoom').metrics('err.req.coords');
        }

        let opts;
        if (params.format !== 'pbf') {
            if (params.format === 'png') {
                // Ensure that PNGs are not 32bit
                // TODO: this should be source-configurable
                params.format = 'png8:m=h';
            }
            opts = {format: params.format};
            if (params.scale) {
                opts.scale = params.scale;
            }
        }

        return core.getTitleWithParamsAsync(source.getHandler(), params.z, params.x, params.y, opts);

    }).spread((data, dataHeaders) => {
        core.setResponseHeaders(res, source, dataHeaders);

        if (params.format === 'json') {
            // Allow JSON to be shortened to simplify debugging
            res.json(filterJson(req.query, data));
        } else {
            res.send(data);
        }

        let mx = util.format('req.%s.%s.%s', params.src, params.z, params.format);
        if (params.scale) {
            // replace '.' with ',' -- otherwise grafana treats it as a divider
            mx += '.' + (params.scale.toString().replace('.', ','));
        }
        core.metrics.endTiming(mx, start);
    }).catch(err => core.reportRequestError(err, res)).catch(next);
}

function filterJson(query, data) {
    if ('summary' in query) {
        data = _(data).reduce((memo, layer) => {
            memo[layer.name] = {
                features: layer.features.length,
                jsonsize: JSON.stringify(layer).length
            };
            return memo;
        }, {});
    } else if ('nogeo' in query) {
        // Recursively remove all "geometry" fields, replacing them with geometry's size
        let filter = (val, key) => {
            if (key === 'geometry') {
                return val.length;
            } else if (_.isArray(val)) {
                return _.map(val, filter);
            } else if (_.isObject(val)) {
                _.each(val, (v, k) => {
                    val[k] = filter(v, k);
                });
            }
            return val;
        };
        data = _.map(data, filter);
    }
    return data;
}
<|endoftext|>


<commit_before>'use strict';

const timer = {};

timer.nextTenSeconds = function(date) {
  let newDate = new Date(date);
  // actually, it's 5 sec
  newDate.setSeconds(newDate.getSeconds() + 5);
  return newDate;
};

module.exports = exports = timer;<commit_msg>Even faster 3 sec<commit_after>'use strict';

const timer = {};

timer.nextTenSeconds = function(date) {
  let newDate = new Date(date);
  // actually, it's 3 sec!!
  newDate.setSeconds(newDate.getSeconds() + 3);
  return newDate;
};

module.exports = exports = timer;<|endoftext|>


<commit_before>function playPause() {
	sendMsg({'cmd':'playpause'}, function() {
	}, function () {
		alert("Failed to play/pause");
	});
}

function guiTogglePlayPause() {
	if ($("#playpause").html() == "Play") {
		$("#playpause").html("Pause");
	} else {
		$("#playpause").html("Play");
	}
}

function play(id) {
	$("#" + id).text("loading");
	$("#" + id).prop("disabled", true);
	sendMsg({'cmd':'play', 'id':id}, function() {}, function(e) {
		alert("Failed to play video");
		$("#" + id).prop("disabled", false);
	$("#" + id).text("play");
	});
}

function addVideo(youtubeURL) {
	$("#videoUrl").prop("disabled", true);
	sendMsg({'cmd':'add', 'uri': youtubeURL}, function(e) {
		$("#videoUrl").prop("disabled", false);
		$("#videoUrl").val("");
	}, function(e) {
		alert("Failed to add video");
		$("#videoUrl").prop("disabled", false);
		$("#videoUrl").val("");
	});
}

function volUp() {
	sendMsg({'cmd':'volup'}, function(e) {
	}, function(e) {
		alert("Failed to increase volume");
	});
}

function volDown() {
	sendMsg({'cmd':'voldn'}, function(e) {
	}, function(e) {
		alert("Failed to decrease volume");
	});
}

//////

var sendMsg;

function playlistPlay(ev) {
	play(ev.toElement.id);
}

function guiUpdatePlaylist(playlist) {
	$("#playlist").empty();
	var lis = [];
	playlist.forEach(function(elm, idx, arr) {
		var li = document.createElement("li");
		li.className = "playlist_elm";

		var button = document.createElement("button");
		button.id = elm.id;
		button.textContent = "play";
		button.onclick = playlistPlay;
		li.appendChild(button);

		var link = document.createElement("a");
		link.href = elm.uri;
		// wheeee no sanitization here
		link.textContent = elm.title;
		link.target = "_blank";
		li.appendChild(link);

		lis.push(li);
	});

	$("#playlist").append(lis);
}

function guiUpdateVolume(vol) {
	$("#volume").text(vol + " %");
}

function guiUpdateCurrentlyPlaying(current) {
	if (current["current"] == null) return;
	var id = "#" + current["current"];
	$(id).text("playing");
	$(id).prop("disabled", true);
}

function generalFail(req) {
	console.log("GENERAL FAIL ON REQUEST:", req);
}

$(document).ready(function() {
	if (!window.WebSocket) {
		alert("Do not have websockets! Boo you >:-(");
		return;
	}

	var ws = new WebSocket("ws://localhost:9000/rq");

	sendMsg = (function(ws) {
		var ws = ws;
		var rqid = 0;
		var wait = {};

		ws.onmessage = function(ev) {
			resp = JSON.parse(ev.data);
			if (resp["broadcast"] === true) {
				switch(resp["type"]) {
					case "playlist":
						guiUpdatePlaylist(resp["playlist"]);
						break;
					case "volume":
						guiUpdateVolume(resp["value"]);
						break;
					case "playpause":
						guiTogglePlayPause();
						break;
					case "current":
						guiUpdateCurrentlyPlaying(resp);
						break;
					default:
						console.log("BAD BROADCAST: ", resp);
				}
				return;
			}

			if (wait[resp["rqid"]] === undefined) {
				console.log("BAD MESSAGE:", resp);
				console.log(resp);
			}
			else {
				if (resp["status"] == false) {
					wait[resp["rqid"]]["failure"](wait[resp["rqid"]]["request"]);
				}
				else {
					wait[resp["rqid"]]["success"](resp);
				}
			}
		};

		return function(jsonMsg, success_call, fail_call) {
			jsonMsg["rqid"] = rqid;
			wait[rqid] = {
				"request": jsonMsg,
				"success": success_call,
				"failure": fail_call
			};
			rqid++;
			ws.send(JSON.stringify(jsonMsg));
		};
	})(ws);

	ws.onopen = function(ev) {
		console.log("WS Ready");
		sendMsg({"cmd":"playlist"}, function(e){guiUpdatePlaylist(e["playlist"]);}, function(e){});
		sendMsg({"cmd":"volume"}, function(e){guiUpdateVolume(e["volume"]);}, function(e){});
		sendMsg({"cmd":"current"}, function(e){guiUpdateCurrentlyPlaying(e);}, function(e){});
		sendMsg({"cmd":"ispaused"}, function(e){
			$("#playpause").html(e.ispaused ? "Play" : "Pause");
		},function(e){});
	};

	$("#addVideo").on("click", function() {
		addVideo($("#videoUrl").val());
	});
});
<commit_msg>Fixed multiple "playing" texts bug.<commit_after>function playPause() {
	sendMsg({'cmd':'playpause'}, function() {
	}, function () {
		alert("Failed to play/pause");
	});
}

function guiTogglePlayPause() {
	if ($("#playpause").html() == "Play") {
		$("#playpause").html("Pause");
	} else {
		$("#playpause").html("Play");
	}
}

function play(id) {
	$("#" + id).text("loading");
	$("#" + id).prop("disabled", true);
	sendMsg({'cmd':'play', 'id':id}, function() {}, function(e) {
		alert("Failed to play video");
		$("#" + id).prop("disabled", false);
	$("#" + id).text("play");
	});
}

function addVideo(youtubeURL) {
	$("#videoUrl").prop("disabled", true);
	sendMsg({'cmd':'add', 'uri': youtubeURL}, function(e) {
		$("#videoUrl").prop("disabled", false);
		$("#videoUrl").val("");
	}, function(e) {
		alert("Failed to add video");
		$("#videoUrl").prop("disabled", false);
		$("#videoUrl").val("");
	});
}

function volUp() {
	sendMsg({'cmd':'volup'}, function(e) {
	}, function(e) {
		alert("Failed to increase volume");
	});
}

function volDown() {
	sendMsg({'cmd':'voldn'}, function(e) {
	}, function(e) {
		alert("Failed to decrease volume");
	});
}

//////

var sendMsg;

function playlistPlay(ev) {
	play(ev.toElement.id);
}

function guiUpdatePlaylist(playlist) {
	$("#playlist").empty();
	var lis = [];
	playlist.forEach(function(elm, idx, arr) {
		var li = document.createElement("li");
		li.className = "playlist_elm";

		var button = document.createElement("button");
		button.id = elm.id;
		button.textContent = "play";
		button.onclick = playlistPlay;
		li.appendChild(button);

		var link = document.createElement("a");
		link.href = elm.uri;
		// wheeee no sanitization here
		link.textContent = elm.title;
		link.target = "_blank";
		li.appendChild(link);

		lis.push(li);
	});

	$("#playlist").append(lis);
}

function guiUpdateVolume(vol) {
	$("#volume").text(vol + " %");
}

function guiUpdateCurrentlyPlaying(current) {
	if (current["current"] == null) return;
	$(".playlist_elm > button").prop('disabled',false).text("play");
	var id = "#" + current["current"];
	$(id).text("playing");
	$(id).prop("disabled", true);
}

function generalFail(req) {
	console.log("GENERAL FAIL ON REQUEST:", req);
}

$(document).ready(function() {
	if (!window.WebSocket) {
		alert("Do not have websockets! Boo you >:-(");
		return;
	}

	var ws = new WebSocket("ws://localhost:9000/rq");

	sendMsg = (function(ws) {
		var ws = ws;
		var rqid = 0;
		var wait = {};

		ws.onmessage = function(ev) {
			resp = JSON.parse(ev.data);
			if (resp["broadcast"] === true) {
				switch(resp["type"]) {
					case "playlist":
						guiUpdatePlaylist(resp["playlist"]);
						break;
					case "volume":
						guiUpdateVolume(resp["value"]);
						break;
					case "playpause":
						guiTogglePlayPause();
						break;
					case "current":
						guiUpdateCurrentlyPlaying(resp);
						break;
					default:
						console.log("BAD BROADCAST: ", resp);
				}
				return;
			}

			if (wait[resp["rqid"]] === undefined) {
				console.log("BAD MESSAGE:", resp);
				console.log(resp);
			}
			else {
				if (resp["status"] == false) {
					wait[resp["rqid"]]["failure"](wait[resp["rqid"]]["request"]);
				}
				else {
					wait[resp["rqid"]]["success"](resp);
				}
			}
		};

		return function(jsonMsg, success_call, fail_call) {
			jsonMsg["rqid"] = rqid;
			wait[rqid] = {
				"request": jsonMsg,
				"success": success_call,
				"failure": fail_call
			};
			rqid++;
			ws.send(JSON.stringify(jsonMsg));
		};
	})(ws);

	ws.onopen = function(ev) {
		console.log("WS Ready");
		sendMsg({"cmd":"playlist"}, function(e){guiUpdatePlaylist(e["playlist"]);}, function(e){});
		sendMsg({"cmd":"volume"}, function(e){guiUpdateVolume(e["volume"]);}, function(e){});
		sendMsg({"cmd":"current"}, function(e){guiUpdateCurrentlyPlaying(e);}, function(e){});
		sendMsg({"cmd":"ispaused"}, function(e){
			$("#playpause").html(e.ispaused ? "Play" : "Pause");
		},function(e){});
	};

	$("#addVideo").on("click", function() {
		addVideo($("#videoUrl").val());
	});
});
<|endoftext|>