<commit_before>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const { helper, assert } = require('./helper');
const {Target} = require('./Target');
const EventEmitter = require('events');
const {TaskQueue} = require('./TaskQueue');
const {Events} = require('./Events');

class Browser extends EventEmitter {
  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Array<string>} contextIds
   * @param {boolean} ignoreHTTPSErrors
   * @param {?Puppeteer.Viewport} defaultViewport
   * @param {?Puppeteer.ChildProcess} process
   * @param {(function():Promise)=} closeCallback
   */
  constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
    super();
    this._ignoreHTTPSErrors = ignoreHTTPSErrors;
    this._defaultViewport = defaultViewport;
    this._process = process;
    this._screenshotTaskQueue = new TaskQueue();
    this._connection = connection;
    this._closeCallback = closeCallback || new Function();

    this._defaultContext = new BrowserContext(this._connection, this, null);
    /** @type {Map<string, BrowserContext>} */
    this._contexts = new Map();
    for (const contextId of contextIds)
      this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId));

    /** @type {Map<string, Target>} */
    this._targets = new Map();
    this._connection.on(Events.Connection.Disconnected, () => this.emit(Events.Browser.Disconnected));
    this._connection.on('Target.targetCreated', this._targetCreated.bind(this));
    this._connection.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
    this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
  }

  /**
   * @return {?Puppeteer.ChildProcess}
   */
  process() {
    return this._process;
  }

  /**
   * @return {!Promise<!BrowserContext>}
   */
  async createIncognitoBrowserContext() {
    const {browserContextId} = await this._connection.send('Target.createBrowserContext');
    const context = new BrowserContext(this._connection, this, browserContextId);
    this._contexts.set(browserContextId, context);
    return context;
  }

  /**
   * @return {!Array<!BrowserContext>}
   */
  browserContexts() {
    return [this._defaultContext, ...Array.from(this._contexts.values())];
  }

  /**
   * @return {!BrowserContext}
   */
  defaultBrowserContext() {
    return this._defaultContext;
  }

  /**
   * @param {?string} contextId
   */
  async _disposeContext(contextId) {
    await this._connection.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
    this._contexts.delete(contextId);
  }

  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Array<string>} contextIds
   * @param {boolean} ignoreHTTPSErrors
   * @param {?Puppeteer.Viewport} defaultViewport
   * @param {?Puppeteer.ChildProcess} process
   * @param {function()=} closeCallback
   */
  static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
    const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
    await connection.send('Target.setDiscoverTargets', {discover: true});
    return browser;
  }

  /**
   * @param {!Protocol.Target.targetCreatedPayload} event
   */
  async _targetCreated(event) {
    const targetInfo = event.targetInfo;
    const {browserContextId} = targetInfo;
    const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;

    const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue);
    assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
    this._targets.set(event.targetInfo.targetId, target);

    if (await target._initializedPromise) {
      this.emit(Events.Browser.TargetCreated, target);
      context.emit(Events.BrowserContext.TargetCreated, target);
    }
  }

  /**
   * @param {{targetId: string}} event
   */
  async _targetDestroyed(event) {
    const target = this._targets.get(event.targetId);
    target._initializedCallback(false);
    this._targets.delete(event.targetId);
    target._closedCallback();
    if (await target._initializedPromise) {
      this.emit(Events.Browser.TargetDestroyed, target);
      target.browserContext().emit(Events.BrowserContext.TargetDestroyed, target);
    }
  }

  /**
   * @param {!Protocol.Target.targetInfoChangedPayload} event
   */
  _targetInfoChanged(event) {
    const target = this._targets.get(event.targetInfo.targetId);
    assert(target, 'target should exist before targetInfoChanged');
    const previousURL = target.url();
    const wasInitialized = target._isInitialized;
    target._targetInfoChanged(event.targetInfo);
    if (wasInitialized && previousURL !== target.url()) {
      this.emit(Events.Browser.TargetChanged, target);
      target.browserContext().emit(Events.BrowserContext.TargetChanged, target);
    }
  }

  /**
   * @return {string}
   */
  wsEndpoint() {
    return this._connection.url();
  }

  /**
   * @return {!Promise<!Puppeteer.Page>}
   */
  async newPage() {
    return this._defaultContext.newPage();
  }

  /**
   * @param {?string} contextId
   * @return {!Promise<!Puppeteer.Page>}
   */
  async _createPageInContext(contextId) {
    const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined});
    const target = await this._targets.get(targetId);
    assert(await target._initializedPromise, 'Failed to create target for page');
    const page = await target.page();
    return page;
  }

  /**
   * @return {!Array<!Target>}
   */
  targets() {
    return Array.from(this._targets.values()).filter(target => target._isInitialized);
  }

  /**
   * @return {!Target}
   */
  target() {
    return this.targets().find(target => target.type() === 'browser');
  }

  /**
   * @param {function(!Target):boolean} predicate
   * @param {{timeout?: number}=} options
   * @return {!Promise<!Target>}
   */
  async waitForTarget(predicate, options = {}) {
    const {
      timeout = 30000
    } = options;
    const existingTarget = this.targets().find(predicate);
    if (existingTarget)
      return existingTarget;
    let resolve;
    const targetPromise = new Promise(x => resolve = x);
    this.on(Events.Browser.TargetCreated, check);
    this.on(Events.Browser.TargetChanged, check);
    try {
      if (!timeout)
        return await targetPromise;
      return await helper.waitWithTimeout(targetPromise, 'target', timeout);
    } finally {
      this.removeListener(Events.Browser.TargetCreated, check);
      this.removeListener(Events.Browser.TargetChanged, check);
    }

    /**
     * @param {!Target} target
     */
    function check(target) {
      if (predicate(target))
        resolve(target);
    }
  }

  /**
   * @return {!Promise<!Array<!Puppeteer.Page>>}
   */
  async pages() {
    const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
    // Flatten array.
    return contextPages.reduce((acc, x) => acc.concat(x), []);
  }

  /**
   * @return {!Promise<string>}
   */
  async version() {
    const version = await this._getVersion();
    return version.product;
  }

  /**
   * @return {!Promise<string>}
   */
  async userAgent() {
    const version = await this._getVersion();
    return version.userAgent;
  }

  async close() {
    await this._closeCallback.call(null);
    this.disconnect();
  }

  disconnect() {
    this._connection.dispose();
  }

  /**
   * @return {!Promise<!Object>}
   */
  _getVersion() {
    return this._connection.send('Browser.getVersion');
  }
}

class BrowserContext extends EventEmitter {
  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Browser} browser
   * @param {?string} contextId
   */
  constructor(connection, browser, contextId) {
    super();
    this._connection = connection;
    this._browser = browser;
    this._id = contextId;
  }

  /**
   * @return {!Array<!Target>} target
   */
  targets() {
    return this._browser.targets().filter(target => target.browserContext() === this);
  }

  /**
   * @param {function(!Target):boolean} predicate
   * @param {{timeout?: number}=} options
   * @return {!Promise<!Target>}
   */
  waitForTarget(predicate, options) {
    return this._browser.waitForTarget(target => target.browserContext() === this && predicate(target), options);
  }

  /**
   * @return {!Promise<!Array<!Puppeteer.Page>>}
   */
  async pages() {
    const pages = await Promise.all(
        this.targets()
            .filter(target => target.type() === 'page')
            .map(target => target.page())
    );
    return pages.filter(page => !!page);
  }

  /**
   * @return {boolean}
   */
  isIncognito() {
    return !!this._id;
  }

  /**
   * @param {string} origin
   * @param {!Array<string>} permissions
   */
  async overridePermissions(origin, permissions) {
    const webPermissionToProtocol = new Map([
      ['geolocation', 'geolocation'],
      ['midi', 'midi'],
      ['notifications', 'notifications'],
      ['push', 'push'],
      ['camera', 'videoCapture'],
      ['microphone', 'audioCapture'],
      ['background-sync', 'backgroundSync'],
      ['ambient-light-sensor', 'sensors'],
      ['accelerometer', 'sensors'],
      ['gyroscope', 'sensors'],
      ['magnetometer', 'sensors'],
      ['accessibility-events', 'accessibilityEvents'],
      ['clipboard-read', 'clipboardRead'],
      ['clipboard-write', 'clipboardWrite'],
      ['payment-handler', 'paymentHandler'],
      // chrome-specific permissions we have.
      ['midi-sysex', 'midiSysex'],
    ]);
    permissions = permissions.map(permission => {
      const protocolPermission = webPermissionToProtocol.get(permission);
      if (!protocolPermission)
        throw new Error('Unknown permission: ' + permission);
      return protocolPermission;
    });
    await this._connection.send('Browser.grantPermissions', {origin, browserContextId: this._id || undefined, permissions});
  }

  async clearPermissionOverrides() {
    await this._connection.send('Browser.resetPermissions', {browserContextId: this._id || undefined});
  }

  /**
   * @return {!Promise<!Puppeteer.Page>}
   */
  newPage() {
    return this._browser._createPageInContext(this._id);
  }

  /**
   * @return {!Browser}
   */
  browser() {
    return this._browser;
  }

  async close() {
    assert(this._id, 'Non-incognito profiles cannot be closed!');
    await this._browser._disposeContext(this._id);
  }
}

module.exports = {Browser, BrowserContext};
<commit_msg>chore: move static constructor to the top (#3873)<commit_after>/**
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

const { helper, assert } = require('./helper');
const {Target} = require('./Target');
const EventEmitter = require('events');
const {TaskQueue} = require('./TaskQueue');
const {Events} = require('./Events');

class Browser extends EventEmitter {
  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Array<string>} contextIds
   * @param {boolean} ignoreHTTPSErrors
   * @param {?Puppeteer.Viewport} defaultViewport
   * @param {?Puppeteer.ChildProcess} process
   * @param {function()=} closeCallback
   */
  static async create(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
    const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
    await connection.send('Target.setDiscoverTargets', {discover: true});
    return browser;
  }

  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Array<string>} contextIds
   * @param {boolean} ignoreHTTPSErrors
   * @param {?Puppeteer.Viewport} defaultViewport
   * @param {?Puppeteer.ChildProcess} process
   * @param {(function():Promise)=} closeCallback
   */
  constructor(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback) {
    super();
    this._ignoreHTTPSErrors = ignoreHTTPSErrors;
    this._defaultViewport = defaultViewport;
    this._process = process;
    this._screenshotTaskQueue = new TaskQueue();
    this._connection = connection;
    this._closeCallback = closeCallback || new Function();

    this._defaultContext = new BrowserContext(this._connection, this, null);
    /** @type {Map<string, BrowserContext>} */
    this._contexts = new Map();
    for (const contextId of contextIds)
      this._contexts.set(contextId, new BrowserContext(this._connection, this, contextId));

    /** @type {Map<string, Target>} */
    this._targets = new Map();
    this._connection.on(Events.Connection.Disconnected, () => this.emit(Events.Browser.Disconnected));
    this._connection.on('Target.targetCreated', this._targetCreated.bind(this));
    this._connection.on('Target.targetDestroyed', this._targetDestroyed.bind(this));
    this._connection.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
  }

  /**
   * @return {?Puppeteer.ChildProcess}
   */
  process() {
    return this._process;
  }

  /**
   * @return {!Promise<!BrowserContext>}
   */
  async createIncognitoBrowserContext() {
    const {browserContextId} = await this._connection.send('Target.createBrowserContext');
    const context = new BrowserContext(this._connection, this, browserContextId);
    this._contexts.set(browserContextId, context);
    return context;
  }

  /**
   * @return {!Array<!BrowserContext>}
   */
  browserContexts() {
    return [this._defaultContext, ...Array.from(this._contexts.values())];
  }

  /**
   * @return {!BrowserContext}
   */
  defaultBrowserContext() {
    return this._defaultContext;
  }

  /**
   * @param {?string} contextId
   */
  async _disposeContext(contextId) {
    await this._connection.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
    this._contexts.delete(contextId);
  }

  /**
   * @param {!Protocol.Target.targetCreatedPayload} event
   */
  async _targetCreated(event) {
    const targetInfo = event.targetInfo;
    const {browserContextId} = targetInfo;
    const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;

    const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport, this._screenshotTaskQueue);
    assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
    this._targets.set(event.targetInfo.targetId, target);

    if (await target._initializedPromise) {
      this.emit(Events.Browser.TargetCreated, target);
      context.emit(Events.BrowserContext.TargetCreated, target);
    }
  }

  /**
   * @param {{targetId: string}} event
   */
  async _targetDestroyed(event) {
    const target = this._targets.get(event.targetId);
    target._initializedCallback(false);
    this._targets.delete(event.targetId);
    target._closedCallback();
    if (await target._initializedPromise) {
      this.emit(Events.Browser.TargetDestroyed, target);
      target.browserContext().emit(Events.BrowserContext.TargetDestroyed, target);
    }
  }

  /**
   * @param {!Protocol.Target.targetInfoChangedPayload} event
   */
  _targetInfoChanged(event) {
    const target = this._targets.get(event.targetInfo.targetId);
    assert(target, 'target should exist before targetInfoChanged');
    const previousURL = target.url();
    const wasInitialized = target._isInitialized;
    target._targetInfoChanged(event.targetInfo);
    if (wasInitialized && previousURL !== target.url()) {
      this.emit(Events.Browser.TargetChanged, target);
      target.browserContext().emit(Events.BrowserContext.TargetChanged, target);
    }
  }

  /**
   * @return {string}
   */
  wsEndpoint() {
    return this._connection.url();
  }

  /**
   * @return {!Promise<!Puppeteer.Page>}
   */
  async newPage() {
    return this._defaultContext.newPage();
  }

  /**
   * @param {?string} contextId
   * @return {!Promise<!Puppeteer.Page>}
   */
  async _createPageInContext(contextId) {
    const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined});
    const target = await this._targets.get(targetId);
    assert(await target._initializedPromise, 'Failed to create target for page');
    const page = await target.page();
    return page;
  }

  /**
   * @return {!Array<!Target>}
   */
  targets() {
    return Array.from(this._targets.values()).filter(target => target._isInitialized);
  }

  /**
   * @return {!Target}
   */
  target() {
    return this.targets().find(target => target.type() === 'browser');
  }

  /**
   * @param {function(!Target):boolean} predicate
   * @param {{timeout?: number}=} options
   * @return {!Promise<!Target>}
   */
  async waitForTarget(predicate, options = {}) {
    const {
      timeout = 30000
    } = options;
    const existingTarget = this.targets().find(predicate);
    if (existingTarget)
      return existingTarget;
    let resolve;
    const targetPromise = new Promise(x => resolve = x);
    this.on(Events.Browser.TargetCreated, check);
    this.on(Events.Browser.TargetChanged, check);
    try {
      if (!timeout)
        return await targetPromise;
      return await helper.waitWithTimeout(targetPromise, 'target', timeout);
    } finally {
      this.removeListener(Events.Browser.TargetCreated, check);
      this.removeListener(Events.Browser.TargetChanged, check);
    }

    /**
     * @param {!Target} target
     */
    function check(target) {
      if (predicate(target))
        resolve(target);
    }
  }

  /**
   * @return {!Promise<!Array<!Puppeteer.Page>>}
   */
  async pages() {
    const contextPages = await Promise.all(this.browserContexts().map(context => context.pages()));
    // Flatten array.
    return contextPages.reduce((acc, x) => acc.concat(x), []);
  }

  /**
   * @return {!Promise<string>}
   */
  async version() {
    const version = await this._getVersion();
    return version.product;
  }

  /**
   * @return {!Promise<string>}
   */
  async userAgent() {
    const version = await this._getVersion();
    return version.userAgent;
  }

  async close() {
    await this._closeCallback.call(null);
    this.disconnect();
  }

  disconnect() {
    this._connection.dispose();
  }

  /**
   * @return {!Promise<!Object>}
   */
  _getVersion() {
    return this._connection.send('Browser.getVersion');
  }
}

class BrowserContext extends EventEmitter {
  /**
   * @param {!Puppeteer.Connection} connection
   * @param {!Browser} browser
   * @param {?string} contextId
   */
  constructor(connection, browser, contextId) {
    super();
    this._connection = connection;
    this._browser = browser;
    this._id = contextId;
  }

  /**
   * @return {!Array<!Target>} target
   */
  targets() {
    return this._browser.targets().filter(target => target.browserContext() === this);
  }

  /**
   * @param {function(!Target):boolean} predicate
   * @param {{timeout?: number}=} options
   * @return {!Promise<!Target>}
   */
  waitForTarget(predicate, options) {
    return this._browser.waitForTarget(target => target.browserContext() === this && predicate(target), options);
  }

  /**
   * @return {!Promise<!Array<!Puppeteer.Page>>}
   */
  async pages() {
    const pages = await Promise.all(
        this.targets()
            .filter(target => target.type() === 'page')
            .map(target => target.page())
    );
    return pages.filter(page => !!page);
  }

  /**
   * @return {boolean}
   */
  isIncognito() {
    return !!this._id;
  }

  /**
   * @param {string} origin
   * @param {!Array<string>} permissions
   */
  async overridePermissions(origin, permissions) {
    const webPermissionToProtocol = new Map([
      ['geolocation', 'geolocation'],
      ['midi', 'midi'],
      ['notifications', 'notifications'],
      ['push', 'push'],
      ['camera', 'videoCapture'],
      ['microphone', 'audioCapture'],
      ['background-sync', 'backgroundSync'],
      ['ambient-light-sensor', 'sensors'],
      ['accelerometer', 'sensors'],
      ['gyroscope', 'sensors'],
      ['magnetometer', 'sensors'],
      ['accessibility-events', 'accessibilityEvents'],
      ['clipboard-read', 'clipboardRead'],
      ['clipboard-write', 'clipboardWrite'],
      ['payment-handler', 'paymentHandler'],
      // chrome-specific permissions we have.
      ['midi-sysex', 'midiSysex'],
    ]);
    permissions = permissions.map(permission => {
      const protocolPermission = webPermissionToProtocol.get(permission);
      if (!protocolPermission)
        throw new Error('Unknown permission: ' + permission);
      return protocolPermission;
    });
    await this._connection.send('Browser.grantPermissions', {origin, browserContextId: this._id || undefined, permissions});
  }

  async clearPermissionOverrides() {
    await this._connection.send('Browser.resetPermissions', {browserContextId: this._id || undefined});
  }

  /**
   * @return {!Promise<!Puppeteer.Page>}
   */
  newPage() {
    return this._browser._createPageInContext(this._id);
  }

  /**
   * @return {!Browser}
   */
  browser() {
    return this._browser;
  }

  async close() {
    assert(this._id, 'Non-incognito profiles cannot be closed!');
    await this._browser._disposeContext(this._id);
  }
}

module.exports = {Browser, BrowserContext};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var _ = require( 'lodash' ),
  errorHandler = require( '../errors.server.controller.js' ),
  mongoose = require( 'mongoose' ),
  passport = require( 'passport' ),
  User = mongoose.model( 'User' );

/**
 * Update user details
 */
exports.update = function ( req, res ) {
  // Init Variables
  var user = req.user;
  var message = null;

  // For security measurement we remove the roles from the req.body object
  delete req.body.roles;

  if ( user ) {
    // Merge existing user
    user = _.extend( user, req.body );
    user.updated = Date.now();
    user.displayName = user.firstName + ' ' + user.lastName;

    user.save( function ( err ) {
      if ( err ) {
        return res.status( 400 ).send( {
          message: errorHandler.getErrorMessage( err )
        } );
      } else {
        req.login( user, function ( err ) {
          if ( err ) {
            res.status( 400 ).send( err );
          } else {
            res.json( user );
          }
        } );
      }
    } );
  } else {
    res.status( 400 ).send( {
      message: 'User is not signed in'
    } );
  }
};

/**
 *
 */
exports.update = function ( req, res ) {
  // Init Variables
  var user = req.profile;
  var message = null;
  if ( user ) {
    // Merge existing user
    user = _.extend( user, req.body );
    user.updated = Date.now();
    user.displayName = user.firstName + ' ' + user.lastName;
    var roles = [ 'user' ];
    if ( req.body.rights.teacher ) roles.push( 'teacher' );
    if ( req.body.rights.manager ) roles.push( 'manager' );
    if ( req.body.rights.admin ) roles.push( 'admin' );
    if ( req.body.rights.printer ) roles.push( 'printer' );
    user.roles = roles;
    user.save( function ( err ) {
      if ( err ) {
        return res.status( 400 ).send( {
          message: errorHandler.getErrorMessage( err )
        } );
      } else {
        req.login( user, function ( err ) {
          if ( err ) {
            res.status( 400 ).send( err );
          } else {
            res.json( user );
          }
        } );
      }
    } );
  } else {
    res.status( 400 ).send( {
      message: 'User is not signed in'
    } );
  }
};

/**
 * Send User
 */
exports.me = function ( req, res ) {
  res.json( req.user || null );
};

/**
 * Delete user
 */
exports.delete = function ( req, res ) {
  var user = req.profile;
  user.remove( function ( err ) {
    if ( err ) {
      return res.status( 400 ).send( {
        message: errorHandler.getErrorMessage( err )
      } );
    } else {
      res.jsonp( user );
    }
  } );
};
<commit_msg>duplicate function<commit_after>'use strict';

/**
 * Module dependencies.
 */
var _ = require( 'lodash' ),
  errorHandler = require( '../errors.server.controller.js' ),
  mongoose = require( 'mongoose' ),
  passport = require( 'passport' ),
  User = mongoose.model( 'User' );

/**
 * Update user details
 */
exports.update = function ( req, res ) {
  // Init Variables
  var user = req.user;
  var message = null;

  // For security measurement we remove the roles from the req.body object
  delete req.body.roles;

  if ( user ) {
    // Merge existing user
    user = _.extend( user, req.body );
    user.updated = Date.now();
    user.displayName = user.firstName + ' ' + user.lastName;

    user.save( function ( err ) {
      if ( err ) {
        return res.status( 400 ).send( {
          message: errorHandler.getErrorMessage( err )
        } );
      } else {
        req.login( user, function ( err ) {
          if ( err ) {
            res.status( 400 ).send( err );
          } else {
            res.json( user );
          }
        } );
      }
    } );
  } else {
    res.status( 400 ).send( {
      message: 'User is not signed in'
    } );
  }
};

/**
 *
 */
exports.updateUser = function ( req, res ) {
  // Init Variables
  var user = req.profile;
  var message = null;
  if ( user ) {
    // Merge existing user
    user = _.extend( user, req.body );
    user.updated = Date.now();
    user.displayName = user.firstName + ' ' + user.lastName;
    var roles = [ 'user' ];
    if ( req.body.rights.teacher ) roles.push( 'teacher' );
    if ( req.body.rights.manager ) roles.push( 'manager' );
    if ( req.body.rights.admin ) roles.push( 'admin' );
    if ( req.body.rights.printer ) roles.push( 'printer' );
    user.roles = roles;
    user.save( function ( err ) {
      if ( err ) {
        return res.status( 400 ).send( {
          message: errorHandler.getErrorMessage( err )
        } );
      } else {
        req.login( user, function ( err ) {
          if ( err ) {
            res.status( 400 ).send( err );
          } else {
            res.json( user );
          }
        } );
      }
    } );
  } else {
    res.status( 400 ).send( {
      message: 'User is not signed in'
    } );
  }
};

/**
 * Send User
 */
exports.me = function ( req, res ) {
  res.json( req.user || null );
};

/**
 * Delete user
 */
exports.delete = function ( req, res ) {
  var user = req.profile;
  user.remove( function ( err ) {
    if ( err ) {
      return res.status( 400 ).send( {
        message: errorHandler.getErrorMessage( err )
      } );
    } else {
      res.jsonp( user );
    }
  } );
};
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */

sap.ui.define(["sap/base/Log", "sap/base/util/ObjectPath", "sap/ui/core/mvc/View"],
	function(Log, ObjectPath, View) {

	"use strict";

	/**
	 * Creates 0..n UI5 controls from an ExtensionPoint.
	 *
	 * One control if the ExtensionPoint is e.g. filled with a View, zero for extension points without configured extension and
	 * n controls for multi-root Fragments as extension.
	 *
	 * In JSViews, this function allows both JSON notation in aggregation content as well as adding an extension point to an aggregation after the target control
	 * has already been instantiated. In the latter case the optional parameters oTargetControls and oTargetAggregation need to be specified.
	 *
	 * @param {sap.ui.core.mvc.View|sap.ui.core.Fragment} oContainer The view or fragment containing the extension point
	 * @param {string} sExtName The extensionName used to identify the extension point in the customizing
	 * @param {function} [fnCreateDefaultContent] Optional callback function creating default content, returning an array of controls. It is executed
	 * 			when there's no customizing, if not provided, no default content will be rendered.
	 * 			fnCreateDefaultContent might also return a Promise, which resolves with an array of controls.
	 * @param {sap.ui.core.Control} [oTargetControl] Optional - use this parameter to attach the extension point to a particular aggregation
	 * @param {string} [sAggregationName] Optional - if provided along with oTargetControl, the extension point content is added to this particular aggregation at oTargetControl,
	 * 			if not given, but an oTargetControl is still present, the function will attempt to add the extension point to the default aggregation of oTargetControl.
	 * 			If no oTargetControl is provided, sAggregationName will also be ignored.
	 *
	 * @returns {sap.ui.core.Control[]|Promise<sap.ui.core.Control[]>} An array with 0..n controls created from an ExtensionPoint or
	 * 			if fnCreateDefaultContent is called and returns a Promise, a Promise with the controls is returned instead
	 * @deprecated since 1.56, Use {@link sap.ui.core.ExtensionPoint.load} instead
	 * @public
	 * @static
	 * @ui5-global-only
	 */
	sap.ui.extensionpoint = function(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName) {
		Log.warning("Do not use deprecated factory function 'sap.ui.extensionpoint'. Use 'sap.ui.core.ExtensionPoint.load' instead", "sap.ui.extensionpoint", null, function () {
			return {
				type: "sap.ui.extensionpoint",
				name: sExtName
			};
		});
		return ExtensionPoint._factory(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName);
	};

	/**
	 * @alias sap.ui.core.ExtensionPoint
	 * @namespace
	 * @public
	 */
	// Following we attach all additional module API functions to the original sap.ui.extensionpoint factory.
	// For compatibility we cannot change the actual return value of this module.
	var ExtensionPoint = sap.ui.extensionpoint;

	/**
	 * API documentation see ExtensionPoint.load() and sap.ui.extensionpoint().
	 *
	 * Used only internally by this module, as well as the XMLTemplateProcessor.
	 */
	ExtensionPoint._factory = function(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName) {
		var extensionConfig, oView, vResult;

		// Note: the dependency to CustomizingConfiguration is not statically declared to not enforce the loading of that module
		var CustomizingConfiguration = sap.ui.require('sap/ui/core/CustomizingConfiguration');

		// do we have a view to check or do we need to check for configuration for a fragment?
		if (oContainer) {
			if (oContainer.isA("sap.ui.core.mvc.View")) {
				extensionConfig = CustomizingConfiguration && CustomizingConfiguration.getViewExtension(oContainer.sViewName, sExtName, oContainer);
				oView = oContainer;
			} else if (oContainer.isA("sap.ui.core.Fragment")) {
				extensionConfig = CustomizingConfiguration && CustomizingConfiguration.getViewExtension(oContainer.getFragmentName(), sExtName, oContainer);
				oView = oContainer._oContainingView;
			}
		}

		// Extension Point - is something configured?
		if (extensionConfig) {
			if (extensionConfig.className) {
				var fnClass = sap.ui.requireSync(extensionConfig.className.replace(/\./g, "/")); // make sure fnClass.getMetadata() exists
				fnClass = fnClass || ObjectPath.get(extensionConfig.className);
				var sId = oView && extensionConfig.id ? oView.createId(extensionConfig.id) : extensionConfig.id;
				Log.info("Customizing: View extension found for extension point '" + sExtName
						+ "' in View '" + oView.sViewName + "': " + extensionConfig.className + ": " + (extensionConfig.viewName || extensionConfig.fragmentName));

				if (extensionConfig.className === "sap.ui.core.Fragment") {
					var oFragment = new fnClass({
						id: sId,
						type: extensionConfig.type,
						fragmentName: extensionConfig.fragmentName,
						containingView: oView
					});
					vResult = (Array.isArray(oFragment) ? oFragment : [oFragment]); // vResult is now an array, even if empty - so if a Fragment is configured, the default content below is not added anymore

				} else if (extensionConfig.className === "sap.ui.core.mvc.View") {
					oView = View._legacyCreate({type: extensionConfig.type, viewName: extensionConfig.viewName, id: sId});
					vResult = [oView]; // vResult is now an array, even if empty - so if a Fragment is configured, the default content below is not added anymore

				} else {
					// unknown extension class
					Log.warning("Customizing: Unknown extension className configured (and ignored) in Component.js for extension point '" + sExtName
							+ "' in View '" + oView.sViewName + "': " + extensionConfig.className);
				}
			} else {
				Log.warning("Customizing: no extension className configured in Component.js for extension point '" + sExtName
						+ "' in View '" + oView.sViewName + "': " + extensionConfig.className);
			}
		} else if (ExtensionPoint._fnExtensionProvider) {
			var sExtensionProvider = ExtensionPoint._fnExtensionProvider(oView);

			// For Fragments we need to make sure to pass the correct View instance to the EP Provider.
			// We can infer the nearest View to the Fragment from the given controller (either explicitly given, or assigned via the containing view).
			var sFragmentId;
			if (oView.isA("sap.ui.core.Fragment")) {
				sFragmentId = oView._sExplicitId;
				// determine actual containing view instance
				var oController = oView.getController();
				oView = oController && oController.isA("sap.ui.core.mvc.Controller") && oController.getView();
				if (oView) {
					// local ID of the fragment (minus the view-id prefix)
					// Might include ID prefixes for nested Fragments.
					sFragmentId = oView.getLocalId(sFragmentId) || sFragmentId;
				}
			}

			if (sExtensionProvider) {
				if (!oView) {
					// Someone could create a Fragment via the factory with a Controller without an associated view,
					// e.g. by creating a Controller instance via Controller.create().
					Log.warning("View instance could not be passed to ExtensionPoint Provider for extension point '" + sExtName + "' " +
								"in fragment '" + sFragmentId + "'.");
				}
				/**
				 * In case we have an ExtensionProvider assigned, we return a marker object.
				 * This marker object will be used later during the View processing to apply the ExtensionProvider
				 * once the target control (parent of the extension point) was instantiated.
				 *
				 * The marker object is defined below, including all available properties.
				 * Properties starting with an underscore will only be used internally for processing.
				 * All other properties are exposed to be used for applying flexibility changes.
				 *
				 * The properties will be correctly filled when applyExtensionPoint() is called on the ExtensionProvider module.
				 */
				return [{
					// Track the correct provider class for each ExtensionPoint instance
					providerClass: sExtensionProvider,

					// The containing view instance.
					view: oView,

					// The ID of the fragment, in case the ExtensionPoint is inside a Fragment
					// (undefined if the ExtensionPoint is contained in a View).
					// The EP Provider needs the Fragment ID to distinguish between multiple occurences of the same fragment.
					fragmentId: sFragmentId,

					// The extension point name.
					name: sExtName,

					// Callback, which can be called to create the default content of the ExtensionPoint if needed.
					// The fnCreateDefaultContent function can either return an array of controls OR a Promise,
					// which then resolves with an array of controls.
					// See:  {@link sap.ui.core.ExtensionPoint.load}
					// Also: {@link sap.ui.extensionpoint}
					createDefault: fnCreateDefaultContent,

					// The target control into which the ExtensionPoint content will be inserted.
					targetControl: undefined,

					// The name of the target aggregation inside the target control.
					aggregationName: undefined,

					// the index of the ExtensionPoint inside its target aggregation in the parent control
					index: undefined,

					// The ready() function needs to be called once the controls have been inserted into the
					// target aggregation of the target control.
					// An array of all inserted controls must be passed to the ready function.
					// The index of all following sibling ExtensionPoints will then be shifted via the the marker object by reference.
					ready: function(aControls) {
						// propagate index shift
						var next = this._nextSibling;
						while (next != null) {
							next.index += aControls.length;
							next = next._nextSibling;
						}
					},

					// only used internally to check for a marker object
					_isExtensionPoint: true,

					// reference to the next sibling ExtensionPoint OR null if none present in the XML DOM
					_nextSibling: null
				}];
			}
		}

		if (!vResult && typeof fnCreateDefaultContent === 'function') {
			// if there is no extension configured or found or customizing disabled - check for default content
			// do we have a callback function?
			vResult = fnCreateDefaultContent();
		}

		var fnProcessResult = function (vResult) {
			// if the result returned from the default content is no array, wrap it in one
			if (vResult && !Array.isArray(vResult)){
				vResult = [vResult];
			}

			//if we have any result from either default content or customizing AND a target control is provided:
			if (vResult && oTargetControl) {
				//directly add the extension to the corresponding aggregation at the target control:
				var oAggregationInfo = oTargetControl.getMetadata().getAggregation(sAggregationName);
				if (oAggregationInfo) {
					for (var i = 0, l = vResult.length; i < l; i++) {
						// call the corresponding mutator for each element within the extension point - may be one or multiple elements
						oTargetControl[oAggregationInfo._sMutator](vResult[i]);
					}
				} else {
					// the target control has no default aggregation, or the aggregationName provided doesn't match an existing aggregation as defined at the targetControl
					Log.error("Creating extension point failed - Tried to add extension point with name " + sExtName + " to an aggregation of " +
							oTargetControl.getId() + " in view " + oView.sViewName + ", but sAggregationName was not provided correctly and I could not find a default aggregation");
				}
			}

			return vResult || [];
		};

		// if vResult was created via fnCreateDefaultContent and is a Promise (or SyncPromise)
		if (vResult && typeof vResult.then === 'function') {
			return vResult.then(fnProcessResult);
		} else {
			return fnProcessResult(vResult);
		}
	};

	/**
	 * Registers a function, which will be called by the XMLTemplateProcessor to retrieve an ExtensionProvider Class.
	 * The registered module will be loaded once an ExtensionPoint is encountered during XMLView processing.
	 *
	 * @param {function|undefined|null} fnExtensionProvider Accepted values are: function, null or undefined.
	 *       If a function is given it must either return the module path of the ExtensionProvider class
	 *       or undefined in case flex is not active.
	 *       If null or undefined is given, an already registered provider is removed.
	 * @private
	 * @ui5-restricted sap.ui.fl
	 * @since 1.78.0
	 */
	ExtensionPoint.registerExtensionProvider = function(fnExtensionProvider) {
		if (fnExtensionProvider == null) {
			// unset if null or undefined
			delete ExtensionPoint._fnExtensionProvider;
		} else if (typeof fnExtensionProvider == "function") {
			ExtensionPoint._fnExtensionProvider = fnExtensionProvider;
		} else {
			Log.error("ExtensionPoint provider must be a function!");
		}
	};

	/**
	 * Creates 0..n UI5 controls from an ExtensionPoint.
	 *
	 * One control if the ExtensionPoint is e.g. filled with a View, zero for extension points without configured extension and
	 * n controls for multi-root Fragments as extension.
	 *
	 * @param {object} mOptions an object map (see below)
	 * @param {sap.ui.core.mvc.View|sap.ui.core.Fragment} mOptions.container The view or fragment containing the extension point
	 * @param {string} mOptions.name The mOptions.name is used to identify the extension point in the customizing
	 * @param {function} [mOptions.createDefaultContent] Optional callback function creating default content, returning an array of controls. It is executed
	 *        when there's no customizing, if not provided, no default content will be rendered.
	 *        mOptions.createDefaultContent might also return a Promise, which resolves with an array of controls.
	 * @returns {Promise<sap.ui.core.Control[]>} a Promise, which resolves with an array of 0..n controls created from an ExtensionPoint.
	 *        If mOptions.createDefaultContent is called and returns a Promise, that Promise is returned by ExtensionPoint.load.
	 * @since 1.56.0
	 * @public
	 * @static
	 */
	ExtensionPoint.load = function(mOptions) {
		return Promise.resolve(
			ExtensionPoint._factory(
				mOptions.container,
				mOptions.name,
				mOptions.createDefaultContent
			)
		);
	};

	return ExtensionPoint;
});<commit_msg>[INTERNAL] ExtensionPoint: Improve controller instance check<commit_after>/*!
 * ${copyright}
 */

sap.ui.define(["sap/base/Log", "sap/base/util/ObjectPath", "sap/ui/core/mvc/View"],
	function(Log, ObjectPath, View) {

	"use strict";

	/**
	 * Creates 0..n UI5 controls from an ExtensionPoint.
	 *
	 * One control if the ExtensionPoint is e.g. filled with a View, zero for extension points without configured extension and
	 * n controls for multi-root Fragments as extension.
	 *
	 * In JSViews, this function allows both JSON notation in aggregation content as well as adding an extension point to an aggregation after the target control
	 * has already been instantiated. In the latter case the optional parameters oTargetControls and oTargetAggregation need to be specified.
	 *
	 * @param {sap.ui.core.mvc.View|sap.ui.core.Fragment} oContainer The view or fragment containing the extension point
	 * @param {string} sExtName The extensionName used to identify the extension point in the customizing
	 * @param {function} [fnCreateDefaultContent] Optional callback function creating default content, returning an array of controls. It is executed
	 * 			when there's no customizing, if not provided, no default content will be rendered.
	 * 			fnCreateDefaultContent might also return a Promise, which resolves with an array of controls.
	 * @param {sap.ui.core.Control} [oTargetControl] Optional - use this parameter to attach the extension point to a particular aggregation
	 * @param {string} [sAggregationName] Optional - if provided along with oTargetControl, the extension point content is added to this particular aggregation at oTargetControl,
	 * 			if not given, but an oTargetControl is still present, the function will attempt to add the extension point to the default aggregation of oTargetControl.
	 * 			If no oTargetControl is provided, sAggregationName will also be ignored.
	 *
	 * @returns {sap.ui.core.Control[]|Promise<sap.ui.core.Control[]>} An array with 0..n controls created from an ExtensionPoint or
	 * 			if fnCreateDefaultContent is called and returns a Promise, a Promise with the controls is returned instead
	 * @deprecated since 1.56, Use {@link sap.ui.core.ExtensionPoint.load} instead
	 * @public
	 * @static
	 * @ui5-global-only
	 */
	sap.ui.extensionpoint = function(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName) {
		Log.warning("Do not use deprecated factory function 'sap.ui.extensionpoint'. Use 'sap.ui.core.ExtensionPoint.load' instead", "sap.ui.extensionpoint", null, function () {
			return {
				type: "sap.ui.extensionpoint",
				name: sExtName
			};
		});
		return ExtensionPoint._factory(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName);
	};

	/**
	 * @alias sap.ui.core.ExtensionPoint
	 * @namespace
	 * @public
	 */
	// Following we attach all additional module API functions to the original sap.ui.extensionpoint factory.
	// For compatibility we cannot change the actual return value of this module.
	var ExtensionPoint = sap.ui.extensionpoint;

	/**
	 * API documentation see ExtensionPoint.load() and sap.ui.extensionpoint().
	 *
	 * Used only internally by this module, as well as the XMLTemplateProcessor.
	 */
	ExtensionPoint._factory = function(oContainer, sExtName, fnCreateDefaultContent,  oTargetControl, sAggregationName) {
		var extensionConfig, oView, vResult;

		// Note: the dependency to CustomizingConfiguration is not statically declared to not enforce the loading of that module
		var CustomizingConfiguration = sap.ui.require('sap/ui/core/CustomizingConfiguration');

		// do we have a view to check or do we need to check for configuration for a fragment?
		if (oContainer) {
			if (oContainer.isA("sap.ui.core.mvc.View")) {
				extensionConfig = CustomizingConfiguration && CustomizingConfiguration.getViewExtension(oContainer.sViewName, sExtName, oContainer);
				oView = oContainer;
			} else if (oContainer.isA("sap.ui.core.Fragment")) {
				extensionConfig = CustomizingConfiguration && CustomizingConfiguration.getViewExtension(oContainer.getFragmentName(), sExtName, oContainer);
				oView = oContainer._oContainingView;
			}
		}

		// Extension Point - is something configured?
		if (extensionConfig) {
			if (extensionConfig.className) {
				var fnClass = sap.ui.requireSync(extensionConfig.className.replace(/\./g, "/")); // make sure fnClass.getMetadata() exists
				fnClass = fnClass || ObjectPath.get(extensionConfig.className);
				var sId = oView && extensionConfig.id ? oView.createId(extensionConfig.id) : extensionConfig.id;
				Log.info("Customizing: View extension found for extension point '" + sExtName
						+ "' in View '" + oView.sViewName + "': " + extensionConfig.className + ": " + (extensionConfig.viewName || extensionConfig.fragmentName));

				if (extensionConfig.className === "sap.ui.core.Fragment") {
					var oFragment = new fnClass({
						id: sId,
						type: extensionConfig.type,
						fragmentName: extensionConfig.fragmentName,
						containingView: oView
					});
					vResult = (Array.isArray(oFragment) ? oFragment : [oFragment]); // vResult is now an array, even if empty - so if a Fragment is configured, the default content below is not added anymore

				} else if (extensionConfig.className === "sap.ui.core.mvc.View") {
					oView = View._legacyCreate({type: extensionConfig.type, viewName: extensionConfig.viewName, id: sId});
					vResult = [oView]; // vResult is now an array, even if empty - so if a Fragment is configured, the default content below is not added anymore

				} else {
					// unknown extension class
					Log.warning("Customizing: Unknown extension className configured (and ignored) in Component.js for extension point '" + sExtName
							+ "' in View '" + oView.sViewName + "': " + extensionConfig.className);
				}
			} else {
				Log.warning("Customizing: no extension className configured in Component.js for extension point '" + sExtName
						+ "' in View '" + oView.sViewName + "': " + extensionConfig.className);
			}
		} else if (ExtensionPoint._fnExtensionProvider) {
			var sExtensionProvider = ExtensionPoint._fnExtensionProvider(oView);

			// For Fragments we need to make sure to pass the correct View instance to the EP Provider.
			// We can infer the nearest View to the Fragment from the given controller (either explicitly given, or assigned via the containing view).
			var sFragmentId;
			if (oView.isA("sap.ui.core.Fragment")) {
				sFragmentId = oView._sExplicitId;
				// determine actual containing view instance
				var oController = oView.getController();
				oView = oController && typeof oController.isA === "function" && oController.isA("sap.ui.core.mvc.Controller") && oController.getView();
				if (oView) {
					// local ID of the fragment (minus the view-id prefix)
					// Might include ID prefixes for nested Fragments.
					sFragmentId = oView.getLocalId(sFragmentId) || sFragmentId;
				}
			}

			if (sExtensionProvider) {
				if (!oView) {
					// Someone could create a Fragment via the factory with a Controller without an associated view,
					// e.g. by creating a Controller instance via Controller.create().
					Log.warning("View instance could not be passed to ExtensionPoint Provider for extension point '" + sExtName + "' " +
								"in fragment '" + sFragmentId + "'.");
				}
				/**
				 * In case we have an ExtensionProvider assigned, we return a marker object.
				 * This marker object will be used later during the View processing to apply the ExtensionProvider
				 * once the target control (parent of the extension point) was instantiated.
				 *
				 * The marker object is defined below, including all available properties.
				 * Properties starting with an underscore will only be used internally for processing.
				 * All other properties are exposed to be used for applying flexibility changes.
				 *
				 * The properties will be correctly filled when applyExtensionPoint() is called on the ExtensionProvider module.
				 */
				return [{
					// Track the correct provider class for each ExtensionPoint instance
					providerClass: sExtensionProvider,

					// The containing view instance.
					view: oView,

					// The ID of the fragment, in case the ExtensionPoint is inside a Fragment
					// (undefined if the ExtensionPoint is contained in a View).
					// The EP Provider needs the Fragment ID to distinguish between multiple occurences of the same fragment.
					fragmentId: sFragmentId,

					// The extension point name.
					name: sExtName,

					// Callback, which can be called to create the default content of the ExtensionPoint if needed.
					// The fnCreateDefaultContent function can either return an array of controls OR a Promise,
					// which then resolves with an array of controls.
					// See:  {@link sap.ui.core.ExtensionPoint.load}
					// Also: {@link sap.ui.extensionpoint}
					createDefault: fnCreateDefaultContent,

					// The target control into which the ExtensionPoint content will be inserted.
					targetControl: undefined,

					// The name of the target aggregation inside the target control.
					aggregationName: undefined,

					// the index of the ExtensionPoint inside its target aggregation in the parent control
					index: undefined,

					// The ready() function needs to be called once the controls have been inserted into the
					// target aggregation of the target control.
					// An array of all inserted controls must be passed to the ready function.
					// The index of all following sibling ExtensionPoints will then be shifted via the the marker object by reference.
					ready: function(aControls) {
						// propagate index shift
						var next = this._nextSibling;
						while (next != null) {
							next.index += aControls.length;
							next = next._nextSibling;
						}
					},

					// only used internally to check for a marker object
					_isExtensionPoint: true,

					// reference to the next sibling ExtensionPoint OR null if none present in the XML DOM
					_nextSibling: null
				}];
			}
		}

		if (!vResult && typeof fnCreateDefaultContent === 'function') {
			// if there is no extension configured or found or customizing disabled - check for default content
			// do we have a callback function?
			vResult = fnCreateDefaultContent();
		}

		var fnProcessResult = function (vResult) {
			// if the result returned from the default content is no array, wrap it in one
			if (vResult && !Array.isArray(vResult)){
				vResult = [vResult];
			}

			//if we have any result from either default content or customizing AND a target control is provided:
			if (vResult && oTargetControl) {
				//directly add the extension to the corresponding aggregation at the target control:
				var oAggregationInfo = oTargetControl.getMetadata().getAggregation(sAggregationName);
				if (oAggregationInfo) {
					for (var i = 0, l = vResult.length; i < l; i++) {
						// call the corresponding mutator for each element within the extension point - may be one or multiple elements
						oTargetControl[oAggregationInfo._sMutator](vResult[i]);
					}
				} else {
					// the target control has no default aggregation, or the aggregationName provided doesn't match an existing aggregation as defined at the targetControl
					Log.error("Creating extension point failed - Tried to add extension point with name " + sExtName + " to an aggregation of " +
							oTargetControl.getId() + " in view " + oView.sViewName + ", but sAggregationName was not provided correctly and I could not find a default aggregation");
				}
			}

			return vResult || [];
		};

		// if vResult was created via fnCreateDefaultContent and is a Promise (or SyncPromise)
		if (vResult && typeof vResult.then === 'function') {
			return vResult.then(fnProcessResult);
		} else {
			return fnProcessResult(vResult);
		}
	};

	/**
	 * Registers a function, which will be called by the XMLTemplateProcessor to retrieve an ExtensionProvider Class.
	 * The registered module will be loaded once an ExtensionPoint is encountered during XMLView processing.
	 *
	 * @param {function|undefined|null} fnExtensionProvider Accepted values are: function, null or undefined.
	 *       If a function is given it must either return the module path of the ExtensionProvider class
	 *       or undefined in case flex is not active.
	 *       If null or undefined is given, an already registered provider is removed.
	 * @private
	 * @ui5-restricted sap.ui.fl
	 * @since 1.78.0
	 */
	ExtensionPoint.registerExtensionProvider = function(fnExtensionProvider) {
		if (fnExtensionProvider == null) {
			// unset if null or undefined
			delete ExtensionPoint._fnExtensionProvider;
		} else if (typeof fnExtensionProvider == "function") {
			ExtensionPoint._fnExtensionProvider = fnExtensionProvider;
		} else {
			Log.error("ExtensionPoint provider must be a function!");
		}
	};

	/**
	 * Creates 0..n UI5 controls from an ExtensionPoint.
	 *
	 * One control if the ExtensionPoint is e.g. filled with a View, zero for extension points without configured extension and
	 * n controls for multi-root Fragments as extension.
	 *
	 * @param {object} mOptions an object map (see below)
	 * @param {sap.ui.core.mvc.View|sap.ui.core.Fragment} mOptions.container The view or fragment containing the extension point
	 * @param {string} mOptions.name The mOptions.name is used to identify the extension point in the customizing
	 * @param {function} [mOptions.createDefaultContent] Optional callback function creating default content, returning an array of controls. It is executed
	 *        when there's no customizing, if not provided, no default content will be rendered.
	 *        mOptions.createDefaultContent might also return a Promise, which resolves with an array of controls.
	 * @returns {Promise<sap.ui.core.Control[]>} a Promise, which resolves with an array of 0..n controls created from an ExtensionPoint.
	 *        If mOptions.createDefaultContent is called and returns a Promise, that Promise is returned by ExtensionPoint.load.
	 * @since 1.56.0
	 * @public
	 * @static
	 */
	ExtensionPoint.load = function(mOptions) {
		return Promise.resolve(
			ExtensionPoint._factory(
				mOptions.container,
				mOptions.name,
				mOptions.createDefaultContent
			)
		);
	};

	return ExtensionPoint;
});<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import WidgetEditor, { Modal, Tooltip, Icons, setConfig, VegaChart, getVegaTheme, getConfig } from 'widget-editor';

import ExtendedHeader from 'components/ExtendedHeader';
import StepsBar from 'components/StepsBar';
import Notification from 'components/Notification';

import { setStep, setWidgetCreationDataset, setWidgetCreationTitle, setWidgetCreationDescription, setWidgetCreationCaption } from 'redactions/management';

const STEPS = [
  {
    name: 'Dataset',
    description: 'Pick the dataset you want to use for the widget.'
  },
  {
    name: 'Visualization',
    description: 'Please use the selector to change the type of visualization and choose the columns you want to use.'
  }
];

class NewWidgetPage extends React.Component {
  constructor(props) {
    super(props);

    // We set the config of the widget editor
    const { env } = props;
    setConfig({
      url: env.apiUrl,
      env: env.apiEnv,
      applications: env.apiApplications,
      authUrl: env.controlTowerUrl,
      assetsPath: '/packs/images/',
      userToken: env.user.token || undefined
    });

    this.state = {
      // Error while retrieving the widgetConfig from the widget-editor
      widgetConfigError: false,
      // Whether the user has dismissed the warning when switching to
      // the advanced editor
      advancedEditorWarningAccepted: false,
      // Whether we're using the advanced editor
      advancedEditor: false,
      // State of the advanced editor
      widgetConfig: {},
      // Whether the preview of the avanced editor is loading
      previewLoading: false,
      // Error while saving the widget
      saveError: false
    };
  }

  componentDidUpdate(_, prevState) {
    if (!prevState.advancedEditor && this.state.advancedEditor && this.advancedEditor) {
      this.codeMirror = CodeMirror.fromTextArea(this.advancedEditor, {
        mode: 'javascript',
        autoCloseTags: true,
        lineWrapping: true,
        lineNumbers: true
      });

      this.codeMirror.on('change', () => {
        try {
          const widgetConfig = JSON.parse(this.codeMirror.getValue());
          this.setState({ widgetConfig });
        } catch (e) {
          // If there's an error in the JSON, we reset the widgetConfig
          // so the user sees the preview is empty
          this.setState({ widgetConfig: {} });
        }
      });
    }
  }

  /**
   * Event handler executed when the user clicks the "Create"
   * button on the second step
   */
  onClickCreate() {
    new Promise((resolve, reject) => { // eslint-disable-line no-new
      if (this.state.advancedEditor) {
        resolve(this.state.widgetConfig);
      } else {
        this.getWidgetConfig()
          .then(resolve)
          .catch(reject);
      }
    }).then((widgetConfig) => {
      const widgetObj = Object.assign(
        {},
        {
          name: this.props.title || null,
          description: this.props.description
        },
        { widgetConfig }
      );

      let metadataObj = null;
      if (this.props.caption) {
        metadataObj = {
          info: {
            caption: this.props.caption
          }
        };
      }

      const widget = Object.assign({}, widgetObj, {
        application: [getConfig().applications],
        published: false,
        default: false,
        dataset: this.props.dataset
      });

      const metadata = !metadataObj
        ? null
        : Object.assign({}, metadataObj, {
          language: getConfig().locale,
          application: getConfig().applications
        });

      fetch(this.props.queryUrl, {
        method: 'POST',
        body: JSON.stringify(Object.assign(
          {},
          { widget },
          { ...(this.state.advancedEditor ? {} : { metadata }) }
        )),
        credentials: 'include',
        headers: new Headers({
          'content-type': 'application/json'
        })
      }).then((res) => {
        if (res.ok) {
          // window.location = this.props.redirectUrl;
        } else {
          throw new Error(res.statusText);
        }
      }).catch(() => {
        this.setState({ saveError: true });
      });
    }).catch(() => {
      // We display a warning in the UI
      this.setState({ widgetConfigError: true });
    });
  }

  /**
   * Event handler executed when the user toggles between
   * the "normal" and avanced editor
   */
  onToggleAdvancedEditor() {
    this.setState({
      advancedEditor: !this.state.advancedEditor,
      advancedEditorWarningAccepted: false
    });
  }

  render() {
    // eslint-disable-next-line no-shadow
    const { currentStep, setStep, datasets, dataset, setDataset,
      setTitle, setDescription, setCaption, title, description, caption, redirectUrl } = this.props;
    const { widgetConfigError, advancedEditor,
      advancedEditorWarningAccepted, widgetConfig, previewLoading, saveError } = this.state;

    let content;
    if (currentStep === 0) {
      content = (

                Dataset
                <select id="dataset" name="dataset" defaultValue={dataset || ''} onChange={e => setDataset(e.target.selectedOptions[0].value)}>
                  <option value="">Select a dataset</option>
                  {datasets.map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
                </select>

      );
    } else {
      content = (

          <Modal />
          <Tooltip />
          <Icons />

                  Name
                  <input type="text" id="name" name="name" placeholder="My widget" value={title} onChange={e => setTitle(e.target.value)} />

                  Description
                  <textarea id="description" name="description" placeholder="Description" value={description} onChange={e => setDescription(e.target.value)} />

                    <input type="checkbox" id="advanced-editor" name="advanced-editor" checked={advancedEditor} onClick={() => this.onToggleAdvancedEditor()} />
                    Use the advanced editor to create the widget manually

              { !advancedEditor && (
                <WidgetEditor
                  datasetId={dataset}
                  widgetTitle={title}
                  widgetCaption={caption}
                  saveButtonMode="never"
                  embedButtonMode="never"
                  onChangeWidgetTitle={t => setTitle(t)}
                  onChangeWidgetCaption={c => setCaption(c)}
                  provideWidgetConfig={(func) => { this.getWidgetConfig = func; }}
                />
              )}
              { advancedEditor && (

                    <textarea
                      ref={(el) => { this.advancedEditor = el; }}
                      defaultValue={JSON.stringify(widgetConfig)}
                    />

                    { previewLoading &&
 }
                    <VegaChart
                      data={widgetConfig}
                      theme={getVegaTheme()}
                      showLegend
                      reloadOnResize
                      toggleLoading={loading => this.setState({ previewLoading: loading })}
                      getForceUpdate={(func) => { this.forceChartUpdate = func; }}
                    />

              )}

      );
    }

    return (

        <ExtendedHeader title={STEPS[currentStep].name} subTitle={STEPS[currentStep].description} />
        <StepsBar
          steps={STEPS.map(s => s.name)}
          currentStep={currentStep}
          onChangeStep={setStep}
        />

        {widgetConfigError && (
          <Notification
            type="warning"
            content="Unable to create the widget"
            additionalContent="Make sure the visualization is correctly previewed before submitting the widget."
            onClose={() => this.setState({ widgetConfigError: false })}
          />
        )}

        {saveError && (
          <Notification
            type="error"
            content="Unable to create the widget"
            additionalContent="Please try again later."
            onClose={() => this.setState({ saveError: false })}
          />
        )}

        {advancedEditor && !advancedEditorWarningAccepted && (
          <Notification
            type="warning"
            content="Onced you've created the widget with the advanced editor, you won't be able to use the simple interface."
            dialogButtons
            closeable={false}
            onCancel={() => this.setState({ advancedEditor: false })}
            onContinue={() => this.setState({ advancedEditorWarningAccepted: true })}
            onClose={() => this.setState({ advancedEditor: false })}
          />
        )}

        {content}

              Cancel

              { currentStep >= 1 && (
                <button type="button" className="c-button -outline -dark-text" onClick={() => setStep(currentStep - 1)}>
                  Back
                </button>
              )}
              { currentStep === 0 && (
                <button type="submit" className="c-button" disabled={!dataset} onClick={() => setStep(currentStep + 1)}>
                  Continue
                </button>
              )}
              { currentStep === 1 && (
                <button type="submit" className="c-button" disabled={!title} onClick={() => this.onClickCreate()}>
                  Create
                </button>
              )}

    );
  }
}

NewWidgetPage.propTypes = {
  env: PropTypes.object.isRequired,
  currentStep: PropTypes.number.isRequired,
  datasets: PropTypes.array.isRequired,
  dataset: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  description: PropTypes.string.isRequired,
  caption: PropTypes.string.isRequired,
  setStep: PropTypes.func.isRequired,
  setDataset: PropTypes.func.isRequired,
  setTitle: PropTypes.func.isRequired,
  setDescription: PropTypes.func.isRequired,
  setCaption: PropTypes.func.isRequired,
  queryUrl: PropTypes.string.isRequired,
  redirectUrl: PropTypes.string.isRequired
};

function mapStateToProps(state) {
  return {
    env: state.env,
    currentStep: state.management.step,
    dataset: state.management.widgetCreation.dataset,
    title: state.management.widgetCreation.title,
    description: state.management.widgetCreation.description,
    caption: state.management.widgetCreation.caption
  };
}

function mapDispatchToProps(dispatch) {
  return {
    setStep: (...params) => dispatch(setStep(...params)),
    setDataset: (...params) => dispatch(setWidgetCreationDataset(...params)),
    setTitle: (...params) => dispatch(setWidgetCreationTitle(...params)),
    setDescription: (...params) => dispatch(setWidgetCreationDescription(...params)),
    setCaption: (...params) => dispatch(setWidgetCreationCaption(...params))
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(NewWidgetPage);
<commit_msg>Bring back the redirection at the end of the widget creation<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import WidgetEditor, { Modal, Tooltip, Icons, setConfig, VegaChart, getVegaTheme, getConfig } from 'widget-editor';

import ExtendedHeader from 'components/ExtendedHeader';
import StepsBar from 'components/StepsBar';
import Notification from 'components/Notification';

import { setStep, setWidgetCreationDataset, setWidgetCreationTitle, setWidgetCreationDescription, setWidgetCreationCaption } from 'redactions/management';

const STEPS = [
  {
    name: 'Dataset',
    description: 'Pick the dataset you want to use for the widget.'
  },
  {
    name: 'Visualization',
    description: 'Please use the selector to change the type of visualization and choose the columns you want to use.'
  }
];

class NewWidgetPage extends React.Component {
  constructor(props) {
    super(props);

    // We set the config of the widget editor
    const { env } = props;
    setConfig({
      url: env.apiUrl,
      env: env.apiEnv,
      applications: env.apiApplications,
      authUrl: env.controlTowerUrl,
      assetsPath: '/packs/images/',
      userToken: env.user.token || undefined
    });

    this.state = {
      // Error while retrieving the widgetConfig from the widget-editor
      widgetConfigError: false,
      // Whether the user has dismissed the warning when switching to
      // the advanced editor
      advancedEditorWarningAccepted: false,
      // Whether we're using the advanced editor
      advancedEditor: false,
      // State of the advanced editor
      widgetConfig: {},
      // Whether the preview of the avanced editor is loading
      previewLoading: false,
      // Error while saving the widget
      saveError: false
    };
  }

  componentDidUpdate(_, prevState) {
    if (!prevState.advancedEditor && this.state.advancedEditor && this.advancedEditor) {
      this.codeMirror = CodeMirror.fromTextArea(this.advancedEditor, {
        mode: 'javascript',
        autoCloseTags: true,
        lineWrapping: true,
        lineNumbers: true
      });

      this.codeMirror.on('change', () => {
        try {
          const widgetConfig = JSON.parse(this.codeMirror.getValue());
          this.setState({ widgetConfig });
        } catch (e) {
          // If there's an error in the JSON, we reset the widgetConfig
          // so the user sees the preview is empty
          this.setState({ widgetConfig: {} });
        }
      });
    }
  }

  /**
   * Event handler executed when the user clicks the "Create"
   * button on the second step
   */
  onClickCreate() {
    new Promise((resolve, reject) => { // eslint-disable-line no-new
      if (this.state.advancedEditor) {
        resolve(this.state.widgetConfig);
      } else {
        this.getWidgetConfig()
          .then(resolve)
          .catch(reject);
      }
    }).then((widgetConfig) => {
      const widgetObj = Object.assign(
        {},
        {
          name: this.props.title || null,
          description: this.props.description
        },
        { widgetConfig }
      );

      let metadataObj = null;
      if (this.props.caption) {
        metadataObj = {
          info: {
            caption: this.props.caption
          }
        };
      }

      const widget = Object.assign({}, widgetObj, {
        application: [getConfig().applications],
        published: false,
        default: false,
        dataset: this.props.dataset
      });

      const metadata = !metadataObj
        ? null
        : Object.assign({}, metadataObj, {
          language: getConfig().locale,
          application: getConfig().applications
        });

      fetch(this.props.queryUrl, {
        method: 'POST',
        body: JSON.stringify(Object.assign(
          {},
          { widget },
          { ...(this.state.advancedEditor ? {} : { metadata }) }
        )),
        credentials: 'include',
        headers: new Headers({
          'content-type': 'application/json'
        })
      }).then((res) => {
        if (res.ok) {
          window.location = this.props.redirectUrl;
        } else {
          throw new Error(res.statusText);
        }
      }).catch(() => {
        this.setState({ saveError: true });
      });
    }).catch(() => {
      // We display a warning in the UI
      this.setState({ widgetConfigError: true });
    });
  }

  /**
   * Event handler executed when the user toggles between
   * the "normal" and avanced editor
   */
  onToggleAdvancedEditor() {
    this.setState({
      advancedEditor: !this.state.advancedEditor,
      advancedEditorWarningAccepted: false
    });
  }

  render() {
    // eslint-disable-next-line no-shadow
    const { currentStep, setStep, datasets, dataset, setDataset,
      setTitle, setDescription, setCaption, title, description, caption, redirectUrl } = this.props;
    const { widgetConfigError, advancedEditor,
      advancedEditorWarningAccepted, widgetConfig, previewLoading, saveError } = this.state;

    let content;
    if (currentStep === 0) {
      content = (

                Dataset
                <select id="dataset" name="dataset" defaultValue={dataset || ''} onChange={e => setDataset(e.target.selectedOptions[0].value)}>
                  <option value="">Select a dataset</option>
                  {datasets.map(d => <option key={d.id} value={d.id}>{d.name}</option>)}
                </select>

      );
    } else {
      content = (

          <Modal />
          <Tooltip />
          <Icons />

                  Name
                  <input type="text" id="name" name="name" placeholder="My widget" value={title} onChange={e => setTitle(e.target.value)} />

                  Description
                  <textarea id="description" name="description" placeholder="Description" value={description} onChange={e => setDescription(e.target.value)} />

                    <input type="checkbox" id="advanced-editor" name="advanced-editor" checked={advancedEditor} onClick={() => this.onToggleAdvancedEditor()} />
                    Use the advanced editor to create the widget manually

              { !advancedEditor && (
                <WidgetEditor
                  datasetId={dataset}
                  widgetTitle={title}
                  widgetCaption={caption}
                  saveButtonMode="never"
                  embedButtonMode="never"
                  onChangeWidgetTitle={t => setTitle(t)}
                  onChangeWidgetCaption={c => setCaption(c)}
                  provideWidgetConfig={(func) => { this.getWidgetConfig = func; }}
                />
              )}
              { advancedEditor && (

                    <textarea
                      ref={(el) => { this.advancedEditor = el; }}
                      defaultValue={JSON.stringify(widgetConfig)}
                    />

                    { previewLoading &&
 }
                    <VegaChart
                      data={widgetConfig}
                      theme={getVegaTheme()}
                      showLegend
                      reloadOnResize
                      toggleLoading={loading => this.setState({ previewLoading: loading })}
                      getForceUpdate={(func) => { this.forceChartUpdate = func; }}
                    />

              )}

      );
    }

    return (

        <ExtendedHeader title={STEPS[currentStep].name} subTitle={STEPS[currentStep].description} />
        <StepsBar
          steps={STEPS.map(s => s.name)}
          currentStep={currentStep}
          onChangeStep={setStep}
        />

        {widgetConfigError && (
          <Notification
            type="warning"
            content="Unable to create the widget"
            additionalContent="Make sure the visualization is correctly previewed before submitting the widget."
            onClose={() => this.setState({ widgetConfigError: false })}
          />
        )}

        {saveError && (
          <Notification
            type="error"
            content="Unable to create the widget"
            additionalContent="Please try again later."
            onClose={() => this.setState({ saveError: false })}
          />
        )}

        {advancedEditor && !advancedEditorWarningAccepted && (
          <Notification
            type="warning"
            content="Onced you've created the widget with the advanced editor, you won't be able to use the simple interface."
            dialogButtons
            closeable={false}
            onCancel={() => this.setState({ advancedEditor: false })}
            onContinue={() => this.setState({ advancedEditorWarningAccepted: true })}
            onClose={() => this.setState({ advancedEditor: false })}
          />
        )}

        {content}

              Cancel

              { currentStep >= 1 && (
                <button type="button" className="c-button -outline -dark-text" onClick={() => setStep(currentStep - 1)}>
                  Back
                </button>
              )}
              { currentStep === 0 && (
                <button type="submit" className="c-button" disabled={!dataset} onClick={() => setStep(currentStep + 1)}>
                  Continue
                </button>
              )}
              { currentStep === 1 && (
                <button type="submit" className="c-button" disabled={!title} onClick={() => this.onClickCreate()}>
                  Create
                </button>
              )}

    );
  }
}

NewWidgetPage.propTypes = {
  env: PropTypes.object.isRequired,
  currentStep: PropTypes.number.isRequired,
  datasets: PropTypes.array.isRequired,
  dataset: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  description: PropTypes.string.isRequired,
  caption: PropTypes.string.isRequired,
  setStep: PropTypes.func.isRequired,
  setDataset: PropTypes.func.isRequired,
  setTitle: PropTypes.func.isRequired,
  setDescription: PropTypes.func.isRequired,
  setCaption: PropTypes.func.isRequired,
  queryUrl: PropTypes.string.isRequired,
  redirectUrl: PropTypes.string.isRequired
};

function mapStateToProps(state) {
  return {
    env: state.env,
    currentStep: state.management.step,
    dataset: state.management.widgetCreation.dataset,
    title: state.management.widgetCreation.title,
    description: state.management.widgetCreation.description,
    caption: state.management.widgetCreation.caption
  };
}

function mapDispatchToProps(dispatch) {
  return {
    setStep: (...params) => dispatch(setStep(...params)),
    setDataset: (...params) => dispatch(setWidgetCreationDataset(...params)),
    setTitle: (...params) => dispatch(setWidgetCreationTitle(...params)),
    setDescription: (...params) => dispatch(setWidgetCreationDescription(...params)),
    setCaption: (...params) => dispatch(setWidgetCreationCaption(...params))
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(NewWidgetPage);
<|endoftext|>


<commit_before>$(document).ready(function() {
  game = new Game(81);
  $('#container').html(game.render())

  $('body').on('mousedown', '.cell', function(event) {
    switch (event.which) {
      case 1:
        game.click(this.id)
        break;
      case 3:
        game.flag(this.id)
        break;
    }
    if (game.checkForWin()){
      alert("YOU WIN! Time: " + $('#clock').text())
    }

    $('#container').html(game.render())
  });
  var sec = 0;
  function pad ( val ) { return val > 9 ? val : "0" + val; }
  function reset () {
    sec = 0

  }
  setInterval( function(){
      $("#seconds").html(pad(++sec%60));
      $("#minutes").html(pad(parseInt(sec/60,10)));
  }, 1000);

  $('#refresh').click(function(event){
    console.log(game)
    game.newGame()
    $('#container').html(game.render())
    reset()
  })

});

<commit_msg>clock with bugs<commit_after>$(document).ready(function() {
  $('#beginner').on('click', function(event){
    game = new Game(9,9);
    $('#container').html(game.render())
    clock()
  })
  $('#intermediate').on('click', function(event){
    game = new Game(16,16);
    $('#container').html(game.render())
    clock()
  })
  $('#expert').on('click', function(event){
    game = new Game(30,16);
    $('#container').html(game.render())
    clock()
  })

  $('body').on('mousedown', '.cell', function(event) {
    switch (event.which) {
      case 1:
      // console.log(this.id)
        game.click(this.id.split(' '))
        break;
      case 3:
        game.flag(this.id.split(' '))
        break;
    }
    if (game.checkForWin()){
      alert("YOU WIN! Time: " + $('#clock').text())
    }

    $('#container').html(game.render())
  });

  $('#refresh').click(function(event){
    game.newGame()
    $('#container').html(game.render())
    reset()
  })

});

var clock = function(){
  var sec = 0;
  function pad ( val ) { return val > 9 ? val : "0" + val; }
  function reset () {
    sec = 0
  }
  setInterval( function(){
      $("#seconds").html(pad(++sec%60));
      $("#minutes").html(pad(parseInt(sec/60,10)));
  }, 1000);
}<|endoftext|>


<commit_before>var assert = require('assert');
var getImgSrc = require('..');

describe('get-img-src', function() {

  it('should return img src attributes within double quotes when they exist', function(done) {
    getImgSrc('', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg']);
      done();
    });
  });

  it('should return img src attributes within single quotes when they exist', function(done) {
    getImgSrc('', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg']);
      done();
    });
  });

  it('should return multiple img src attributes within single quotes when they exist', function(done) {
    getImgSrc('', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg', 'some-other-image.jpg']);
      done();
    });
  });

  it('should return an empty array when there are no images', function(done) {
    getImgSrc('HTML without an image', function(err, srcs) {
      assert.deepEqual(srcs, []);
      done();
    });
  });
});
<commit_msg>Reformat test<commit_after>var assert = require('assert');
var getImgSrc = require('..');

describe('get-img-src', function() {

  it('should return img src attributes within double quotes when they exist', function(done) {
    getImgSrc('', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg']);
      done();
    });
  });

  it('should return img src attributes within single quotes when they exist', function(done) {
    getImgSrc('', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg']);
      done();
    });
  });

  it('should return multiple img src attributes within single quotes when they exist', function(done) {
    getImgSrc('' +
              '', function(err, srcs) {
      assert.deepEqual(srcs, ['some-image.jpg', 'some-other-image.jpg']);
      done();
    });
  });

  it('should return an empty array when there are no images', function(done) {
    getImgSrc('HTML without an image', function(err, srcs) {
      assert.deepEqual(srcs, []);
      done();
    });
  });
});
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');

/**
 * @classdesc
 * A Render Target encapsulates a WebGL framebuffer and the WebGL Texture that displays it.
 *
 * Instances of this class are created by, and belong to WebGL Pipelines.
 *
 * @class RenderTarget
 * @memberof Phaser.Renderer.WebGL
 * @constructor
 * @since 3.50.0
 *
 * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The WebGLPipeline to which this Render Target belongs.
 * @param {number} width - The width of the WebGL Pipeline.
 * @param {number} height - The height of the WebGL Pipeline.
 * @param {number} scale - A value between 0 and 1. Controls the size of this Render Target in relation to the Renderer.
 * @param {number} minFilter - The minFilter mode of the texture when created. 0 is `LINEAR`, 1 is `NEAREST`.
 * @param {boolean} autoClear - Automatically clear this framebuffer when bound?
 */
var RenderTarget = new Class({

    initialize:

    function RenderTarget (pipeline, width, height, scale, minFilter, autoClear)
    {
        /**
         * A reference to the WebGLPipeline that owns this Render Target.
         *
         * A Render Target class can only belong to a single pipeline.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#pipeline
         * @type {Phaser.Renderer.WebGL.WebGLPipeline}
         * @since 3.50.0
         */
        this.pipeline = pipeline;

        /**
         * A reference to the WebGLRenderer instance.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#renderer
         * @type {Phaser.Renderer.WebGL.WebGLRenderer}
         * @since 3.50.0
         */
        this.renderer = pipeline.renderer;

        /**
         * The WebGLFramebuffer of this Render Target.
         *
         * This is created in the `RenderTarget.resize` method.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#framebuffer
         * @type {WebGLFramebuffer}
         * @since 3.50.0
         */
        this.framebuffer = null;

        /**
         * The WebGLTexture of this Render Target.
         *
         * This is created in the `RenderTarget.resize` method.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#texture
         * @type {WebGLTexture}
         * @since 3.50.0
         */
        this.texture = null;

        /**
         * The width of the texture.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#width
         * @type {number}
         * @readonly
         * @since 3.50.0
         */
        this.width = 0;

        /**
         * The height of the texture.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#height
         * @type {number}
         * @readonly
         * @since 3.50.0
         */
        this.height = 0;

        /**
         * A value between 0 and 1. Controls the size of this Render Target in relation to the Renderer.
         *
         * A value of 1 matches it. 0.5 makes the Render Target half the size of the renderer, etc.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#scale
         * @type {number}
         * @since 3.50.0
         */
        this.scale = scale;

        /**
         * The minFilter mode of the texture. 0 is `LINEAR`, 1 is `NEAREST`.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#minFilter
         * @type {number}
         * @since 3.50.0
         */
        this.minFilter = minFilter;

        /**
         * Controls if this Render Target is automatically cleared (via `gl.COLOR_BUFFER_BIT`)
         * during the `RenderTarget.bind` method.
         *
         * If you need more control over how, or if, the target is cleared, you can disable
         * this via the config on creation, or even toggle it directly at runtime.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#autoClear
         * @type {boolean}
         * @since 3.50.0
         */
        this.autoClear = autoClear;

        this.resize(width, height);
    },

    /**
     * Resizes this Render Target.
     *
     * Deletes both the frame buffer and texture, if they exist and then re-creates
     * them using the new sizes.
     *
     * This method is called automatically by the pipeline during its resize handler.
     *
     * @method Phaser.Renderer.WebGL.RenderTarget#resize
     * @since 3.50.0
     *
     * @param {number} width - The new width of the WebGL Pipeline.
     * @param {number} height - The new height of the WebGL Pipeline.
     *
     * @return {this} This RenderTarget instance.
     */
    resize: function (width, height)
    {
        var renderer = this.renderer;

        renderer.deleteFramebuffer(this.framebuffer);

        renderer.deleteTexture(this.texture);

        width *= this.scale;
        height *= this.scale;

        this.texture = renderer.createTextureFromSource(null, width, height, this.minFilter);
        this.framebuffer = renderer.createFramebuffer(width, height, this.texture, false);

        this.width = width;
        this.height = height;

        return this;
    },

    /**
     * Pushes this Render Target as the current frame buffer of the renderer.
     *
     * If `autoClear` is set, then clears the texture.
     *
     * @method Phaser.Renderer.WebGL.RenderTarget#bind
     * @since 3.50.0
     */
    bind: function ()
    {
        this.renderer.pushFramebuffer(this.framebuffer);

        if (this.autoClear)
        {
            var gl = this.pipeline.gl;

            gl.clearColor(0, 0, 0, 0);

            gl.clear(gl.COLOR_BUFFER_BIT);
        }
    },

    /**
     * Unbinds this Render Target.
     *
     * @name Phaser.Renderer.WebGL.RenderTarget#unbind
     * @since 3.50.0
     */
    unbind: function ()
    {
        this.renderer.popFramebuffer();
    },

    /**
     * Removes all external references from this class and deletes the
     * WebGL framebuffer and texture instances.
     *
     * Does not remove this Render Target from the parent pipeline.
     *
     * @name Phaser.Renderer.WebGL.RenderTarget#destroy
     * @since 3.50.0
     */
    destroy: function ()
    {
        var renderer = this.pipeline.renderer;

        renderer.deleteFramebuffer(this.framebuffer);
        renderer.deleteTexture(this.texture);

        this.pipeline = null;
        this.framebuffer = null;
        this.texture = null;
    }

});

module.exports = RenderTarget;
<commit_msg>Return the freshly bound fbo<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../../utils/Class');

/**
 * @classdesc
 * A Render Target encapsulates a WebGL framebuffer and the WebGL Texture that displays it.
 *
 * Instances of this class are created by, and belong to WebGL Pipelines.
 *
 * @class RenderTarget
 * @memberof Phaser.Renderer.WebGL
 * @constructor
 * @since 3.50.0
 *
 * @param {Phaser.Renderer.WebGL.WebGLPipeline} pipeline - The WebGLPipeline to which this Render Target belongs.
 * @param {number} width - The width of the WebGL Pipeline.
 * @param {number} height - The height of the WebGL Pipeline.
 * @param {number} scale - A value between 0 and 1. Controls the size of this Render Target in relation to the Renderer.
 * @param {number} minFilter - The minFilter mode of the texture when created. 0 is `LINEAR`, 1 is `NEAREST`.
 * @param {boolean} autoClear - Automatically clear this framebuffer when bound?
 */
var RenderTarget = new Class({

    initialize:

    function RenderTarget (pipeline, width, height, scale, minFilter, autoClear)
    {
        /**
         * A reference to the WebGLPipeline that owns this Render Target.
         *
         * A Render Target class can only belong to a single pipeline.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#pipeline
         * @type {Phaser.Renderer.WebGL.WebGLPipeline}
         * @since 3.50.0
         */
        this.pipeline = pipeline;

        /**
         * A reference to the WebGLRenderer instance.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#renderer
         * @type {Phaser.Renderer.WebGL.WebGLRenderer}
         * @since 3.50.0
         */
        this.renderer = pipeline.renderer;

        /**
         * The WebGLFramebuffer of this Render Target.
         *
         * This is created in the `RenderTarget.resize` method.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#framebuffer
         * @type {WebGLFramebuffer}
         * @since 3.50.0
         */
        this.framebuffer = null;

        /**
         * The WebGLTexture of this Render Target.
         *
         * This is created in the `RenderTarget.resize` method.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#texture
         * @type {WebGLTexture}
         * @since 3.50.0
         */
        this.texture = null;

        /**
         * The width of the texture.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#width
         * @type {number}
         * @readonly
         * @since 3.50.0
         */
        this.width = 0;

        /**
         * The height of the texture.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#height
         * @type {number}
         * @readonly
         * @since 3.50.0
         */
        this.height = 0;

        /**
         * A value between 0 and 1. Controls the size of this Render Target in relation to the Renderer.
         *
         * A value of 1 matches it. 0.5 makes the Render Target half the size of the renderer, etc.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#scale
         * @type {number}
         * @since 3.50.0
         */
        this.scale = scale;

        /**
         * The minFilter mode of the texture. 0 is `LINEAR`, 1 is `NEAREST`.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#minFilter
         * @type {number}
         * @since 3.50.0
         */
        this.minFilter = minFilter;

        /**
         * Controls if this Render Target is automatically cleared (via `gl.COLOR_BUFFER_BIT`)
         * during the `RenderTarget.bind` method.
         *
         * If you need more control over how, or if, the target is cleared, you can disable
         * this via the config on creation, or even toggle it directly at runtime.
         *
         * @name Phaser.Renderer.WebGL.RenderTarget#autoClear
         * @type {boolean}
         * @since 3.50.0
         */
        this.autoClear = autoClear;

        this.resize(width, height);
    },

    /**
     * Resizes this Render Target.
     *
     * Deletes both the frame buffer and texture, if they exist and then re-creates
     * them using the new sizes.
     *
     * This method is called automatically by the pipeline during its resize handler.
     *
     * @method Phaser.Renderer.WebGL.RenderTarget#resize
     * @since 3.50.0
     *
     * @param {number} width - The new width of the WebGL Pipeline.
     * @param {number} height - The new height of the WebGL Pipeline.
     *
     * @return {this} This RenderTarget instance.
     */
    resize: function (width, height)
    {
        var renderer = this.renderer;

        renderer.deleteFramebuffer(this.framebuffer);

        renderer.deleteTexture(this.texture);

        width *= this.scale;
        height *= this.scale;

        this.texture = renderer.createTextureFromSource(null, width, height, this.minFilter);
        this.framebuffer = renderer.createFramebuffer(width, height, this.texture, false);

        this.width = width;
        this.height = height;

        return this;
    },

    /**
     * Pushes this Render Target as the current frame buffer of the renderer.
     *
     * If `autoClear` is set, then clears the texture.
     *
     * @method Phaser.Renderer.WebGL.RenderTarget#bind
     * @since 3.50.0
     */
    bind: function ()
    {
        this.renderer.pushFramebuffer(this.framebuffer);

        if (this.autoClear)
        {
            var gl = this.pipeline.gl;

            gl.clearColor(0, 0, 0, 0);

            gl.clear(gl.COLOR_BUFFER_BIT);
        }
    },

    /**
     * Unbinds this Render Target.
     *
     * @name Phaser.Renderer.WebGL.RenderTarget#unbind
     * @since 3.50.0
     *
     * @return {WebGLFramebuffer} The Framebuffer that was set, or `null` if there aren't any more in the stack.
     */
    unbind: function ()
    {
        return this.renderer.popFramebuffer();
    },

    /**
     * Removes all external references from this class and deletes the
     * WebGL framebuffer and texture instances.
     *
     * Does not remove this Render Target from the parent pipeline.
     *
     * @name Phaser.Renderer.WebGL.RenderTarget#destroy
     * @since 3.50.0
     */
    destroy: function ()
    {
        var renderer = this.pipeline.renderer;

        renderer.deleteFramebuffer(this.framebuffer);
        renderer.deleteTexture(this.texture);

        this.pipeline = null;
        this.framebuffer = null;
        this.texture = null;
    }

});

module.exports = RenderTarget;
<|endoftext|>