'use strict';

/**
 * Module dependencies.
 */

var InvalidArgumentError = require('../errors/invalid-argument-error');
var InvalidRequestError = require('../errors/invalid-request-error');
var InsufficientScopeError = require('../errors/insufficient-scope-error');
var InvalidTokenError = require('../errors/invalid-token-error');
var OAuthError = require('../errors/oauth-error');
var Promise = require('bluebird');
var promisify = require('promisify-any').use(Promise);
var Request = require('../request');
var Response = require('../response');
var ServerError = require('../errors/server-error');
var UnauthorizedRequestError = require('../errors/unauthorized-request-error');

/**
 * Constructor.
 */

function AuthenticateHandler(options) {
  options = options || {};

  if (!options.model) {
    throw new InvalidArgumentError('Missing parameter: `model`');
  }

  if (!options.model.getAccessToken) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `getAccessToken()`');
  }

  if (options.scope && undefined === options.addAcceptedScopesHeader) {
    throw new InvalidArgumentError('Missing parameter: `addAcceptedScopesHeader`');
  }

  if (options.scope && undefined === options.addAuthorizedScopesHeader) {
    throw new InvalidArgumentError('Missing parameter: `addAuthorizedScopesHeader`');
  }

  if (options.scope && !options.model.verifyScope) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `verifyScope()`');
  }

  this.addAcceptedScopesHeader = options.addAcceptedScopesHeader;
  this.addAuthorizedScopesHeader = options.addAuthorizedScopesHeader;
  this.allowBearerTokensInQueryString = options.allowBearerTokensInQueryString;
  this.model = options.model;
  this.scope = options.scope;
}

/**
 * Authenticate Handler.
 */

AuthenticateHandler.prototype.handle = function(request, response) {
  if (!(request instanceof Request)) {
    throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request');
  }

  if (!(response instanceof Response)) {
    throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response');
  }

  return Promise.bind(this)
    .then(function() {
      return this.getTokenFromRequest(request);
    })
    .then(function(token) {
      return this.getAccessToken(token);
    })
    .tap(function(token) {
      return this.validateAccessToken(token);
    })
    .tap(function(token) {
      if (!this.scope) {
        return;
      }

      return this.verifyScope(token);
    })
    .tap(function(token) {
      return this.updateResponse(response, token);
    })
    .catch(function(e) {
      // Include the "WWW-Authenticate" response header field if the client
      // lacks any authentication information.
      //
      // @see https://tools.ietf.org/html/rfc6750#section-3.1
      if (e instanceof UnauthorizedRequestError) {
        response.set('WWW-Authenticate', 'Bearer realm="Service"');
      }

      if (!(e instanceof OAuthError)) {
        throw new ServerError(e);
      }

      throw e;
    });
};

/**
 * Get the token from the header or body, depending on the request.
 *
 * "Clients MUST NOT use more than one method to transmit the token in each request."
 *
 * @see https://tools.ietf.org/html/rfc6750#section-2
 */

AuthenticateHandler.prototype.getTokenFromRequest = function(request) {
  var headerToken = request.get('Authorization');
  var queryToken = request.query.access_token;
  var bodyToken = request.body.access_token;

  if (!!headerToken + !!queryToken + !!bodyToken > 1) {
    throw new InvalidRequestError('Invalid request: only one authentication method is allowed');
  }

  if (headerToken) {
    return this.getTokenFromRequestHeader(request);
  }

  if (queryToken) {
    return this.getTokenFromRequestQuery(request);
  }

  if (bodyToken) {
    return this.getTokenFromRequestBody(request);
  }

  throw new UnauthorizedRequestError('Unauthorized request: no authentication given');
};

/**
 * Get the token from the request header.
 *
 * @see http://tools.ietf.org/html/rfc6750#section-2.1
 */

AuthenticateHandler.prototype.getTokenFromRequestHeader = function(request) {
  var token = request.get('Authorization');
  var matches = token.match(/Bearer\s(\S+)/);

  if (!matches) {
    throw new InvalidRequestError('Invalid request: malformed authorization header');
  }

  return matches[1];
};

/**
 * Get the token from the request query.
 *
 * "Don't pass bearer tokens in page URLs:  Bearer tokens SHOULD NOT be passed in page
 * URLs (for example, as query string parameters). Instead, bearer tokens SHOULD be
 * passed in HTTP message headers or message bodies for which confidentiality measures
 * are taken. Browsers, web servers, and other software may not adequately secure URLs
 * in the browser history, web server logs, and other data structures. If bearer tokens
 * are passed in page URLs, attackers might be able to steal them from the history data,
 * logs, or other unsecured locations."
 *
 * @see http://tools.ietf.org/html/rfc6750#section-2.3
 */

AuthenticateHandler.prototype.getTokenFromRequestQuery = function(request) {
  if (!this.allowBearerTokensInQueryString) {
    throw new InvalidRequestError('Invalid request: do not send bearer tokens in query URLs');
  }

  return request.query.access_token;
};

/**
 * Get the token from the request body.
 *
 * "The HTTP request method is one for which the request-body has defined semantics.
 * In particular, this means that the "GET" method MUST NOT be used."
 *
 * @see http://tools.ietf.org/html/rfc6750#section-2.2
 */

AuthenticateHandler.prototype.getTokenFromRequestBody = function(request) {
  if (request.method === 'GET') {
    throw new InvalidRequestError('Invalid request: token may not be passed in the body when using the GET verb');
  }

  if (!request.is('application/x-www-form-urlencoded')) {
    throw new InvalidRequestError('Invalid request: content must be application/x-www-form-urlencoded');
  }

  return request.body.access_token;
};

/**
 * Get the access token from the model.
 */

AuthenticateHandler.prototype.getAccessToken = function(token) {
  return promisify(this.model.getAccessToken, 1).call(this.model, token)
    .then(function(accessToken) {
      if (!accessToken) {
        throw new InvalidTokenError('Invalid token: access token is invalid');
      }

      if (!accessToken.user) {
        throw new ServerError('Server error: `getAccessToken()` did not return a `user` object');
      }

      return accessToken;
    });
};

/**
 * Validate access token.
 */

AuthenticateHandler.prototype.validateAccessToken = function(accessToken) {
  if (!(accessToken.accessTokenExpiresAt instanceof Date)) {
    throw new ServerError('Server error: `accessTokenExpiresAt` must be a Date instance');
  }

  if (accessToken.accessTokenExpiresAt < new Date()) {
    throw new InvalidTokenError('Invalid token: access token has expired');
  }

  return accessToken;
};

/**
 * Verify scope.
 */

AuthenticateHandler.prototype.verifyScope = function(accessToken) {
  return promisify(this.model.verifyScope, 2).call(this.model, accessToken, this.scope)
    .then(function(scope) {
      if (!scope) {
        throw new InsufficientScopeError('Insufficient scope: authorized scope is insufficient');
      }

      return scope;
    });
};

/**
 * Update response.
 */

AuthenticateHandler.prototype.updateResponse = function(response, accessToken) {
  if (this.scope && this.addAcceptedScopesHeader) {
    response.set('X-Accepted-OAuth-Scopes', this.scope);
  }

  if (this.scope && this.addAuthorizedScopesHeader) {
    response.set('X-OAuth-Scopes', accessToken.scope);
  }
};

/**
 * Export constructor.
 */

module.exports = AuthenticateHandler;


'use strict';

/**
 * Module dependencies.
 */

var _ = require('lodash');
var OAuthError = require('./oauth-error');
var util = require('util');

/**
 * Constructor.
 */

function InvalidArgumentError(message, properties) {
  properties = _.assign({
    code: 500,
    name: 'invalid_argument'
  }, properties);

  OAuthError.call(this, message, properties);
}

/**
 * Inherit prototype.
 */

util.inherits(InvalidArgumentError, OAuthError);

/**
 * Export constructor.
 */

module.exports = InvalidArgumentError;


'use strict';

/**
 * Module dependencies.
 */

var AbstractGrantType = require('./abstract-grant-type');
var InvalidArgumentError = require('../errors/invalid-argument-error');
var InvalidGrantError = require('../errors/invalid-grant-error');
var InvalidRequestError = require('../errors/invalid-request-error');
var Promise = require('bluebird');
var promisify = require('promisify-any').use(Promise);
var ServerError = require('../errors/server-error');
var is = require('../validator/is');
var util = require('util');

/**
 * Constructor.
 */

function RefreshTokenGrantType(options) {
  options = options || {};

  if (!options.model) {
    throw new InvalidArgumentError('Missing parameter: `model`');
  }

  if (!options.model.getRefreshToken) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `getRefreshToken()`');
  }

  if (!options.model.revokeToken) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `revokeToken()`');
  }

  if (!options.model.saveToken) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `saveToken()`');
  }

  AbstractGrantType.call(this, options);
}

/**
 * Inherit prototype.
 */

util.inherits(RefreshTokenGrantType, AbstractGrantType);

/**
 * Handle refresh token grant.
 *
 * @see https://tools.ietf.org/html/rfc6749#section-6
 */

RefreshTokenGrantType.prototype.handle = function(request, client) {
  if (!request) {
    throw new InvalidArgumentError('Missing parameter: `request`');
  }

  if (!client) {
    throw new InvalidArgumentError('Missing parameter: `client`');
  }

  return Promise.bind(this)
    .then(function() {
      return this.getRefreshToken(request, client);
    })
    .tap(function(token) {
      return this.revokeToken(token);
    })
    .then(function(token) {
      return this.saveToken(token.user, client, token.scope);
    });
};

/**
 * Get refresh token.
 */

RefreshTokenGrantType.prototype.getRefreshToken = function(request, client) {
  if (!request.body.refresh_token) {
    throw new InvalidRequestError('Missing parameter: `refresh_token`');
  }

  if (!is.vschar(request.body.refresh_token)) {
    throw new InvalidRequestError('Invalid parameter: `refresh_token`');
  }

  return promisify(this.model.getRefreshToken, 1).call(this.model, request.body.refresh_token)
    .then(function(token) {
      if (!token) {
        throw new InvalidGrantError('Invalid grant: refresh token is invalid');
      }

      if (!token.client) {
        throw new ServerError('Server error: `getRefreshToken()` did not return a `client` object');
      }

      if (!token.user) {
        throw new ServerError('Server error: `getRefreshToken()` did not return a `user` object');
      }

      if (token.client.id !== client.id) {
        throw new InvalidGrantError('Invalid grant: refresh token is invalid');
      }

      if (token.refreshTokenExpiresAt && !(token.refreshTokenExpiresAt instanceof Date)) {
        throw new ServerError('Server error: `refreshTokenExpiresAt` must be a Date instance');
      }

      if (token.refreshTokenExpiresAt && token.refreshTokenExpiresAt < new Date()) {
        throw new InvalidGrantError('Invalid grant: refresh token has expired');
      }

      return token;
    });
};

/**
 * Revoke the refresh token.
 *
 * @see https://tools.ietf.org/html/rfc6749#section-6
 */

RefreshTokenGrantType.prototype.revokeToken = function(token) {
  if (this.alwaysIssueNewRefreshToken === false) {
    return Promise.resolve(token);
  }

  return promisify(this.model.revokeToken, 1).call(this.model, token)
    .then(function(status) {
      if (!status) {
        throw new InvalidGrantError('Invalid grant: refresh token is invalid');
      }

      return token;
    });
};

/**
 * Save token.
 */

RefreshTokenGrantType.prototype.saveToken = function(user, client, scope) {
  var fns = [
    this.generateAccessToken(client, user, scope),
    this.generateRefreshToken(client, user, scope),
    this.getAccessTokenExpiresAt(),
    this.getRefreshTokenExpiresAt()
  ];

  return Promise.all(fns)
    .bind(this)
    .spread(function(accessToken, refreshToken, accessTokenExpiresAt, refreshTokenExpiresAt) {
      var token = {
        accessToken: accessToken,
        accessTokenExpiresAt: accessTokenExpiresAt,
        scope: scope
      };

      if (this.alwaysIssueNewRefreshToken !== false) {
        token.refreshToken = refreshToken;
        token.refreshTokenExpiresAt = refreshTokenExpiresAt;
      }

      return token;
    })
    .then(function(token) {
      return promisify(this.model.saveToken, 3).call(this.model, token, client, user)
        .then(function(savedToken) {
          return savedToken;
        });
    });
};

/**
 * Export constructor.
 */

module.exports = RefreshTokenGrantType;


'use strict';

/**
 * Module dependencies.
 */

var InvalidArgumentError = require('./errors/invalid-argument-error');
var typeis = require('type-is');

/**
 * Constructor.
 */

function Request(options) {
  options = options || {};

  if (!options.headers) {
    throw new InvalidArgumentError('Missing parameter: `headers`');
  }

  if (!options.method) {
    throw new InvalidArgumentError('Missing parameter: `method`');
  }

  if (!options.query) {
    throw new InvalidArgumentError('Missing parameter: `query`');
  }

  this.body = options.body || {};
  this.headers = {};
  this.method = options.method;
  this.query = options.query;

  // Store the headers in lower case.
  for (var field in options.headers) {
    if (options.headers.hasOwnProperty(field)) {
      this.headers[field.toLowerCase()] = options.headers[field];
    }
  }

  // Store additional properties of the request object passed in
  for (var property in options) {
    if (options.hasOwnProperty(property) && !this[property]) {
      this[property] = options[property];
    }
  }
}

/**
 * Get a request header.
 */

Request.prototype.get = function(field) {
  return this.headers[field.toLowerCase()];
};

/**
 * Check if the content-type matches any of the given mime type.
 */

Request.prototype.is = function(types) {
  if (!Array.isArray(types)) {
    types = [].slice.call(arguments);
  }

  return typeis(this, types) || false;
};

/**
 * Export constructor.
 */

module.exports = Request;


'use strict';

/**
 * Module dependencies.
 */

var _ = require('lodash');
var AccessDeniedError = require('../errors/access-denied-error');
var AuthenticateHandler = require('../handlers/authenticate-handler');
var InvalidArgumentError = require('../errors/invalid-argument-error');
var InvalidClientError = require('../errors/invalid-client-error');
var InvalidRequestError = require('../errors/invalid-request-error');
var InvalidScopeError = require('../errors/invalid-scope-error');
var UnsupportedResponseTypeError = require('../errors/unsupported-response-type-error');
var OAuthError = require('../errors/oauth-error');
var Promise = require('bluebird');
var promisify = require('promisify-any').use(Promise);
var Request = require('../request');
var Response = require('../response');
var ServerError = require('../errors/server-error');
var UnauthorizedClientError = require('../errors/unauthorized-client-error');
var is = require('../validator/is');
var tokenUtil = require('../utils/token-util');
var url = require('url');

/**
 * Response types.
 */

var responseTypes = {
  code: require('../response-types/code-response-type'),
  //token: require('../response-types/token-response-type')
};

/**
 * Constructor.
 */

function AuthorizeHandler(options) {
  options = options || {};

  if (options.authenticateHandler && !options.authenticateHandler.handle) {
    throw new InvalidArgumentError('Invalid argument: authenticateHandler does not implement `handle()`');
  }

  if (!options.authorizationCodeLifetime) {
    throw new InvalidArgumentError('Missing parameter: `authorizationCodeLifetime`');
  }

  if (!options.model) {
    throw new InvalidArgumentError('Missing parameter: `model`');
  }

  if (!options.model.getClient) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `getClient()`');
  }

  if (!options.model.saveAuthorizationCode) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `saveAuthorizationCode()`');
  }

  this.allowEmptyState = options.allowEmptyState;
  this.authenticateHandler = options.authenticateHandler || new AuthenticateHandler(options);
  this.authorizationCodeLifetime = options.authorizationCodeLifetime;
  this.model = options.model;
}

/**
 * Authorize Handler.
 */

AuthorizeHandler.prototype.handle = function(request, response) {
  if (!(request instanceof Request)) {
    throw new InvalidArgumentError('Invalid argument: `request` must be an instance of Request');
  }

  if (!(response instanceof Response)) {
    throw new InvalidArgumentError('Invalid argument: `response` must be an instance of Response');
  }

  if ('false' === request.query.allowed) {
    return Promise.reject(new AccessDeniedError('Access denied: user denied access to application'));
  }

  var fns = [
    this.getAuthorizationCodeLifetime(),
    this.getClient(request),
    this.getUser(request, response)
  ];

  return Promise.all(fns)
    .bind(this)
    .spread(function(expiresAt, client, user) {
      var uri = this.getRedirectUri(request, client);
      var scope;
      var state;
      var ResponseType;

      return Promise.bind(this)
	.then(function() {
          scope = this.getScope(request);

	  return this.generateAuthorizationCode(client, user, scope);
	})
        .then(function(authorizationCode) {
          state = this.getState(request);
          ResponseType = this.getResponseType(request);

          return this.saveAuthorizationCode(authorizationCode, expiresAt, scope, client, uri, user);
        })
        .then(function(code) {
          var responseType = new ResponseType(code.authorizationCode);
          var redirectUri = this.buildSuccessRedirectUri(uri, responseType);

          this.updateResponse(response, redirectUri, state);

          return code;
        })
        .catch(function(e) {
          if (!(e instanceof OAuthError)) {
            e = new ServerError(e);
          }
          var redirectUri = this.buildErrorRedirectUri(uri, e);

          this.updateResponse(response, redirectUri, state);

          throw e;
        });
    });
};

/**
 * Generate authorization code.
 */

AuthorizeHandler.prototype.generateAuthorizationCode = function(client, user, scope) {
  if (this.model.generateAuthorizationCode) {
    return promisify(this.model.generateAuthorizationCode).call(this.model, client, user, scope);
  }
  return tokenUtil.generateRandomToken();
};

/**
 * Get authorization code lifetime.
 */

AuthorizeHandler.prototype.getAuthorizationCodeLifetime = function() {
  var expires = new Date();

  expires.setSeconds(expires.getSeconds() + this.authorizationCodeLifetime);
  return expires;
};

/**
 * Get the client from the model.
 */

AuthorizeHandler.prototype.getClient = function(request) {
  var clientId = request.body.client_id || request.query.client_id;

  if (!clientId) {
    throw new InvalidRequestError('Missing parameter: `client_id`');
  }

  if (!is.vschar(clientId)) {
    throw new InvalidRequestError('Invalid parameter: `client_id`');
  }

  var redirectUri = request.body.redirect_uri || request.query.redirect_uri;

  if (redirectUri && !is.uri(redirectUri)) {
    throw new InvalidRequestError('Invalid request: `redirect_uri` is not a valid URI');
  }
  return promisify(this.model.getClient, 2).call(this.model, clientId, null)
    .then(function(client) {
      if (!client) {
        throw new InvalidClientError('Invalid client: client credentials are invalid');
      }

      if (!client.grants) {
        throw new InvalidClientError('Invalid client: missing client `grants`');
      }

      if (!_.includes(client.grants, 'authorization_code')) {
        throw new UnauthorizedClientError('Unauthorized client: `grant_type` is invalid');
      }

      if (!client.redirectUris || 0 === client.redirectUris.length) {
        throw new InvalidClientError('Invalid client: missing client `redirectUri`');
      }

      if (redirectUri && !_.includes(client.redirectUris, redirectUri)) {
        throw new InvalidClientError('Invalid client: `redirect_uri` does not match client value');
      }
      return client;
    });
};

/**
 * Get scope from the request.
 */

AuthorizeHandler.prototype.getScope = function(request) {
  var scope = request.body.scope || request.query.scope;

  if (!is.nqschar(scope)) {
    throw new InvalidScopeError('Invalid parameter: `scope`');
  }

  return scope;
};

/**
 * Get state from the request.
 */

AuthorizeHandler.prototype.getState = function(request) {
  var state = request.body.state || request.query.state;

  if (!this.allowEmptyState && !state) {
    throw new InvalidRequestError('Missing parameter: `state`');
  }

  if (!is.vschar(state)) {
    throw new InvalidRequestError('Invalid parameter: `state`');
  }

  return state;
};

/**
 * Get user by calling the authenticate middleware.
 */

AuthorizeHandler.prototype.getUser = function(request, response) {
  if (this.authenticateHandler instanceof AuthenticateHandler) {
    return this.authenticateHandler.handle(request, response).get('user');
  }
  return promisify(this.authenticateHandler.handle, 2)(request, response).then(function(user) {
    if (!user) {
      throw new ServerError('Server error: `handle()` did not return a `user` object');
    }

    return user;
  });
};

/**
 * Get redirect URI.
 */

AuthorizeHandler.prototype.getRedirectUri = function(request, client) {
  return request.body.redirect_uri || request.query.redirect_uri || client.redirectUris[0];
};

/**
 * Save authorization code.
 */

AuthorizeHandler.prototype.saveAuthorizationCode = function(authorizationCode, expiresAt, scope, client, redirectUri, user) {
  var code = {
    authorizationCode: authorizationCode,
    expiresAt: expiresAt,
    redirectUri: redirectUri,
    scope: scope
  };
  return promisify(this.model.saveAuthorizationCode, 3).call(this.model, code, client, user);
};

/**
 * Get response type.
 */

AuthorizeHandler.prototype.getResponseType = function(request) {
  var responseType = request.body.response_type || request.query.response_type;

  if (!responseType) {
    throw new InvalidRequestError('Missing parameter: `response_type`');
  }

  if (!_.has(responseTypes, responseType)) {
    throw new UnsupportedResponseTypeError('Unsupported response type: `response_type` is not supported');
  }

  return responseTypes[responseType];
};

/**
 * Build a successful response that redirects the user-agent to the client-provided url.
 */

AuthorizeHandler.prototype.buildSuccessRedirectUri = function(redirectUri, responseType) {
  return responseType.buildRedirectUri(redirectUri);
};

/**
 * Build an error response that redirects the user-agent to the client-provided url.
 */

AuthorizeHandler.prototype.buildErrorRedirectUri = function(redirectUri, error) {
  var uri = url.parse(redirectUri);

  uri.query = {
    error: error.name
  };

  if (error.message) {
    uri.query.error_description = error.message;
  }

  return uri;
};

/**
 * Update response with the redirect uri and the state parameter, if available.
 */

AuthorizeHandler.prototype.updateResponse = function(response, redirectUri, state) {
  redirectUri.query = redirectUri.query || {};

  if (state) {
    redirectUri.query.state = state;
  }

  response.redirect(url.format(redirectUri));
};

/**
 * Export constructor.
 */

module.exports = AuthorizeHandler;


'use strict';

/**
 * Module dependencies.
 */

var AbstractGrantType = require('./abstract-grant-type');
var InvalidArgumentError = require('../errors/invalid-argument-error');
var InvalidGrantError = require('../errors/invalid-grant-error');
var Promise = require('bluebird');
var promisify = require('promisify-any').use(Promise);
var util = require('util');

/**
 * Constructor.
 */

function ClientCredentialsGrantType(options) {
  options = options || {};

  if (!options.model) {
    throw new InvalidArgumentError('Missing parameter: `model`');
  }

  if (!options.model.getUserFromClient) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `getUserFromClient()`');
  }

  if (!options.model.saveToken) {
    throw new InvalidArgumentError('Invalid argument: model does not implement `saveToken()`');
  }

  AbstractGrantType.call(this, options);
}

/**
 * Inherit prototype.
 */

util.inherits(ClientCredentialsGrantType, AbstractGrantType);

/**
 * Handle client credentials grant.
 *
 * @see https://tools.ietf.org/html/rfc6749#section-4.4.2
 */

ClientCredentialsGrantType.prototype.handle = function(request, client) {
  if (!request) {
    throw new InvalidArgumentError('Missing parameter: `request`');
  }

  if (!client) {
    throw new InvalidArgumentError('Missing parameter: `client`');
  }

  var scope = this.getScope(request);

  return Promise.bind(this)
    .then(function() {
      return this.getUserFromClient(client);
    })
    .then(function(user) {
      return this.saveToken(user, client, scope);
    });
};

/**
 * Retrieve the user using client credentials.
 */

ClientCredentialsGrantType.prototype.getUserFromClient = function(client) {
  return promisify(this.model.getUserFromClient, 1).call(this.model, client)
    .then(function(user) {
      if (!user) {
        throw new InvalidGrantError('Invalid grant: user credentials are invalid');
      }

      return user;
    });
};

/**
 * Save token.
 */

ClientCredentialsGrantType.prototype.saveToken = function(user, client, scope) {
  var fns = [
    this.validateScope(user, client, scope),
    this.generateAccessToken(client, user, scope),
    this.getAccessTokenExpiresAt(client, user, scope)
  ];

  return Promise.all(fns)
    .bind(this)
    .spread(function(scope, accessToken, accessTokenExpiresAt) {
      var token = {
        accessToken: accessToken,
        accessTokenExpiresAt: accessTokenExpiresAt,
        scope: scope
      };

      return promisify(this.model.saveToken, 3).call(this.model, token, client, user);
    });
};

/**
 * Export constructor.
 */

module.exports = ClientCredentialsGrantType;


'use strict';

/**
 * Module dependencies.
 */

var InvalidArgumentError = require('../errors/invalid-argument-error');

/**
 * Constructor.
 */

function BearerTokenType(accessToken, accessTokenLifetime, refreshToken, scope, customAttributes) {
  if (!accessToken) {
    throw new InvalidArgumentError('Missing parameter: `accessToken`');
  }

  this.accessToken = accessToken;
  this.accessTokenLifetime = accessTokenLifetime;
  this.refreshToken = refreshToken;
  this.scope = scope;

  if (customAttributes) {
    this.customAttributes = customAttributes;
  }
}

/**
 * Retrieve the value representation.
 */

BearerTokenType.prototype.valueOf = function() {
  var object = {
    access_token: this.accessToken,
    token_type: 'Bearer'
  };

  if (this.accessTokenLifetime) {
    object.expires_in = this.accessTokenLifetime;
  }

  if (this.refreshToken) {
    object.refresh_token = this.refreshToken;
  }

  if (this.scope) {
    object.scope = this.scope;
  }

  for (var key in this.customAttributes) {
    if (this.customAttributes.hasOwnProperty(key)) {
      object[key] = this.customAttributes[key];
    }
  }
  return object;
};

/**
 * Export constructor.
 */

module.exports = BearerTokenType;


'use strict';

/**
 * Constructor.
 */

function Response(options) {
  options = options || {};

  this.body = options.body || {};
  this.headers = {};
  this.status = 200;

  // Store the headers in lower case.
  for (var field in options.headers) {
    if (options.headers.hasOwnProperty(field)) {
      this.headers[field.toLowerCase()] = options.headers[field];
    }
  }

  // Store additional properties of the response object passed in
  for (var property in options) {
    if (options.hasOwnProperty(property) && !this[property]) {
      this[property] = options[property];
    }
  }
}

/**
 * Get a response header.
 */

Response.prototype.get = function(field) {
  return this.headers[field.toLowerCase()];
};

/**
 * Redirect response.
 */

Response.prototype.redirect = function(url) {
  this.set('Location', url);
  this.status = 302;
};

/**
 * Set a response header.
 */

Response.prototype.set = function(field, value) {
  this.headers[field.toLowerCase()] = value;
};

/**
 * Export constructor.
 */

module.exports = Response;


;(function($B) {
var _b_ = $B.builtins
  // Static binding in function closure needed for import hooks
  // to stick to builtin cache even if module is overriden in sys.modules
  //$sys = $B.imported['sys'];

/**
* [Import spec] [PEP 302] Brython import machinery
*
* @param mod_name        {string}    name of module to load
* @param origin          {string}    name of module context invoking the import
* @param _path           {list}      Brython's None for top-level modules
*                                    Value of parent package's __path__
* @param module          {module}    [Optional] Existing module, for reload only
*/
function import_hooks(mod_name, _path, from_stdlib) {

    var _meta_path = $B.meta_path.slice(),
        _sys_modules = $B.imported,
        _loader,
        spec

    if(from_stdlib == "static"){
        // No use trying anything else than stdlib finders
        var ix = _meta_path.indexOf($B.finders["path"])
        if(ix > -1){
            _meta_path.splice(ix, 1)
        }
    }else if(from_stdlib == "VFS"){
        var keys = ["path", "stdlib_static"]
        keys.forEach(function(key){
            var ix = _meta_path.indexOf($B.finders[key])
            if(ix > -1){
                _meta_path.splice(ix, 1)
            }
        })
    }

    for(var i = 0, len = _meta_path.length; i < len; i++){
        var _finder = _meta_path[i],
            find_spec = $B.$getattr(_finder, "find_spec", _b_.None)
        if(find_spec == _b_.None){
            // If find_spec is not defined for the meta path, try the legacy
            // method find_module()
            var find_module = $B.$getattr(_finder, "find_module", _b_.None)
            if(find_module !== _b_.None){
                _loader = find_module(mod_name, _path)
                // The loader has a method load_module()
                var load_module = $B.$getattr(_loader, "load_module")
                module = $B.$call(load_module)(mod_name)
                _sys_modules[mod_name] = module
                return module
            }
        }else{
            spec = find_spec(mod_name, _path, undefined)
            if(!$B.is_none(spec)){
                module = $B.imported[spec.name]
                if(module !== undefined){
                    // If module of same name is already in imports, return it
                    return _sys_modules[spec.name] = module
                }
                _loader = _b_.getattr(spec, "loader", _b_.None)
                break
            }
        }
    }

    if(_loader === undefined){
        // No import spec found
        var exc = _b_.ImportError.$factory("No module named " + mod_name)
        exc.name = mod_name
        throw exc
    }

    // Import spec represents a match
    if($B.is_none(module)){
        var _spec_name = _b_.getattr(spec, "name")

        // Create module object
        if(!$B.is_none(_loader)){
            var create_module = _b_.getattr(_loader, "create_module", _b_.None)
            if(!$B.is_none(create_module)){
                module = $B.$call(create_module)(spec)
            }
        }
        if(module === undefined){throw _b_.ImportError.$factory(mod_name)}
        if($B.is_none(module)){
            // FIXME : Initialize __doc__ and __package__
            module = $B.module.$factory(mod_name)
            var mod_desc = _b_.getattr(spec, "origin")
            if(_b_.getattr(spec, "has_location")){
                mod_desc = "from '" + mod_desc + "'"
            }else{
                mod_desc = "(" + mod_desc + ")"
            }
        }
    }
    module.__name__ = _spec_name
    module.__loader__ = _loader
    module.__package__ = _b_.getattr(spec, "parent", "")
    module.__spec__ = spec

    var locs = _b_.getattr(spec, "submodule_search_locations")
    // Brython-specific var
    if(module.$is_package = !$B.is_none(locs)){
        module.__path__ = locs
    }
    if(_b_.getattr(spec, "has_location")){
        module.__file__ = _b_.getattr(spec, "origin")
        $B.$py_module_path[module.__name__] = module.__file__
    }
    var cached = _b_.getattr(spec, "cached")
    if(! $B.is_none(cached)){
        module.__cached__ = cached
    }

    if($B.is_none(_loader)){
        if(!$B.is_none(locs)){
            _sys_modules[_spec_name] = module
        }else{
            throw _b_.ImportError.$factory(mod_name)
        }
    }else{
        var exec_module = _b_.getattr(_loader, "exec_module", _b_.None)
        if($B.is_none(exec_module)){
            // FIXME : Remove !!! Backwards compat in CPython
            module = _b_.getattr(_loader, "load_module")(_spec_name)
        }else{
            _sys_modules[_spec_name] = module
            try{
                exec_module(module)
            }catch(e){
                delete _sys_modules[_spec_name]
                throw e
           }
        }
    }
    return _sys_modules[_spec_name]
}

$B.import_hooks = import_hooks
})(__BRYTHON__)


;(function($B){

//eval($B.InjectBuiltins())

var _b_ = $B.builtins;
var object = _b_.object
var JSObject = $B.JSObject
var _window = self;

// cross-browser utility functions
function $getMouseOffset(target, ev){
    ev = ev || _window.event;
    var docPos    = $getPosition(target);
    var mousePos  = $mouseCoords(ev);
    return {x:mousePos.x - docPos.x, y:mousePos.y - docPos.y};
}

function $getPosition(e){
    var left = 0,
        top  = 0,
        width = e.width || e.offsetWidth,
        height = e.height || e.offsetHeight

    while (e.offsetParent){
        left += e.offsetLeft
        top  += e.offsetTop
        e = e.offsetParent
    }

    left += e.offsetLeft || 0
    top  += e.offsetTop || 0

    if(e.parentElement){
        // eg SVG element inside an HTML element
        var parent_pos = $getPosition(e.parentElement)
        left += parent_pos.left
        top += parent_pos.top
    }

    return {left: left, top: top, width: width, height: height}
}

function $mouseCoords(ev){
    var posx = 0,
        posy = 0
    if(!ev){var ev = _window.event}
    if(ev.pageX || ev.pageY){
        posx = ev.pageX
        posy = ev.pageY
    }else if(ev.clientX || ev.clientY){
        posx = ev.clientX + document.body.scrollLeft +
            document.documentElement.scrollLeft
        posy = ev.clientY + document.body.scrollTop +
            document.documentElement.scrollTop
    }
    var res = {}
    res.x = _b_.int.$factory(posx)
    res.y = _b_.int.$factory(posy)
    res.__getattr__ = function(attr){return this[attr]}
    res.__class__ = "MouseCoords"
    return res
}

var $DOMNodeAttrs = ["nodeName", "nodeValue", "nodeType", "parentNode",
    "childNodes", "firstChild", "lastChild", "previousSibling", "nextSibling",
    "attributes", "ownerDocument"]

$B.$isNode = function(o){
    // copied from http://stackoverflow.com/questions/384286/
    // javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
  return (
      typeof Node === "object" ? o instanceof Node :
      o && typeof o === "object" && typeof o.nodeType === "number" &&
      typeof o.nodeName === "string"
  )
}

$B.$isNodeList = function(nodes) {
    // copied from http://stackoverflow.com/questions/7238177/
    // detect-htmlcollection-nodelist-in-javascript
    try{
        var result = Object.prototype.toString.call(nodes)
        var re = new RegExp("^\\[object (HTMLCollection|NodeList)\\]$")
        return (typeof nodes === "object" &&
            re.exec(result) !== null &&
            nodes.length !== undefined &&
            (nodes.length == 0 ||
                (typeof nodes[0] === "object" && nodes[0].nodeType > 0))
        )
    }catch(err){
        return false
    }
}

var $DOMEventAttrs_W3C = ["NONE", "CAPTURING_PHASE", "AT_TARGET",
    "BUBBLING_PHASE", "type", "target", "currentTarget", "eventPhase",
    "bubbles", "cancelable", "timeStamp", "stopPropagation",
    "preventDefault", "initEvent"]

var $DOMEventAttrs_IE = ["altKey", "altLeft", "button", "cancelBubble",
    "clientX", "clientY", "contentOverflow", "ctrlKey", "ctrlLeft", "data",
    "dataFld", "dataTransfer", "fromElement", "keyCode", "nextPage",
    "offsetX", "offsetY", "origin", "propertyName", "reason", "recordset",
    "repeat", "screenX", "screenY", "shiftKey", "shiftLeft",
    "source", "srcElement", "srcFilter", "srcUrn", "toElement", "type",
    "url", "wheelDelta", "x", "y"]

$B.$isEvent = function(obj){
    var flag = true
    for(var i = 0; i < $DOMEventAttrs_W3C.length; i++){
        if(obj[$DOMEventAttrs_W3C[i]] === undefined){flag = false; break}
    }
    if(flag){return true}
    for(var i = 0; i < $DOMEventAttrs_IE.length; i++){
        if(obj[$DOMEventAttrs_IE[i]] === undefined){return false}
    }
    return true
}

// DOM node types
var $NodeTypes = {1:  "ELEMENT",
    2: "ATTRIBUTE",
    3: "TEXT",
    4: "CDATA_SECTION",
    5: "ENTITY_REFERENCE",
    6: "ENTITY",
    7: "PROCESSING_INSTRUCTION",
    8: "COMMENT",
    9: "DOCUMENT",
    10: "DOCUMENT_TYPE",
    11: "DOCUMENT_FRAGMENT",
    12: "NOTATION"
}

// Class for DOM attributes
var Attributes = $B.make_class("Attributes",
    function(elt){
        return{
            __class__: Attributes,
            elt: elt
        }
    }
)

Attributes.__contains__ = function(){
    var $ = $B.args("__getitem__", 2, {self: null, key:null},
        ["self", "key"], arguments, {}, null, null)
    if($.self.elt instanceof SVGElement){
        return $.self.elt.hasAttributeNS(null, $.key)
    }else if(typeof $.self.elt.hasAttribute == "function"){
        return $.self.elt.hasAttribute($.key)
    }
    return false
}

Attributes.__delitem__ = function(){
    var $ = $B.args("__getitem__", 2, {self: null, key:null},
        ["self", "key"], arguments, {}, null, null)
    if(!Attributes.__contains__($.self, $.key)){
        throw _b_.KeyError.$factory($.key)
    }
    if($.self.elt instanceof SVGElement){
        $.self.elt.removeAttributeNS(null, $.key)
        return _b_.None
    }else if(typeof $.self.elt.hasAttribute == "function"){
        $.self.elt.removeAttribute($.key)
        return _b_.None
    }
}

Attributes.__getitem__ = function(){
    var $ = $B.args("__getitem__", 2, {self: null, key:null},
        ["self", "key"], arguments, {}, null, null)
    if($.self.elt instanceof SVGElement &&
            $.self.elt.hasAttributeNS(null, $.key)){
        return $.self.elt.getAttributeNS(null, $.key)
    }else if(typeof $.self.elt.hasAttribute == "function" &&
            $.self.elt.hasAttribute($.key)){
        return $.self.elt.getAttribute($.key)
    }
    throw _b_.KeyError.$factory($.key)
}

Attributes.__iter__ = function(self){
    self.$counter = 0
    // Initialize list of key-value attribute pairs
    var attrs = self.elt.attributes,
        items = []
    for(var i = 0; i < attrs.length; i++){
        items.push(attrs[i].name)
    }
    self.$items = items
    return self
}

Attributes.__next__ = function(){
    var $ = $B.args("__next__", 1, {self: null},
        ["self"], arguments, {}, null, null)
    if($.self.$counter < $.self.$items.length){
        var res = $.self.$items[$.self.$counter]
        $.self.$counter++
        return res
    }else{
        throw _b_.StopIteration.$factory("")
    }
}

Attributes.__setitem__ = function(){
    var $ = $B.args("__setitem__", 3, {self: null, key:null, value: null},
        ["self", "key", "value"], arguments, {}, null, null)
    if($.self.elt instanceof SVGElement &&
            typeof $.self.elt.setAttributeNS == "function"){
        $.self.elt.setAttributeNS(null, $.key, $.value)
        return _b_.None
    }else if(typeof $.self.elt.setAttribute == "function"){
        $.self.elt.setAttribute($.key, $.value)
        return _b_.None
    }
    throw _b_.TypeError.$factory("Can't set attributes on element")
}

Attributes.get = function(){
    var $ = $B.args("get", 3, {self: null, key:null, deflt: null},
        ["self", "key", "deflt"], arguments, {deflt:_b_.None}, null, null)
    try{
        return Attributes.__getitem__($.self, $.key)
    }catch(err){
        if(err.__class__ === _b_.KeyError){
            return $B.deflt
        }else{
            throw err
        }
    }
}

Attributes.keys = function(){
    return Attributes.__iter__.apply(null, arguments)
}

Attributes.items = function(){
    var $ = $B.args("values", 1, {self: null},
        ["self"], arguments, {}, null, null),
        attrs = $.self.elt.attributes,
        values = []
    for(var i = 0; i < attrs.length; i++){
        values.push([attrs[i].name, attrs[i].value])
    }
    return _b_.list.__iter__(values)
}

Attributes.values = function(){
    var $ = $B.args("values", 1, {self: null},
        ["self"], arguments, {}, null, null),
        attrs = $.self.elt.attributes,
        values = []
    for(var i = 0; i < attrs.length; i++){
        values.push(attrs[i].value)
    }
    return _b_.list.__iter__(values)
}
$B.set_func_names(Attributes, "<dom>")

// Class for DOM events

var DOMEvent = $B.DOMEvent = {
    __class__: _b_.type,
    __mro__: [object],
    $infos:{
        __name__: "DOMEvent"
    }
}

DOMEvent.__new__ = function(cls, evt_name){
    var ev = new Event(evt_name)
    ev.__class__ = DOMEvent
    if(ev.preventDefault === undefined){
        ev.preventDefault = function(){ev.returnValue = false}
    }
    if(ev.stopPropagation === undefined){
        ev.stopPropagation = function(){ev.cancelBubble = true}
    }
    return ev
}

function dom2svg(svg_elt, coords){
    // Used to compute the mouse position relatively to the upper left corner
    // of an SVG element, based on the coordinates coords.x, coords.y that are
    // relative to the browser screen.
    var pt = svg_elt.createSVGPoint()
    pt.x = coords.x
    pt.y = coords.y
    return pt.matrixTransform(svg_elt.getScreenCTM().inverse())
}

DOMEvent.__getattribute__ = function(self, attr){
    switch(attr) {
        case '__repr__':
        case '__str__':
            return function(){return '<DOMEvent object>'}
        case 'x':
            return $mouseCoords(self).x
        case 'y':
            return $mouseCoords(self).y
        case 'data':
            if(self.dataTransfer !== undefined){
                return Clipboard.$factory(self.dataTransfer)
            }
            return self['data']
        case 'target':
            if(self.target !== undefined){
                return DOMNode.$factory(self.target)
            }
        case 'char':
            return String.fromCharCode(self.which)
        case 'svgX':
            if(self.target instanceof SVGSVGElement){
                return Math.floor(dom2svg(self.target, $mouseCoords(self)).x)
            }
            throw _b_.AttributeError.$factory("event target is not an SVG " +
                "element")
        case 'svgY':
            if(self.target instanceof SVGSVGElement){
                return Math.floor(dom2svg(self.target, $mouseCoords(self)).y)
            }
            throw _b_.AttributeError.$factory("event target is not an SVG " +
                "element")
    }

    var res =  self[attr]
    if(res !== undefined){
        if(typeof res == "function"){
            var func = function(){
                var args = []
                for(var i = 0; i < arguments.length; i++){
                    args.push($B.pyobj2jsobj(arguments[i]))
                }
                return res.apply(self, arguments)
            }
            func.$infos = {
                __name__: res.name,
                __qualname__: res.name
            }
            return func
        }
        return $B.$JS2Py(res)
    }
    throw _b_.AttributeError.$factory("object DOMEvent has no attribute '" +
        attr + "'")
}

DOMEvent.$factory = function(evt_name){
    // Factory to create instances of DOMEvent, based on an event name
    return DOMEvent.__new__(DOMEvent, evt_name)
}

// Function to transform a DOM event into an instance of DOMEvent
var $DOMEvent = $B.$DOMEvent = function(ev){
    ev.__class__ = DOMEvent
    if(ev.preventDefault === undefined){
        ev.preventDefault = function(){ev.returnValue = false}
    }
    if(ev.stopPropagation === undefined){
        ev.stopPropagation = function(){ev.cancelBubble = true}
    }
    return ev
}

$B.set_func_names(DOMEvent, "<dom>")

var Clipboard = {
    __class__: _b_.type,
    $infos: {
        __module__: "<pydom>",
        __name__: "Clipboard"
    }
}

Clipboard.__getitem__ = function(self, name){
    return self.data.getData(name)
}

Clipboard.__mro__ = [object]

Clipboard.__setitem__ = function(self, name, value){
    self.data.setData(name, value)
}

Clipboard.$factory = function(data){ // drag and drop dataTransfer
    return {
        __class__ : Clipboard,
        __dict__: _b_.dict.$factory(),
        data : data
    }
}

$B.set_func_names(Clipboard, "<dom>")

function $EventsList(elt, evt, arg){
    // handles a list of callback fuctions for the event evt of element elt
    // method .remove(callback) removes the callback from the list, and
    // removes the event listener
    this.elt = elt
    this.evt = evt
    if(isintance(arg, list)){this.callbacks = arg}
    else{this.callbacks = [arg]}
    this.remove = function(callback){
        var found = false
        for(var i = 0; i < this.callbacks.length; i++){
            if(this.callbacks[i] === callback){
                found = true
                this.callback.splice(i, 1)
                this.elt.removeEventListener(this.evt, callback, false)
                break
            }
        }
        if(! found){throw _b_.KeyError.$factory("not found")}
    }
}

var OpenFile = $B.OpenFile = {
    __class__: _b_.type,  // metaclass type
    __mro__: [object],
    $infos: {
        __module__: "<pydom>",
        __name__: "OpenFile"
    }
}

OpenFile.$factory = function(file, mode, encoding) {
    var res = {
        __class__: $OpenFileDict,
        file: file,
        reader: new FileReader()
    }
    if(mode === "r"){
        res.reader.readAsText(file, encoding)
    }else if(mode === "rb"){
        res.reader.readAsBinaryString(file)
    }
    return res
}

OpenFile.__getattr__ = function(self, attr) {
    if(self["get_" + attr] !== undefined){return self["get_" + attr]}
    return self.reader[attr]
}

OpenFile.__setattr__ = function(self, attr, value) {
    var obj = self.reader
    if(attr.substr(0,2) == "on"){ // event
        var callback = function(ev) { return value($DOMEvent(ev)) }
        obj.addEventListener(attr.substr(2), callback)
    }else if("set_" + attr in obj){
        return obj["set_" + attr](value)
    }else if(attr in obj){
        obj[attr] = value
    }else{
        setattr(obj, attr, value)
    }
}

$B.set_func_names(OpenFile, "<dom>")

var dom = {
    File : function(){},
    FileReader : function(){}
    }
dom.File.__class__ = _b_.type
dom.File.__str__ = function(){return "<class 'File'>"}
dom.FileReader.__class__ = _b_.type
dom.FileReader.__str__ = function(){return "<class 'FileReader'>"}

// Class for options in a select box

var Options = {
    __class__: _b_.type,
    __delitem__: function(self, arg){
        self.parent.options.remove(arg.elt)
    },
    __getitem__: function(self, key){
        return DOMNode.$factory(self.parent.options[key])
    },
    __len__: function(self){
        return self.parent.options.length
    },
    __mro__: [object],
    __setattr__: function(self, attr, value){
        self.parent.options[attr] = value
    },
    __setitem__: function(self, attr, value){
        self.parent.options[attr] = $B.$JS2Py(value)
    },
    __str__: function(self){
        return "<object Options wraps " + self.parent.options + ">"
    },
    append: function(self, element){
        self.parent.options.add(element.elt)
    },
    insert: function(self, index, element){
        if(index === undefined){self.parent.options.add(element.elt)}
        else{self.parent.options.add(element.elt, index)}
    },
    item: function(self, index){
        return self.parent.options.item(index)
    },
    namedItem: function(self, name){
        return self.parent.options.namedItem(name)
    },
    remove: function(self, arg){
        self.parent.options.remove(arg.elt)
    },
    $infos: {
        __module__: "<pydom>",
        __name__: "Options"
    }
}

Options.$factory = function(parent){
    return {
        __class__: Options,
        parent: parent
    }
}

$B.set_func_names(Options, "<dom>")

// Class for DOM nodes

var DOMNode = {
    __class__ : _b_.type,
    __mro__: [object],
    $infos: {
        __module__: "<pydom>",
        __name__: "DOMNode"
    }
}

DOMNode.$factory = function(elt, fromtag){
    if(elt.__class__ === DOMNode){return elt}
    if(typeof elt == "number" || typeof elt == "boolean" ||
        typeof elt == "string"){return elt}

    // if none of the above, fromtag determines if the call is made by
    // the tag factory or by any other call to DOMNode
    // if made by tag factory (fromtag will be defined, the value is not
    // important), the regular plain old behavior is retained. Only the
    // return value of a DOMNode is sought

    // In other cases (fromtag is undefined), DOMNode tries to return a "tag"
    // from the browser.html module by looking into "$tags" which is set
    // by the  browser.html module itself (external sources could override
    // it) and piggybacks on the tag factory by adding an "elt_wrap"
    // attribute to the class to let it know, that special behavior
    // is needed. i.e: don't create the element, use the one provided
    if(fromtag === undefined) {
        if(DOMNode.tags !== undefined) {  // tags is a python dictionary
            var tdict = DOMNode.tags.$string_dict
            if(tdict !== undefined) {
                var klass = tdict[elt.tagName]
                if(klass !== undefined) {
                    // all checks are good
                    klass.$elt_wrap = elt  // tell class to wrap element
                    return klass.$factory()  // and return what the factory wants
                }
            }
        }
        // all "else" ... default to old behavior of plain DOMNode wrapping
    }
    if(elt["$brython_id"] === undefined || elt.nodeType == 9){
        // add a unique id for comparisons
        elt.$brython_id = "DOM-" + $B.UUID()
    }

    return {
        __class__: DOMNode,
        elt: elt
    }
}

DOMNode.__add__ = function(self, other){
    // adding another element to self returns an instance of TagSum
    var res = TagSum.$factory()
    res.children = [self], pos = 1
    if(_b_.isinstance(other, TagSum)){
        res.children = res.children.concat(other.children)
    }else if(_b_.isinstance(other,[_b_.str, _b_.int, _b_.float, _b_.list,
                                _b_.dict, _b_.set, _b_.tuple])){
        res.children[pos++] = DOMNode.$factory(
            document.createTextNode(_b_.str.$factory(other)))
    }else if(_b_.isinstance(other, DOMNode)){
        res.children[pos++] = other
    }else{
        // If other is iterable, add all items
        try{res.children = res.children.concat(_b_.list.$factory(other))}
        catch(err){throw _b_.TypeError.$factory("can't add '" +
            $B.class_name(other) + "' object to DOMNode instance")
        }
    }
    return res
}

DOMNode.__bool__ = function(self){return true}

DOMNode.__contains__ = function(self, key){
    // For document, if key is a string, "key in document" tells if an element
    // with id "key" is in the document
    if(self.elt.nodeType == 9 && typeof key == "string"){
        return document.getElementById(key) !== null
    }
    key = key.elt !==undefined ? key.elt : key
    if(self.elt.length !== undefined && typeof self.elt.item == "function"){
        for(var i = 0, len = self.elt.length; i < len; i++){
            if(self.elt.item(i) === key){return true}
        }
    }
    return false
}

DOMNode.__del__ = function(self){
    // if element has a parent, calling __del__ removes object
    // from the parent's children
    if(!self.elt.parentNode){
        throw _b_.ValueError.$factory("can't delete " + _b_.str.$factory(self.elt))
    }
    self.elt.parentNode.removeChild(self.elt)
}

DOMNode.__delitem__ = function(self, key){
    if(self.elt.nodeType == 9){ // document : remove by id
        var res = self.elt.getElementById(key)
        if(res){res.parentNode.removeChild(res)}
        else{throw _b_.KeyError.$factory(key)}
    }else{ // other node : remove by rank in child nodes
        self.elt.parentNode.removeChild(self.elt)
    }
}

DOMNode.__dir__ = function(self){
    var res = []
    // generic DOM attributes
    for(var attr in self.elt){
        if(attr.charAt(0) != "$"){res.push(attr)}
    }
    // Brython-specific attributes
    for(var attr in DOMNode){
        if(attr.charAt(0) != "$" && res.indexOf(attr) == -1){res.push(attr)}
    }
    return res
}

DOMNode.__eq__ = function(self, other){
    return self.elt == other.elt
}

DOMNode.__getattribute__ = function(self, attr){

    if(attr.substr(0, 2) == "$$"){attr = attr.substr(2)}
    switch(attr) {
        case "attrs":
            return Attributes.$factory(self.elt)
        case "class_name":
        case "html":
        case "id":
        case "parent":
        case "query":
        case "text":
            return DOMNode[attr](self)

        case "height":
        case "left":
        case "top":
        case "width":
            // Special case for Canvas
            // http://stackoverflow.com/questions/4938346/canvas-width-and-height-in-html5
            if(self.elt.tagName == "CANVAS" && self.elt[attr]){
                return self.elt[attr]
            }

            if(self.elt instanceof SVGElement){
                return self.elt[attr].baseVal.value
            }
            if(self.elt.style[attr]){
                return parseInt(self.elt.style[attr])
            }else{
                throw _b_.AttributeError.$factory("style." + attr +
                    " is not set for " + _b_.str.$factory(self))
            }
        case "clear":
        case "closest":
            return function(){return DOMNode[attr](self, arguments[0])}
        case "headers":
          if(self.elt.nodeType == 9){
              // HTTP headers
              var req = new XMLHttpRequest();
              req.open("GET", document.location, false)
              req.send(null);
              var headers = req.getAllResponseHeaders()
              headers = headers.split("\r\n")
              var res = _b_.dict.$factory()
              for(var i = 0; i < headers.length; i++){
                  var header = headers[i]
                  if(header.strip().length == 0){continue}
                  var pos = header.search(":")
                  res.__setitem__(header.substr(0, pos),
                      header.substr(pos + 1).lstrip())
              }
              return res
          }
          break
        case "$$location":
            attr = "location"
            break
    }

    // Special case for attribute "select" of INPUT or TEXTAREA tags :
    // they have a "select" methods ; element.select() selects the
    // element text content.
    // Return a function that, if called without arguments, uses this
    // method ; otherwise, uses DOMNode.select
    if(attr == "select" && self.elt.nodeType == 1 &&
            ["INPUT", "TEXTAREA"].indexOf(self.elt.tagName.toUpperCase()) > -1){
        return function(selector){
            if(selector === undefined){self.elt.select(); return _b_.None}
            return DOMNode.select(self, selector)
        }
    }

    // Looking for property. If the attribute is in the forbidden
    // arena ... look for the aliased version
    var property = self.elt[attr]
    if(property === undefined && $B.aliased_names[attr]){
        property = self.elt["$$" + attr]
    }

    if(property === undefined){
        return object.__getattribute__(self, attr)
    }

    var res = property

    if(res !== undefined){
        if(res === null){return _b_.None}
        if(typeof res === "function"){
            var func = (function(f, elt){
                return function(){
                    var args = [], pos = 0
                    for(var i = 0; i < arguments.length; i++){
                        var arg = arguments[i]
                        if(typeof arg == "function"){
                            var f1 = function(dest_fn) { return function(){
                                try{return dest_fn.apply(null, arguments)}
                                catch(err){
                                    console.log(dest_fn, typeof dest_fn, err)
                                    if(err.__class__ !== undefined){
                                        var msg = $B.$getattr(err, 'info') +
                                            '\n' + $B.class_name(err)
                                        if(err.args){msg += ': ' + err.args[0]}
                                        try{$B.$getattr($B.stderr, "write")(msg)}
                                        catch(err){console.log(msg)}
                                    }else{
                                        try{$B.$getattr($B.stderr, "write")(err)}
                                        catch(err1){console.log(err)}
                                    }
                                    throw err
                                }
                            }}(arg)
                            args[pos++] = f1
                        }
                        else if(_b_.isinstance(arg, JSObject)){
                            args[pos++] = arg.js
                        }else if(_b_.isinstance(arg, DOMNode)){
                            args[pos++] = arg.elt
                        }else if(arg === _b_.None){
                            args[pos++] = null
                        }else{
                            args[pos++] = arg
                        }
                    }
                    var result = f.apply(elt, args)
                    return $B.$JS2Py(result)
                }
            })(res, self.elt)
            func.$infos = {__name__ : attr}
            func.$is_func = true
            return func
        }
        if(attr == 'options'){return Options.$factory(self.elt)}
        if(attr == 'style'){return $B.JSObject.$factory(self.elt[attr])}
        if(Array.isArray(res)){return res} // issue #619

        return $B.$JS2Py(res)
    }
    return object.__getattribute__(self, attr)
}

DOMNode.__getitem__ = function(self, key){
    if(self.elt.nodeType == 9){ // Document
        if(typeof key == "string"){
            var res = self.elt.getElementById(key)
            if(res){return DOMNode.$factory(res)}
            throw _b_.KeyError.$factory(key)
        }else{
            try{
                var elts = self.elt.getElementsByTagName(key.$infos.__name__),
                    res = []
                    for(var i = 0; i < elts.length; i++){
                        res.push(DOMNode.$factory(elts[i]))
                    }
                    return res
            }catch(err){
                throw _b_.KeyError.$factory(_b_.str.$factory(key))
            }
        }
    }else{
        if((typeof key == "number" || typeof key == "boolean") &&
            typeof self.elt.item == "function"){
                var key_to_int = _b_.int.$factory(key)
                if(key_to_int < 0){key_to_int += self.elt.length}
                var res = DOMNode.$factory(self.elt.item(key_to_int))
                if(res === undefined){throw _b_.KeyError.$factory(key)}
                return res
        }else if(typeof key == "string" &&
                 self.elt.attributes &&
                 typeof self.elt.attributes.getNamedItem == "function"){
             var attr = self.elt.attributes.getNamedItem(key)
             if(!!attr){return attr.value}
             throw _b_.KeyError.$factory(key)
        }
    }
}

DOMNode.__iter__ = function(self){
    // iteration on a Node
    if(self.elt.length !== undefined && typeof self.elt.item == "function"){
        var items = []
        for(var i = 0, len = self.elt.length; i < len; i++){
            items.push(DOMNode.$factory(self.elt.item(i)))
        }
    }else if(self.elt.childNodes !== undefined){
        var items = []
        for(var i = 0, len = self.elt.childNodes.length; i < len; i++){
            items.push(DOMNode.$factory(self.elt.childNodes[i]))
        }
    }
    return $B.$iter(items)
}

DOMNode.__le__ = function(self, other){
    // for document, append child to document.body
    var elt = self.elt
    if(self.elt.nodeType == 9){elt = self.elt.body}
    if(_b_.isinstance(other, TagSum)){
        for(var i = 0; i < other.children.length; i++){
            elt.appendChild(other.children[i].elt)
        }
    }else if(typeof other == "string" || typeof other == "number"){
        var $txt = document.createTextNode(other.toString())
        elt.appendChild($txt)
    }else if(_b_.isinstance(other, DOMNode)){
        // other is a DOMNode instance
        elt.appendChild(other.elt)
    }else{
        try{
            // If other is an iterable, add the items
            var items = _b_.list.$factory(other)
            items.forEach(function(item){
                DOMNode.__le__(self, item)
            })
        }catch(err){
            throw _b_.TypeError.$factory("can't add '" +
                $B.class_name(other) + "' object to DOMNode instance")
        }
    }
}

DOMNode.__len__ = function(self){return self.elt.length}

DOMNode.__mul__ = function(self,other){
    if(_b_.isinstance(other, _b_.int) && other.valueOf() > 0){
        var res = TagSum.$factory()
        var pos = res.children.length
        for(var i = 0; i < other.valueOf(); i++){
            res.children[pos++] = DOMNode.clone(self)()
        }
        return res
    }
    throw _b_.ValueError.$factory("can't multiply " + self.__class__ +
        "by " + other)
}

DOMNode.__ne__ = function(self, other){return ! DOMNode.__eq__(self, other)}

DOMNode.__next__ = function(self){
   self.$counter++
   if(self.$counter < self.elt.childNodes.length){
       return DOMNode.$factory(self.elt.childNodes[self.$counter])
   }
   throw _b_.StopIteration.$factory("StopIteration")
}

DOMNode.__radd__ = function(self, other){ // add to a string
    var res = TagSum.$factory()
    var txt = DOMNode.$factory(document.createTextNode(other))
    res.children = [txt, self]
    return res
}

DOMNode.__str__ = DOMNode.__repr__ = function(self){
    var proto = Object.getPrototypeOf(self.elt)
    if(proto){
        var name = proto.constructor.name
        if(name === undefined){ // IE
            var proto_str = proto.constructor.toString()
            name = proto_str.substring(8, proto_str.length - 1)
        }
        return "<" + name + " object>"
    }
    var res = "<DOMNode object type '"
    return res + $NodeTypes[self.elt.nodeType] + "' name '" +
        self.elt.nodeName + "'>"
}

DOMNode.__setattr__ = function(self, attr, value){
    // Sets the *property* attr of the underlying element (not its
    // *attribute*)

    if(attr.substr(0,2) == "on"){ // event
        if(!$B.$bool(value)){ // remove all callbacks attached to event
            DOMNode.unbind(self, attr.substr(2))
        }else{
            // value is a function taking an event as argument
            DOMNode.bind(self, attr.substr(2), value)
        }
    }else{
        switch(attr){
            case "left":
            case "top":
            case "width":
            case "height":
                if(_b_.isinstance(value, _b_.int) && self.elt.nodeType == 1){
                    self.elt.style[attr] = value + "px"
                    return _b_.None
                }else{
                    throw _b_.ValueError.$factory(attr + " value should be" +
                        " an integer, not " + $B.class_name(value))
                }
                break
        }
        if(DOMNode["set_" + attr] !== undefined) {
          return DOMNode["set_" + attr](self, value)
        }

        function warn(msg){
            console.log(msg)
            var frame = $B.last($B.frames_stack)
            if($B.debug > 0){
                var info = frame[1].$line_info.split(",")
                console.log("module", info[1], "line", info[0])
                if($B.$py_src.hasOwnProperty(info[1])){
                    var src = $B.$py_src[info[1]]
                    console.log(src.split("\n")[parseInt(info[0]) - 1])
                }
            }else{
                console.log("module", frame[2])
            }
        }

        // Warns if attr is a descriptor of the element's prototype
        // and it is not writable
        var proto = Object.getPrototypeOf(self.elt),
            nb = 0
        while(!!proto && proto !== Object.prototype && nb++ < 10){
            var descriptors = Object.getOwnPropertyDescriptors(proto)
            if(!!descriptors &&
                    typeof descriptors.hasOwnProperty == "function"){
                if(descriptors.hasOwnProperty(attr)){
                    if(!descriptors[attr].writable &&
                            descriptors[attr].set === undefined){
                        warn("Warning: property '" + attr +
                            "' is not writable. Use element.attrs['" +
                            attr +"'] instead.")
                    }
                    break
                }
            }else{
                break
            }
            proto = Object.getPrototypeOf(proto)
        }

        // Warns if attribute is a property of style
        if(self.elt.style && self.elt.style[attr] !== undefined){
            warn("Warning: '" + attr + "' is a property of element.style")
        }

        // Set the property
        self.elt[attr] = value

        return _b_.None
    }
}

DOMNode.__setitem__ = function(self, key, value){
    if(typeof key == "number"){
        self.elt.childNodes[key] = value
    }else if(typeof key == "string"){
        if(self.elt.attributes){
            if(self.elt instanceof SVGElement){
                self.elt.setAttributeNS(null, key, value)
            }else if(typeof self.elt.setAttribute == "function"){
                self.elt.setAttribute(key, value)
            }
        }
    }
}

DOMNode.abs_left = {
    __get__: function(self){
        return $getPosition(self.elt).left
    },
    __set__: function(){
        throw _b_.AttributeError.$factory("'DOMNode' objectattribute " +
            "'abs_left' is read-only")
    }
}

DOMNode.abs_top = {
    __get__: function(self){
        return $getPosition(self.elt).top
    },
    __set__: function(){
        throw _b_.AttributeError.$factory("'DOMNode' objectattribute " +
            "'abs_top' is read-only")
    }
}

DOMNode.bind = function(self, event){
    // bind functions to the event (event = "click", "mouseover" etc.)
    var $ = $B.args("bind", 4,
            {self: null, event: null, func: null, options: null},
            ["self", "event", "func", "options"], arguments,
            {options: _b_.None}, null, null),
            self = $.self,
            event = $.event,
            func = $.func,
            options = $.options

    var callback = (function(f){
        return function(ev){
            try{
                return f($DOMEvent(ev))
            }catch(err){
                if(err.__class__ !== undefined){
                    var msg = $B.$getattr(err, "info") +
                        "\n" + $B.class_name(err)
                    if(err.args){msg += ": " + err.args[0]}
                    try{$B.$getattr($B.stderr, "write")(msg)}
                    catch(err){console.log(msg)}
                }else{
                    try{$B.$getattr($B.stderr, "write")(err)}
                    catch(err1){console.log(err)}
                }
            }
        }}
    )(func)
    callback.$infos = func.$infos
    callback.$attrs = func.$attrs || {}
    callback.$func = func
    if(typeof options == "boolean"){
        self.elt.addEventListener(event, callback, options)
    }else if(options.__class__ === _b_.dict){
        self.elt.addEventListener(event, callback, options.$string_dict)
    }else if(options === _b_.None){
        self.elt.addEventListener(event, callback, false)
    }
    self.elt.$events = self.elt.$events || {}
    self.elt.$events[event] = self.elt.$events[event] || []
    self.elt.$events[event].push([func, callback])
    return self
}

DOMNode.children = function(self){
    var res = [],
        elt = self.elt
    console.log(elt, elt.childNodes)
    if(elt.nodeType == 9){elt = elt.body}
    elt.childNodes.forEach(function(child){
        res.push(DOMNode.$factory(child))
    })
    return res
}

DOMNode.clear = function(self){
    // remove all children elements
    var elt = self.elt
    if(elt.nodeType == 9){elt = elt.body}
    while(elt.firstChild){
       elt.removeChild(elt.firstChild)
    }
}

DOMNode.Class = function(self){
    if(self.elt.className !== undefined){return self.elt.className}
    return _b_.None
}

DOMNode.class_name = function(self){return DOMNode.Class(self)}

DOMNode.clone = function(self){
    var res = DOMNode.$factory(self.elt.cloneNode(true))

    // bind events on clone to the same callbacks as self
    var events = self.elt.$events || {}
    for(var event in events){
        var evt_list = events[event]
        evt_list.forEach(function(evt){
            var func = evt[0]
            DOMNode.bind(res, event, func)
        })
    }
    return res
}

DOMNode.closest = function(self, tagName){
    // Returns the first parent of self with specified tagName
    // Raises KeyError if not found
    var res = self.elt,
        tagName = tagName.toLowerCase()
    while(res.tagName.toLowerCase() != tagName){
        res = res.parentNode
        if(res === undefined || res.tagName === undefined){
            throw _b_.KeyError.$factory("no parent of type " + tagName)
        }
    }
    return DOMNode.$factory(res)
}

DOMNode.events = function(self, event){
    self.elt.$events = self.elt.$events || {}
    var evt_list = self.elt.$events[event] = self.elt.$events[event] || [],
        callbacks = []
    evt_list.forEach(function(evt){
        callbacks.push(evt[1])
    })
    return callbacks
}

DOMNode.focus = function(self){
    return (function(obj){
        return function(){
            // focus() is not supported in IE
            setTimeout(function(){obj.focus()}, 10)
        }
    })(self.elt)
}

function make_list(node_list){
    var res = []
    for(var i = 0; i < node_list.length; i++){
        res.push(DOMNode.$factory(node_list[i]))
    }
    return res
}

DOMNode.get = function(self){
    // for document : doc.get(key1=value1[,key2=value2...]) returns a list of the elements
    // with specified keys/values
    // key can be 'id','name' or 'selector'
    var obj = self.elt,
        args = []
    for(var i = 1; i < arguments.length; i++){args.push(arguments[i])}
    var $ns = $B.args("get", 0, {}, [], args, {}, null, "kw"),
        $dict = {},
        items = _b_.list.$factory(_b_.dict.items($ns["kw"]))
    items.forEach(function(item){
        $dict[item[0]] = item[1]
    })
    if($dict["name"] !== undefined){
        if(obj.getElementsByName === undefined){
            throw _b_.TypeError.$factory("DOMNode object doesn't support " +
                "selection by name")
        }
        return make_list(obj.getElementsByName($dict['name']))
    }
    if($dict["tag"] !== undefined){
        if(obj.getElementsByTagName === undefined){
            throw _b_.TypeError.$factory("DOMNode object doesn't support " +
                "selection by tag name")
        }
        return make_list(obj.getElementsByTagName($dict["tag"]))
    }
    if($dict["classname"] !== undefined){
        if(obj.getElementsByClassName === undefined){
            throw _b_.TypeError.$factory("DOMNode object doesn't support " +
                "selection by class name")
        }
        return make_list(obj.getElementsByClassName($dict['classname']))
    }
    if($dict["id"] !== undefined){
        if(obj.getElementById === undefined){
            throw _b_.TypeError.$factory("DOMNode object doesn't support " +
                "selection by id")
        }
        var id_res = document.getElementById($dict['id'])
        if(! id_res){return []}
        return [DOMNode.$factory(id_res)]
    }
    if($dict["selector"] !== undefined){
        if(obj.querySelectorAll === undefined){
            throw _b_.TypeError.$factory("DOMNode object doesn't support " +
                "selection by selector")
        }
        return make_list(obj.querySelectorAll($dict['selector']))
    }
    return res
}

DOMNode.getContext = function(self){ // for CANVAS tag
    if(!("getContext" in self.elt)){
      throw _b_.AttributeError.$factory("object has no attribute 'getContext'")
    }
    var obj = self.elt
    return function(ctx){return JSObject.$factory(obj.getContext(ctx))}
}

DOMNode.getSelectionRange = function(self){ // for TEXTAREA
    if(self.elt["getSelectionRange"] !== undefined){
        return self.elt.getSelectionRange.apply(null, arguments)
    }
}

DOMNode.html = function(self){
    var res = self.elt.innerHTML
    if(res === undefined){
        if(self.elt.nodeType == 9){res = self.elt.body.innerHTML}
        else{res = _b_.None}
    }
    return res
}

DOMNode.id = function(self){
    if(self.elt.id !== undefined){return self.elt.id}
    return _b_.None
}

DOMNode.index = function(self, selector){
    var items
    if(selector === undefined){
        items = self.elt.parentElement.childNodes
    }else{
        items = self.elt.parentElement.querySelectorAll(selector)
    }
    var rank = -1
    for(var i = 0; i < items.length; i++){
        if(items[i] === self.elt){rank = i; break}
    }
    return rank
}

DOMNode.inside = function(self, other){
    // Test if a node is inside another node
    other = other.elt
    var elt = self.elt
    while(true){
        if(other === elt){return true}
        elt = elt.parentElement
        if(! elt){return false}
    }
}

DOMNode.options = function(self){ // for SELECT tag
    return new $OptionsClass(self.elt)
}

DOMNode.parent = function(self){
    if(self.elt.parentElement){
        return DOMNode.$factory(self.elt.parentElement)
    }
    return _b_.None
}

DOMNode.reset = function(self){ // for FORM
    return function(){self.elt.reset()}
}

DOMNode.select = function(self, selector){
    // alias for get(selector=...)
    if(self.elt.querySelectorAll === undefined){
        throw _b_.TypeError.$factory("DOMNode object doesn't support " +
            "selection by selector")
    }
    return make_list(self.elt.querySelectorAll(selector))
}

DOMNode.select_one = function(self, selector){
    // return the element matching selector, or None
    if(self.elt.querySelector === undefined){
        throw _b_.TypeError.$factory("DOMNode object doesn't support " +
            "selection by selector")
    }
    var res = self.elt.querySelector(selector)
    if(res === null) {
        return _b_.None
    }
    return DOMNode.$factory(res)
}

DOMNode.style = function(self){
    // set attribute "float" for cross-browser compatibility
    self.elt.style.float = self.elt.style.cssFloat || self.style.styleFloat
    return $B.JSObject.$factory(self.elt.style)
}

DOMNode.setSelectionRange = function(self){ // for TEXTAREA
    if(this["setSelectionRange"] !== undefined){
        return (function(obj){
            return function(){
                return obj.setSelectionRange.apply(obj, arguments)
            }})(this)
    }else if (this["createTextRange"] !== undefined){
        return (function(obj){
            return function(start_pos, end_pos){
                if(end_pos == undefined){end_pos = start_pos}
            var range = obj.createTextRange()
            range.collapse(true)
            range.moveEnd("character", start_pos)
            range.moveStart("character", end_pos)
            range.select()
                }
        })(this)
    }
}

DOMNode.set_class_name = function(self, arg){
    self.elt.setAttribute("class", arg)
}

DOMNode.set_html = function(self, value){
    var elt = self.elt
    if(elt.nodeType == 9){elt = elt.body}
    elt.innerHTML = _b_.str.$factory(value)
}

DOMNode.set_style = function(self, style){ // style is a dict
    if(!_b_.isinstance(style, _b_.dict)){
        throw _b_.TypeError.$factory("style must be dict, not " +
            $B.class_name(style))
    }
    var items = _b_.list.$factory(_b_.dict.items(style))
    for(var i = 0; i < items.length; i++){
        var key = items[i][0],
            value = items[i][1]
        if(key.toLowerCase() == "float"){
            self.elt.style.cssFloat = value
            self.elt.style.styleFloat = value
        }else{
            switch(key) {
                case "top":
                case "left":
                case "width":
                case "borderWidth":
                    if(_b_.isinstance(value,_b_.int)){value = value + "px"}
            }
            self.elt.style[key] = value
        }
    }
}

DOMNode.set_text = function(self,value){
    var elt = self.elt
    if(elt.nodeType == 9){elt = elt.body}
    elt.innerText = _b_.str.$factory(value)
    elt.textContent = _b_.str.$factory(value)
}

DOMNode.set_value = function(self, value){self.elt.value = _b_.str.$factory(value)}

DOMNode.submit = function(self){ // for FORM
    return function(){self.elt.submit()}
}

DOMNode.text = function(self){
    var elt = self.elt
    if(elt.nodeType == 9){elt = elt.body}
    var res = elt.innerText || elt.textContent
    if(res === null){
        res = _b_.None
    }
    return res
}

DOMNode.toString = function(self){
    if(self === undefined){return 'DOMNode'}
    return self.elt.nodeName
}

DOMNode.trigger = function (self, etype){
    // Artificially triggers the event type provided for this DOMNode
    if(self.elt.fireEvent){
      self.elt.fireEvent("on" + etype)
    }else{
      var evObj = document.createEvent("Events")
      evObj.initEvent(etype, true, false)
      self.elt.dispatchEvent(evObj)
    }
}

DOMNode.unbind = function(self, event){
    // unbind functions from the event (event = "click", "mouseover" etc.)
    // if no function is specified, remove all callback functions
    // If no event is specified, remove all callbacks for all events
    self.elt.$events = self.elt.$events || {}
    if(self.elt.$events === {}){return _b_.None}

    if(event === undefined){
        for(var event in self.elt.$events){
            DOMNode.unbind(self, event)
        }
        return _b_.None
    }

    if(self.elt.$events[event] === undefined ||
            self.elt.$events[event].length == 0){
        return _b_.None
    }

    var events = self.elt.$events[event]
    if(arguments.length == 2){
        // remove all callback functions
        for(var i = 0; i < events.length; i++){
            var callback = events[i][1]
            self.elt.removeEventListener(event, callback, false)
        }
        self.elt.$events[event] = []
        return _b_.None
    }

    for(var i = 2; i < arguments.length; i++){
        var callback = arguments[i],
            flag = false,
            func = callback.$func
        if(func === undefined){
            // If a callback is created by an assignment to an existing
            // function
            var found = false
            for(var j = 0; j < events.length; j++){
                if(events[j][0] === callback){
                    var func = callback,
                        found = true
                    break
                }
            }
            if(!found){
                throw _b_.TypeError.$factory("function is not an event callback")
            }
        }
        for(var j = 0; j < events.length; j++){
            if($B.$getattr(func, '__eq__')(events[j][0])){
                var callback = events[j][1]
                self.elt.removeEventListener(event, callback, false)
                events.splice(j, 1)
                flag = true
                break
            }
        }
        // The indicated func was not found, error is thrown
        if(!flag){
            throw _b_.KeyError.$factory('missing callback for event ' + event)
        }
    }
}

$B.set_func_names(DOMNode, "<dom>")

// return query string as an object with methods to access keys and values
// same interface as cgi.FieldStorage, with getvalue / getlist / getfirst
var Query = {
    __class__: _b_.type,
    $infos:{
        __name__: "query"
    }
}

Query.__contains__ = function(self, key){
    return self._keys.indexOf(key) > -1
}

Query.__getitem__ = function(self, key){
    // returns a single value or a list of values
    // associated with key, or raise KeyError
    var result = self._values[key]
    if(result === undefined){throw _b_.KeyError.$factory(key)}
    if(result.length == 1){return result[0]}
    return result
}

var Query_iterator = $B.make_iterator_class("query string iterator")
Query.__iter__ = function(self){
    return Query_iterator.$factory(self._keys)
}

Query.__mro__ = [object]

Query.getfirst = function(self, key, _default){
    // returns the first value associated with key
    var result = self._values[key]
    if(result === undefined){
       if(_default === undefined){return _b_.None}
       return _default
    }
    return result[0]
}

Query.getlist = function(self, key){
    // always return a list
    var result = self._values[key]
    if(result === undefined){return []}
    return result
}

Query.getvalue = function(self, key, _default){
    try{return Query.__getitem__(self, key)}
    catch(err){
        if(_default === undefined){return _b_.None}
        return _default
    }
}

Query.keys = function(self){return self._keys}

DOMNode.query = function(self){

    var res = {
        __class__: Query,
        _keys : [],
        _values : {}
    }
    var qs = location.search.substr(1).split('&')
    for(var i = 0; i < qs.length; i++){
        var pos = qs[i].search("="),
            elts = [qs[i].substr(0,pos),qs[i].substr(pos + 1)],
            key = decodeURIComponent(elts[0]),
            value = decodeURIComponent(elts[1])
        if(res._keys.indexOf(key) > -1){res._values[key].push(value)}
        else{
            res._keys.push(key)
            res._values[key] = [value]
        }
    }

    return res
}

// class used for tag sums
var TagSum = {
    __class__ : _b_.type,
    __mro__: [object],
    $infos: {
        __module__: "<pydom>",
        __name__: "TagSum"
    }
}

TagSum.appendChild = function(self, child){
    self.children.push(child)
}

TagSum.__add__ = function(self, other){
    if($B.get_class(other) === TagSum){
        self.children = self.children.concat(other.children)
    }else if(_b_.isinstance(other, [_b_.str, _b_.int, _b_.float,
                               _b_.dict, _b_.set, _b_.list])){
        self.children = self.children.concat(
            DOMNode.$factory(document.createTextNode(other)))
    }else{self.children.push(other)}
    return self
}

TagSum.__radd__ = function(self, other){
    var res = TagSum.$factory()
    res.children = self.children.concat(
        DOMNode.$factory(document.createTextNode(other)))
    return res
}

TagSum.__repr__ = function(self){
    var res = "<object TagSum> "
    for(var i = 0; i < self.children.length; i++){
        res += self.children[i]
        if(self.children[i].toString() == "[object Text]"){
            res += " [" + self.children[i].textContent + "]\n"
        }
    }
    return res
}

TagSum.__str__ = TagSum.toString = TagSum.__repr__

TagSum.clone = function(self){
    var res = TagSum.$factory()
    for(var i = 0; i < self.children.length; i++){
        res.children.push(self.children[i].cloneNode(true))
    }
    return res
}

TagSum.$factory = function(){
    return {
        __class__: TagSum,
        children: [],
        toString: function(){return "(TagSum)"}
    }
}

$B.set_func_names(TagSum, "<dom>")

$B.TagSum = TagSum // used in _html.js and _svg.js

var win = JSObject.$factory(_window) //{__class__:$WinDict}

win.get_postMessage = function(msg,targetOrigin){
    if(_b_.isinstance(msg, dict)){
        var temp = {__class__:"dict"},
            items = _b_.list.$factory(_b_.dict.items(msg))
        items.forEach(function(item){
            temp[item[0]] = item[1]
        })
        msg = temp
    }
    return _window.postMessage(msg, targetOrigin)
}

$B.DOMNode = DOMNode

$B.win = win
})(__BRYTHON__)