<commit_before>/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2002-2016 Pentaho Corporation..  All rights reserved.
*/

define(['common-ui/util/util', 'pentaho/common/Messages', "dijit/registry", "common-ui/prompting/api/PromptingAPI", "common-ui/jquery-clean", "underscore"],

    function(util, Messages, registry, PromptingAPI, $, _) {
      var _api =  new PromptingAPI('promptPanel');

  return function() {
    return logged({
      // The current prompt mode
      mode: 'INITIAL',

      /**
       * Gets the prompt api instance
       *
       * @type string
       * @readonly
       */
      get api () {
        return _api;
      },

      _buildReportContentOptions: function(renderMode) {
        var options = util.getUrlParameters();

        if (this.panel) {
          $.extend(options, this.api.operation.getParameterValues());
        }

        options['renderMode'] = renderMode;

        // Never send the session back. This is generated by the server.
        delete options['::session'];

        return options;
      },

      /**
       * Gets the prompt panel instance, retrieved via the prompt api
       *
       * @returns {*}
       */
      //TODO: to be removed once prompt api is totally applied
      get panel () {
        return this._panel;
      },

      /**
       * Gets the prompt panel instance, retrieved via the prompt api
       *
       * @param panel
       */
      //TODO: to be removed once prompt api is totally applied
      set panel (panel) {
        this._panel = panel;
      },

      load: function() {
        Messages.addUrlBundle('reportviewer', CONTEXT_PATH+'i18n?plugin=reporting&name=reportviewer/messages/messages');
      },

      /*
       * Gets a property value from the state object in the Prompting API.
       *
       * @param prop The property which value to fetch.
       *
       * @private
       */
      _getStateProperty: function(prop) {
        return this.api.operation.state()[prop];
      },

      createPromptPanel: function() {
        this.api.operation.render(function(api, callback) {
          var paramDefnCallback = function(xml) {
            var paramDefn = this.parseParameterDefinition(xml);

            // A first request is made with promptMode='INITIAL' and renderMode='PARAMETER'.
            //
            // The response will not have page count information (pagination was not performed), but simply information about the prompt parameters (paramDefn).
            //
            // When paramDefn.allowAutoSubmit() is true,
            // And no validation errors/required parameters exist to be specified, TODO: Don't think that this is being checked here!
            // Then a second request is made with promptMode='MANUAL' and renderMode='XML' is performed.
            //
            // When the response to the second request arrives,
            // Then the prompt panel is rendered, including with page count information, and  the report content is loaded and shown.
            //
            // [PIR-1163] Used 'inSchedulerDialog' variable to make sure that the second request is not sent if it's scheduler dialog.
            // Because the scheduler needs only parameters without full XML.
            if (!inSchedulerDialog && this.mode === 'INITIAL' && paramDefn.allowAutoSubmit()) {
              this.fetchParameterDefinition(paramDefnCallback.bind(this), 'MANUAL');
              return;
            }

            // Make sure we retain the current auto-submit setting
            //  pp.getAutoSubmitSetting -> pp.autoSubmit, which is updated by the check-box
            var autoSubmit = this.panel && this._getStateProperty('autoSubmit');
            if (autoSubmit != null) {
              paramDefn.autoSubmitUI = autoSubmit;
            }
            callback(paramDefn);

            this._createPromptPanelFetchCallback(paramDefn);
            this.hideGlassPane();
          };
          this.fetchParameterDefinition(paramDefnCallback.bind(this), this.mode);
        }.bind(this));
       },

      _createPromptPanelFetchCallback: _.once(function(paramDefn) {
        // Temporary used prompt panel instance from api. Need delete it after fully applying prompting api functions.
        this.panel = this.api.operation._getPromptPanel();
        this.panel.setParamDefn(paramDefn);

        this.panel.submitStart = this.submitStart.bind(this);
        this.panel.ready = this.ready.bind(this);

        this.initPromptPanel();

        this._hideLoadingIndicator();
      }),

      _hideLoadingIndicator: function() {
        try{
          if (window.top.hideLoadingIndicator) {
            window.top.hideLoadingIndicator();
          } else if (window.parent.hideLoadingIndicator) {
            window.parent.hideLoadingIndicator();
          }
        } catch (ignored) {} // Ignore "Same-origin policy" violation in embedded IFrame
      },

      initPromptPanel: function() {
        this.api.operation.init();
      },

      showGlassPane: function() {
        // Show glass pane when updating the prompt.
        registry.byId('glassPane').show();
      },

      hideGlassPane: function() {
        registry.byId('glassPane').hide();
      },

      ready: function() {
        this.hideGlassPane();
      },

      /**
       * Called when the prompt-panel component's submit button is pressed (mouse-down only).
       */
      submitStart: function(promptPanel) {
        alert('submit start fired for panel: ' + promptPanel);
      },

      parseParameterDefinition: function(xmlString) {
        xmlString = this.removeControlCharacters(xmlString);
        return this.api.util.parseParameterXml(xmlString);
      },

      /**
       * This method will remove illegal control characters from the text in the range of � through
       * SEE:  PRD-3882 and ESR-1953
       */
      removeControlCharacters : function(inStr) {
        for (var i = 0; i <= 31; i++) {
          var safe = i;
          if (i < 10) {
            safe = '0' + i;
          }
          eval('inStr = inStr.replace(/\&#' + safe + ';/g, "")');
        }
        return inStr;
      },

      checkSessionTimeout: function(content, args) {
        if (content.status == 401 || this.isSessionTimeoutResponse(content)) {
          this.handleSessionTimeout(args);
          return true;
        }
        return false;
      },

      /**
       * @return true if the content is the login page.
       */
      isSessionTimeoutResponse: function(content) {
        if(String(content).indexOf('j_spring_security_check') != -1) {
          // looks like we have the login page returned to us
          return true;
        }
        return false;
      },

      /**
       * Prompts the user to relog in if they're within PUC, otherwise displays a dialog
       * indicating their session has expired.
       *
       * @return true if the session has timed out
       */
      handleSessionTimeout: function(args) {
        var callback = function() {
          this.fetchParameterDefinition.apply(this, args);
        }.bind(this);

        this.reauthenticate(callback);
      },

      reauthenticate: function(f) {
        var isRunningIFrameInSameOrigin = null;
        try {
          var ignoredCheckCanReachOutToParent = window.parent.mantle_initialized;
          isRunningIFrameInSameOrigin = true;
        } catch (ignoredSameOriginPolicyViolation) {
          // IFrame is running embedded in a web page in another domain
          isRunningIFrameInSameOrigin = false;
        }

        if(isRunningIFrameInSameOrigin && top.mantle_initialized) {
          var callback = {
            loginCallback : f
          }
          window.parent.authenticate(callback);
        } else {
          this.showMessageBox(
            Messages.getString('SessionExpiredComment'),
            Messages.getString('SessionExpired'),
            Messages.getString('OK'),
            undefined,
            undefined,
            undefined,
            true
          );
        }
      },

      /**
       * @private Sequence number to detect concurrent fetchParameterDefinition calls.
       * Only the response to the last call will be processed.
       */
      _fetchParamDefId: -1,

      /**
       * Loads the parameter xml definition from the server.
       * @param {function} callback function to call when successful.
       * The callback signature is:
       *
void function(xmlString)

       *  and is called in the context of the report viewer prompt instance.
       * @param {string} [promptMode='MANUAL'] the prompt mode to request from server:
       *  x INITIAL   - first time
       *  x MANUAL    - user pressed the submit button (or, when autosubmit, after INITIAL fetch)
       *  x USERINPUT - due to a change + auto-submit
       *
       * If not provided, 'MANUAL' will be used.
       */
      fetchParameterDefinition: function(callback, promptMode) {
        var me = this;

        var fetchParamDefId = ++me._fetchParamDefId;

        me.showGlassPane();

        if (!promptMode) {
          promptMode = 'MANUAL';
        } else if (promptMode == 'USERINPUT') {
          // Hide glass pane to prevent user from being blocked from changing his selection
          me.hideGlassPane();
        }

        if (me.clicking) {
          // If "Upgrading" a Change to a Submit we do not want to process the next Submit Click, if any
          var upgrade = (promptMode === 'USERINPUT');

          me.ignoreNextClickSubmit = upgrade;

          // Also, force the Change to behave as if AutoSubmit was on!
          if (me.panel) { me.panel.forceAutoSubmit = upgrade; }

          delete me.clicking;
        }

        // Store mode so we can check if we need to refresh the report content or not in the view
        // As only the last request's response is processed, the last value of mode is actually the correct one.
        me.mode = promptMode;

        var options = me._buildReportContentOptions(this._getParameterDefinitionRenderMode(promptMode));

        var args = arguments;

        var onSuccess = logged('fetchParameterDefinition_success', function(xmlString) {
          if (me.checkSessionTimeout(xmlString, args)) { return; }

          // Another request was made after this one, so this one is ignored.
          if (fetchParamDefId !== me._fetchParamDefId) { return; }

          try {
            callback(xmlString);
          } catch (e) {
            me.onFatalError(e);
          } finally {
            me.mode = 'USERINPUT';
          }
        });

        var onError = function(e) {
          if (!me.checkSessionTimeout(e, args)) {
            me.onFatalError(e);
          }
        };

        $.ajax({
          async:   true,
          traditional: true, // Controls internal use of $.param() to serialize data to the url/body.
          cache:   false,
          type:    'POST',
          url:     me.getParameterUrl(),
          data:    options,
          dataType:'text',
          success: onSuccess,
          error:   onError
        });
      },

      _getParameterDefinitionRenderMode: function(promptMode) {
        switch(promptMode) {
          case 'INITIAL':
              return 'PARAMETER';

          case 'USERINPUT':
            if (!this.panel || !this._getStateProperty('autoSubmit')) {
              return 'PARAMETER';
            }
            break;
        }

        return 'XML';
      },

      getParameterUrl: function() {
        return 'parameter';
      },

      showMessageBox: function( message, dialogTitle, button1Text, button1Callback, button2Text, button2Callback, blocker ) {
        var messageBox = registry.byId('messageBox');

        messageBox.setTitle(dialogTitle);
        messageBox.setMessage(message);

        if (blocker) {
          messageBox.setButtons([]);
        } else {
          var closeFunc = (function() {
            this.api.ui.hideProgressIndicator();
            messageBox.hide.call(messageBox);
          }).bind(this);

          if(!button1Text) {
            button1Text = Messages.getString('OK');
          }
          if(!button1Callback) {
            button1Callback = closeFunc;
          }

          messageBox.onCancel = closeFunc;

          if(button2Text) {
            messageBox.callbacks = [
              button1Callback,
              button2Callback
            ];
            messageBox.setButtons([button1Text,button2Text]);
          } else {
            messageBox.callbacks = [
              button1Callback
            ];
            messageBox.setButtons([button1Text]);
          }
        }

        this.api.ui.showProgressIndicator();
        messageBox.show();
      },

      /**
       * Called when there is a fatal error during parameter definition fetching/parsing
       *
       * @param e Error/exception encountered
       */
      onFatalError: function(e) {
        var errorMsg = Messages.getString('ErrorParsingParamXmlMessage');
        if (typeof console !== 'undefined' && console.log) {
          console.log(errorMsg + ": " + e);
        }
        this.showMessageBox(
          errorMsg,
          Messages.getString('FatalErrorTitle'));
      }
    }); // return logged
  }; // return function
});
<commit_msg>[BACKLOG-6961] Fixed usage of underscore dependency     - using "common-ui/underscore" instead of "underscore" module ID<commit_after>/*!
 * This program is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 * or from the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * Copyright (c) 2002-2016 Pentaho Corporation..  All rights reserved.
 */

define([
  "common-ui/util/util",
  "pentaho/common/Messages",
  "dijit/registry",
  "common-ui/prompting/api/PromptingAPI",
  "common-ui/jquery-clean",
  "common-ui/underscore"
], function(util, Messages, registry, PromptingAPI, $, _) {

  var _api =  new PromptingAPI('promptPanel');

  return function() {
    return logged({
      // The current prompt mode
      mode: 'INITIAL',

      /**
       * Gets the prompt api instance
       *
       * @type string
       * @readonly
       */
      get api () {
        return _api;
      },

      _buildReportContentOptions: function(renderMode) {
        var options = util.getUrlParameters();

        if (this.panel) {
          $.extend(options, this.api.operation.getParameterValues());
        }

        options['renderMode'] = renderMode;

        // Never send the session back. This is generated by the server.
        delete options['::session'];

        return options;
      },

      /**
       * Gets the prompt panel instance, retrieved via the prompt api
       *
       * @returns {*}
       */
      //TODO: to be removed once prompt api is totally applied
      get panel () {
        return this._panel;
      },

      /**
       * Gets the prompt panel instance, retrieved via the prompt api
       *
       * @param panel
       */
      //TODO: to be removed once prompt api is totally applied
      set panel (panel) {
        this._panel = panel;
      },

      load: function() {
        Messages.addUrlBundle('reportviewer', CONTEXT_PATH+'i18n?plugin=reporting&name=reportviewer/messages/messages');
      },

      /*
       * Gets a property value from the state object in the Prompting API.
       *
       * @param prop The property which value to fetch.
       *
       * @private
       */
      _getStateProperty: function(prop) {
        return this.api.operation.state()[prop];
      },

      createPromptPanel: function() {
        this.api.operation.render(function(api, callback) {
          var paramDefnCallback = function(xml) {
            var paramDefn = this.parseParameterDefinition(xml);

            // A first request is made with promptMode='INITIAL' and renderMode='PARAMETER'.
            //
            // The response will not have page count information (pagination was not performed), but simply information about the prompt parameters (paramDefn).
            //
            // When paramDefn.allowAutoSubmit() is true,
            // And no validation errors/required parameters exist to be specified, TODO: Don't think that this is being checked here!
            // Then a second request is made with promptMode='MANUAL' and renderMode='XML' is performed.
            //
            // When the response to the second request arrives,
            // Then the prompt panel is rendered, including with page count information, and  the report content is loaded and shown.
            //
            // [PIR-1163] Used 'inSchedulerDialog' variable to make sure that the second request is not sent if it's scheduler dialog.
            // Because the scheduler needs only parameters without full XML.
            if (!inSchedulerDialog && this.mode === 'INITIAL' && paramDefn.allowAutoSubmit()) {
              this.fetchParameterDefinition(paramDefnCallback.bind(this), 'MANUAL');
              return;
            }

            // Make sure we retain the current auto-submit setting
            //  pp.getAutoSubmitSetting -> pp.autoSubmit, which is updated by the check-box
            var autoSubmit = this.panel && this._getStateProperty('autoSubmit');
            if (autoSubmit != null) {
              paramDefn.autoSubmitUI = autoSubmit;
            }
            callback(paramDefn);

            this._createPromptPanelFetchCallback(paramDefn);
            this.hideGlassPane();
          };
          this.fetchParameterDefinition(paramDefnCallback.bind(this), this.mode);
        }.bind(this));
       },

      _createPromptPanelFetchCallback: _.once(function(paramDefn) {
        // Temporary used prompt panel instance from api. Need delete it after fully applying prompting api functions.
        this.panel = this.api.operation._getPromptPanel();
        this.panel.setParamDefn(paramDefn);

        this.panel.submitStart = this.submitStart.bind(this);
        this.panel.ready = this.ready.bind(this);

        this.initPromptPanel();

        this._hideLoadingIndicator();
      }),

      _hideLoadingIndicator: function() {
        try{
          if (window.top.hideLoadingIndicator) {
            window.top.hideLoadingIndicator();
          } else if (window.parent.hideLoadingIndicator) {
            window.parent.hideLoadingIndicator();
          }
        } catch (ignored) {} // Ignore "Same-origin policy" violation in embedded IFrame
      },

      initPromptPanel: function() {
        this.api.operation.init();
      },

      showGlassPane: function() {
        // Show glass pane when updating the prompt.
        registry.byId('glassPane').show();
      },

      hideGlassPane: function() {
        registry.byId('glassPane').hide();
      },

      ready: function() {
        this.hideGlassPane();
      },

      /**
       * Called when the prompt-panel component's submit button is pressed (mouse-down only).
       */
      submitStart: function(promptPanel) {
        alert('submit start fired for panel: ' + promptPanel);
      },

      parseParameterDefinition: function(xmlString) {
        xmlString = this.removeControlCharacters(xmlString);
        return this.api.util.parseParameterXml(xmlString);
      },

      /**
       * This method will remove illegal control characters from the text in the range of � through
       * SEE:  PRD-3882 and ESR-1953
       */
      removeControlCharacters : function(inStr) {
        for (var i = 0; i <= 31; i++) {
          var safe = i;
          if (i < 10) {
            safe = '0' + i;
          }
          eval('inStr = inStr.replace(/\&#' + safe + ';/g, "")');
        }
        return inStr;
      },

      checkSessionTimeout: function(content, args) {
        if (content.status == 401 || this.isSessionTimeoutResponse(content)) {
          this.handleSessionTimeout(args);
          return true;
        }
        return false;
      },

      /**
       * @return true if the content is the login page.
       */
      isSessionTimeoutResponse: function(content) {
        if(String(content).indexOf('j_spring_security_check') != -1) {
          // looks like we have the login page returned to us
          return true;
        }
        return false;
      },

      /**
       * Prompts the user to relog in if they're within PUC, otherwise displays a dialog
       * indicating their session has expired.
       *
       * @return true if the session has timed out
       */
      handleSessionTimeout: function(args) {
        var callback = function() {
          this.fetchParameterDefinition.apply(this, args);
        }.bind(this);

        this.reauthenticate(callback);
      },

      reauthenticate: function(f) {
        var isRunningIFrameInSameOrigin = null;
        try {
          var ignoredCheckCanReachOutToParent = window.parent.mantle_initialized;
          isRunningIFrameInSameOrigin = true;
        } catch (ignoredSameOriginPolicyViolation) {
          // IFrame is running embedded in a web page in another domain
          isRunningIFrameInSameOrigin = false;
        }

        if(isRunningIFrameInSameOrigin && top.mantle_initialized) {
          var callback = {
            loginCallback : f
          }
          window.parent.authenticate(callback);
        } else {
          this.showMessageBox(
            Messages.getString('SessionExpiredComment'),
            Messages.getString('SessionExpired'),
            Messages.getString('OK'),
            undefined,
            undefined,
            undefined,
            true
          );
        }
      },

      /**
       * @private Sequence number to detect concurrent fetchParameterDefinition calls.
       * Only the response to the last call will be processed.
       */
      _fetchParamDefId: -1,

      /**
       * Loads the parameter xml definition from the server.
       * @param {function} callback function to call when successful.
       * The callback signature is:
       *
void function(xmlString)

       *  and is called in the context of the report viewer prompt instance.
       * @param {string} [promptMode='MANUAL'] the prompt mode to request from server:
       *  x INITIAL   - first time
       *  x MANUAL    - user pressed the submit button (or, when autosubmit, after INITIAL fetch)
       *  x USERINPUT - due to a change + auto-submit
       *
       * If not provided, 'MANUAL' will be used.
       */
      fetchParameterDefinition: function(callback, promptMode) {
        var me = this;

        var fetchParamDefId = ++me._fetchParamDefId;

        me.showGlassPane();

        if (!promptMode) {
          promptMode = 'MANUAL';
        } else if (promptMode == 'USERINPUT') {
          // Hide glass pane to prevent user from being blocked from changing his selection
          me.hideGlassPane();
        }

        if (me.clicking) {
          // If "Upgrading" a Change to a Submit we do not want to process the next Submit Click, if any
          var upgrade = (promptMode === 'USERINPUT');

          me.ignoreNextClickSubmit = upgrade;

          // Also, force the Change to behave as if AutoSubmit was on!
          if (me.panel) { me.panel.forceAutoSubmit = upgrade; }

          delete me.clicking;
        }

        // Store mode so we can check if we need to refresh the report content or not in the view
        // As only the last request's response is processed, the last value of mode is actually the correct one.
        me.mode = promptMode;

        var options = me._buildReportContentOptions(this._getParameterDefinitionRenderMode(promptMode));

        var args = arguments;

        var onSuccess = logged('fetchParameterDefinition_success', function(xmlString) {
          if (me.checkSessionTimeout(xmlString, args)) { return; }

          // Another request was made after this one, so this one is ignored.
          if (fetchParamDefId !== me._fetchParamDefId) { return; }

          try {
            callback(xmlString);
          } catch (e) {
            me.onFatalError(e);
          } finally {
            me.mode = 'USERINPUT';
          }
        });

        var onError = function(e) {
          if (!me.checkSessionTimeout(e, args)) {
            me.onFatalError(e);
          }
        };

        $.ajax({
          async:   true,
          traditional: true, // Controls internal use of $.param() to serialize data to the url/body.
          cache:   false,
          type:    'POST',
          url:     me.getParameterUrl(),
          data:    options,
          dataType:'text',
          success: onSuccess,
          error:   onError
        });
      },

      _getParameterDefinitionRenderMode: function(promptMode) {
        switch(promptMode) {
          case 'INITIAL':
              return 'PARAMETER';

          case 'USERINPUT':
            if (!this.panel || !this._getStateProperty('autoSubmit')) {
              return 'PARAMETER';
            }
            break;
        }

        return 'XML';
      },

      getParameterUrl: function() {
        return 'parameter';
      },

      showMessageBox: function( message, dialogTitle, button1Text, button1Callback, button2Text, button2Callback, blocker ) {
        var messageBox = registry.byId('messageBox');

        messageBox.setTitle(dialogTitle);
        messageBox.setMessage(message);

        if (blocker) {
          messageBox.setButtons([]);
        } else {
          var closeFunc = (function() {
            this.api.ui.hideProgressIndicator();
            messageBox.hide.call(messageBox);
          }).bind(this);

          if(!button1Text) {
            button1Text = Messages.getString('OK');
          }
          if(!button1Callback) {
            button1Callback = closeFunc;
          }

          messageBox.onCancel = closeFunc;

          if(button2Text) {
            messageBox.callbacks = [
              button1Callback,
              button2Callback
            ];
            messageBox.setButtons([button1Text,button2Text]);
          } else {
            messageBox.callbacks = [
              button1Callback
            ];
            messageBox.setButtons([button1Text]);
          }
        }

        this.api.ui.showProgressIndicator();
        messageBox.show();
      },

      /**
       * Called when there is a fatal error during parameter definition fetching/parsing
       *
       * @param e Error/exception encountered
       */
      onFatalError: function(e) {
        var errorMsg = Messages.getString('ErrorParsingParamXmlMessage');
        if (typeof console !== 'undefined' && console.log) {
          console.log(errorMsg + ": " + e);
        }
        this.showMessageBox(
          errorMsg,
          Messages.getString('FatalErrorTitle'));
      }
    }); // return logged
  }; // return function
});
<|endoftext|>


<commit_before>/* eslint no-throw-literal: 0 */

import {promisify} from 'bluebird'
import eventToPromise from 'event-to-promise'
import {createClient} from 'ldapjs'
import {escape} from 'ldapjs/lib/filters/escape'
import {readFileSync} from 'fs'

// ===================================================================

const VAR_RE = /\{\{([^}]+)\}\}/g
function evalFilter (filter, vars) {
  return filter.replace(VAR_RE, (_, name) => {
    const value = vars[name]

    if (value === undefined) {
      throw new Error('invalid variable: ' + name)
    }

    return escape(value)
  })
}

export const configurationSchema = {
  type: 'object',
  properties: {
    uri: {
      description: 'URI of the LDAP server.',
      type: 'string'
    },
    certificateAuthorities: {
      description: `
Paths to CA certificates to use when connecting to SSL-secured LDAP servers.

If not specified, it will use a default set of well-known CAs.
`.trim(),
      type: 'array',
      items: {
        type: 'string'
      }
    },
    checkCertificate: {
      description: 'Check the validity of the server\'s certificates. Useful when connecting to servers that use a self-signed certificate.',
      type: 'boolean',
      default: true
    },
    bind: {
      description: 'Credentials to use before looking for the user record.',
      type: 'object',
      properties: {
        dn: {
          description: `
Distinguished name of the user permitted to search the LDAP directory for the user to authenticate.

For Microsoft Active Directory, it can also be \`<user>@<domain>\`.
`.trim(),
          type: 'string'
        },
        password: {
          description: 'Password of the user permitted ot search the LDAP directory.',
          type: 'string'
        }
      },
      required: ['dn', 'password']
    },
    base: {
      description: 'The base is the part of the description tree where the users are looked for.',
      type: 'string'
    },
    filter: {
      description: `
Filter used to find the user.

For Microsoft Active Directory, you can try one of the following filters:

- \`(cn={{name}})\`
- \`(sAMAccountName={{name}})\`
- \`(sAMAccountName={{name}}@<domain>)\`
- \`(userPrincipalName={{name}})\`
`.trim(),
      type: 'string',
      default: '(uid={{name}})'
    }
  },
  required: ['uri', 'base']
}

// ===================================================================

class AuthLdap {
  constructor (xo) {
    this._xo = xo
  }

  configure (conf) {
    const clientOpts = {
      url: conf.uri,
      maxConnections: 5,
      tlsOptions: {}
    }

    {
      const {
        bind,
        checkCertificate = true,
        certificateAuthorities
      } = conf

      if (bind) {
        clientOpts.bindDN = bind.dn
        clientOpts.bindCredentials = bind.password
      }

      const {tlsOptions} = clientOpts

      tlsOptions.rejectUnauthorized = checkCertificate
      if (certificateAuthorities) {
        // FIXME: should be async!!!
        tlsOptions.ca = certificateAuthorities.map(path => readFileSync(path))
      }
    }

    const {
      base: searchBase,
      filter: searchFilter = '(uid={{name}})'
    } = conf

    this._provider = async function ({username, password}) {
      if (username === undefined || password === undefined) {
        throw null
      }

      const client = createClient(clientOpts)

      try {
        // Promisify some methods.
        const bind = promisify(client.bind, client)
        const search = promisify(client.search, client)

        // Bind if necessary.
        {
          const {bind: credentials} = conf
          if (credentials) {
            await bind(credentials.dn, credentials.password)
          }
        }

        // Search for the user.
        const entries = []
        {
          const response = await search(searchBase, {
            scope: 'sub',
            filter: evalFilter(searchFilter, {
              name: username
            })
          })

          response.on('searchEntry', entry => {
            entries.push(entry.json)
          })

          const {status} = await eventToPromise(response, 'end')
          if (status) {
            throw new Error('unexpected search response status: ' + status)
          }
        }

        // Try to find an entry which can be bind with the given password.
        for (const entry of entries) {
          try {
            await bind(entry.objectName, password)
            return { username }
          } catch (_) {}
        }

        throw null
      } finally {
        client.unbind()
      }
    }
  }

  load () {
    this._xo.registerAuthenticationProvider(this._provider)
  }

  unload () {
    this._xo.unregisterAuthenticationProvider(this._provider)
  }
}

// ===================================================================

export default ({xo}) => new AuthLdap(xo)
<commit_msg>No need to reload the plugin after a configure.<commit_after>/* eslint no-throw-literal: 0 */

import {promisify} from 'bluebird'
import eventToPromise from 'event-to-promise'
import {createClient} from 'ldapjs'
import {escape} from 'ldapjs/lib/filters/escape'
import {readFileSync} from 'fs'

// ===================================================================

function bind (fn, thisArg) {
  return function () {
    return fn.apply(thisArg, arguments)
  }
}

const VAR_RE = /\{\{([^}]+)\}\}/g
function evalFilter (filter, vars) {
  return filter.replace(VAR_RE, (_, name) => {
    const value = vars[name]

    if (value === undefined) {
      throw new Error('invalid variable: ' + name)
    }

    return escape(value)
  })
}

export const configurationSchema = {
  type: 'object',
  properties: {
    uri: {
      description: 'URI of the LDAP server.',
      type: 'string'
    },
    certificateAuthorities: {
      description: `
Paths to CA certificates to use when connecting to SSL-secured LDAP servers.

If not specified, it will use a default set of well-known CAs.
`.trim(),
      type: 'array',
      items: {
        type: 'string'
      }
    },
    checkCertificate: {
      description: 'Check the validity of the server\'s certificates. Useful when connecting to servers that use a self-signed certificate.',
      type: 'boolean',
      default: true
    },
    bind: {
      description: 'Credentials to use before looking for the user record.',
      type: 'object',
      properties: {
        dn: {
          description: `
Distinguished name of the user permitted to search the LDAP directory for the user to authenticate.

For Microsoft Active Directory, it can also be \`<user>@<domain>\`.
`.trim(),
          type: 'string'
        },
        password: {
          description: 'Password of the user permitted ot search the LDAP directory.',
          type: 'string'
        }
      },
      required: ['dn', 'password']
    },
    base: {
      description: 'The base is the part of the description tree where the users are looked for.',
      type: 'string'
    },
    filter: {
      description: `
Filter used to find the user.

For Microsoft Active Directory, you can try one of the following filters:

- \`(cn={{name}})\`
- \`(sAMAccountName={{name}})\`
- \`(sAMAccountName={{name}}@<domain>)\`
- \`(userPrincipalName={{name}})\`
`.trim(),
      type: 'string',
      default: '(uid={{name}})'
    }
  },
  required: ['uri', 'base']
}

// ===================================================================

class AuthLdap {
  constructor (xo) {
    this._xo = xo

    this._authenticate = bind(this._authenticate, this)
  }

  configure (conf) {
    const clientOpts = this._clientOpts = {
      url: conf.uri,
      maxConnections: 5,
      tlsOptions: {}
    }

    {
      const {
        bind,
        checkCertificate = true,
        certificateAuthorities
      } = conf

      if (bind) {
        clientOpts.bindDN = bind.dn
        clientOpts.bindCredentials = bind.password
      }

      const {tlsOptions} = clientOpts

      tlsOptions.rejectUnauthorized = checkCertificate
      if (certificateAuthorities) {
        // FIXME: should be async!!!
        tlsOptions.ca = certificateAuthorities.map(path => readFileSync(path))
      }
    }

    const {
      bind: credentials,
      base: searchBase,
      filter: searchFilter = '(uid={{name}})'
    } = conf

    this._credentials = credentials
    this._searchBase = searchBase
    this._searchFilter = searchFilter
  }

  load () {
    this._xo.registerAuthenticationProvider(this._authenticate)
  }

  unload () {
    this._xo.unregisterAuthenticationProvider(this._authenticate)
  }

  async _authenticate ({ username, password }) {
    if (username === undefined || password === undefined) {
      throw null
    }

    const client = createClient(this._clientOpts)

    try {
      // Promisify some methods.
      const bind = promisify(client.bind, client)
      const search = promisify(client.search, client)

      // Bind if necessary.
      {
        const {_credentials: credentials} = this
        if (credentials) {
          await bind(credentials.dn, credentials.password)
        }
      }

      // Search for the user.
      const entries = []
      {
        const response = await search(this._searchBase, {
          scope: 'sub',
          filter: evalFilter(this._searchFilter, {
            name: username
          })
        })

        response.on('searchEntry', entry => {
          entries.push(entry.json)
        })

        const {status} = await eventToPromise(response, 'end')
        if (status) {
          throw new Error('unexpected search response status: ' + status)
        }
      }

      // Try to find an entry which can be bind with the given password.
      for (const entry of entries) {
        try {
          await bind(entry.objectName, password)
          return { username }
        } catch (_) {}
      }

      throw null
    } finally {
      client.unbind()
    }
  }
}

// ===================================================================

export default ({xo}) => new AuthLdap(xo)
<|endoftext|>


<commit_before>// through2 is a thin wrapper around node transform streams
var through = require('through2'),
	vm = require('vm'),
	gutil = require('gulp-util'),
	_ = require('lodash');

// consts
const PLUGIN_NAME = 'gulp-jsufonify';

// plugin level function (dealing with files)
function jsufonify(prefixText) {

	// creating a stream through which each file will pass
	var stream = through.obj(function(file, enc, cb) {
		var sandbox = { exports: { glyphs: {} } },
			font;

		vm.runInNewContext( file.contents, sandbox );

		font = sandbox.exports;

		// WIP: convert ptf object to jsufon
		_(font.glyphs).forEach(function( glyph, name ) {
			glyph.name = name;
			if ( glyph.name.length === 1 || typeof glyph.unicode === 'string' ) {
				glyph.unicode = glyph.unicode.charCodeAt(0);
			}

			// glyph.anchors -> glyph.anchor
			if ( glyph.anchors ) {
				glyph.anchor = [];

				_(glyph.anchors).forEach(function( anchor, i ) {
					glyph.anchor[i] = anchor;
				});
			}
			delete glyph.anchors;

			if ( !glyph.outline ) {
				glyph.outline = {};
			}

			// glyph.contours -> glyph.outline.contour
			if ( !glyph.outline.contour ) {
				glyph.outline.contour = [];
			}
			_(glyph.contours).forEach(function( contour, i ) {
				contour.point = [];

				// contour.nodes -> contour.point
				_(contour.nodes).forEach(function( node, j ) {
					contour.point[j] = node;
				});
				delete contour.nodes;

				glyph.outline.contour[i] = contour;
			});
			delete glyph.contours;

			// glyph.components -> glyph.outline.component
			if ( !glyph.outline.component ) {
				glyph.outline.component = [];
			}
			_(glyph.components).forEach(function( component, i ) {
				glyph.outline.component[i] = component;

				component.anchor = [];

				if ( !component.anchor ) {
					component.anchor = [];
				}
				_(component.anchors).forEach(function( anchor, i ) {
					component.anchor[i] = anchor;
				});
				delete component.anchors;
			});
			delete glyph.components;
		});

		file.contents = new Buffer( JSON.stringify( sandbox.exports ) );

		this.push(file);

		return cb();
	});

	// returning the file stream
	return stream;
}

// exporting the plugin main function
module.exports = jsufonify;
<commit_msg>fix incorrect unicode conversion of single char glyph name<commit_after>// through2 is a thin wrapper around node transform streams
var through = require('through2'),
	vm = require('vm'),
	gutil = require('gulp-util'),
	_ = require('lodash');

// consts
const PLUGIN_NAME = 'gulp-jsufonify';

// plugin level function (dealing with files)
function jsufonify(prefixText) {

	// creating a stream through which each file will pass
	var stream = through.obj(function(file, enc, cb) {
		var sandbox = { exports: { glyphs: {} } },
			font;

		vm.runInNewContext( file.contents, sandbox );

		font = sandbox.exports;

		// WIP: convert ptf object to jsufon
		_(font.glyphs).forEach(function( glyph, name ) {
			glyph.name = name;

			if ( glyph.name.length === 1 ) {
				glyph.unicode = glyph.name.charCodeAt(0);

			} else if ( typeof glyph.unicode === 'string' ) {
				glyph.unicode = glyph.unicode.charCodeAt(0);
			}

			// glyph.anchors -> glyph.anchor
			if ( glyph.anchors ) {
				glyph.anchor = [];

				_(glyph.anchors).forEach(function( anchor, i ) {
					glyph.anchor[i] = anchor;
				});
			}
			delete glyph.anchors;

			if ( !glyph.outline ) {
				glyph.outline = {};
			}

			// glyph.contours -> glyph.outline.contour
			if ( !glyph.outline.contour ) {
				glyph.outline.contour = [];
			}
			_(glyph.contours).forEach(function( contour, i ) {
				contour.point = [];

				// contour.nodes -> contour.point
				_(contour.nodes).forEach(function( node, j ) {
					contour.point[j] = node;
				});
				delete contour.nodes;

				glyph.outline.contour[i] = contour;
			});
			delete glyph.contours;

			// glyph.components -> glyph.outline.component
			if ( !glyph.outline.component ) {
				glyph.outline.component = [];
			}
			_(glyph.components).forEach(function( component, i ) {
				glyph.outline.component[i] = component;

				component.anchor = [];

				if ( !component.anchor ) {
					component.anchor = [];
				}
				_(component.anchors).forEach(function( anchor, i ) {
					component.anchor[i] = anchor;
				});
				delete component.anchors;
			});
			delete glyph.components;
		});

		file.contents = new Buffer( JSON.stringify( sandbox.exports ) );

		this.push(file);

		return cb();
	});

	// returning the file stream
	return stream;
}

// exporting the plugin main function
module.exports = jsufonify;
<|endoftext|>


<commit_before>/*
 * from noVNC: HTML5 VNC client
 * Copyright (C) 2012 Joel Martin
 * Licensed under MPL 2.0 (see LICENSE.txt)
 *
 * See README.md for usage and integration instructions.
 */

"use strict";
/*jslint bitwise: false, white: false */
/*global Util, window, document */

// Globals defined here
var WebUtil = {}, $D;

/*
 * Simple DOM selector by ID
 */
if (!window.$D) {
    window.$D = function (id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[id];
        } else if (document.layers) {
            return document.layers[id];
        }
        return undefined;
    };
}

/*
 * ------------------------------------------------------
 * Namespaced in WebUtil
 * ------------------------------------------------------
 */

// init log level reading the logging HTTP param
WebUtil.init_logging = function(level) {
    if (typeof level !== "undefined") {
        Util._log_level = level;
    } else {
        Util._log_level = (document.location.href.match(
            /logging=([A-Za-z0-9\._\-]*)/) ||
            ['', Util._log_level])[1];
    }
    Util.init_logging();
};

WebUtil.dirObj = function (obj, depth, parent) {
    var i, msg = "", val = "";
    if (! depth) { depth=2; }
    if (! parent) { parent= ""; }

    // Print the properties of the passed-in object
    for (i in obj) {
        if ((depth > 1) && (typeof obj[i] === "object")) {
            // Recurse attributes that are objects
            msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
        } else {
            //val = new String(obj[i]).replace("\n", " ");
            if (typeof(obj[i]) === "undefined") {
                val = "undefined";
            } else {
                val = obj[i].toString().replace("\n", " ");
            }
            if (val.length > 30) {
                val = val.substr(0,30) + "...";
            }
            msg += parent + "." + i + ": " + val + "\n";
        }
    }
    return msg;
};

// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
    var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
        match = document.location.href.match(re);
    if (typeof defVal === 'undefined') { defVal = null; }
    if (match) {
        return decodeURIComponent(match[1]);
    } else {
        return defVal;
    }
};

/*
 * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
 */

// No days means only for this browser session
WebUtil.createCookie = function(name,value,days) {
    var date, expires;
    if (days) {
        date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        expires = "; expires="+date.toGMTString();
    }
    else {
        expires = "";
    }
    document.cookie = name+"="+value+expires+"; path=/";
};

WebUtil.readCookie = function(name, defaultValue) {
    var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
    for(i=0; i < ca.length; i += 1) {
        c = ca[i];
        while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
        if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
    }
    return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};

WebUtil.eraseCookie = function(name) {
    WebUtil.createCookie(name,"",-1);
};

/*
 * Setting handling.
 */

WebUtil.initSettings = function(callback) {
    var callbackArgs = Array.prototype.slice.call(arguments, 1);
    if (window.chrome && window.chrome.storage) {
        window.chrome.storage.sync.get(function (cfg) {
            WebUtil.settings = cfg;
            console.log(WebUtil.settings);
            if (callback) {
                callback.apply(this, callbackArgs);
            }
        });
    } else {
        // No-op
        if (callback) {
            callback.apply(this, callbackArgs);
        }
    }
};

// No days means only for this browser session
WebUtil.writeSetting = function(name, value) {
    if (window.chrome && window.chrome.storage) {
        //console.log("writeSetting:", name, value);
        if (WebUtil.settings[name] !== value) {
            WebUtil.settings[name] = value;
            window.chrome.storage.sync.set(WebUtil.settings);
        }
    } else {
        localStorage.setItem(name, value);
    }
};

WebUtil.readSetting = function(name, defaultValue) {
    var value;
    if (window.chrome && window.chrome.storage) {
        value = WebUtil.settings[name];
    } else {
        value = localStorage.getItem(name);
    }
    if (typeof value === "undefined") {
        value = null;
    }
    if (value === null && typeof defaultValue !== undefined) {
        return defaultValue;
    } else {
        return value;
    }
};

WebUtil.eraseSetting = function(name) {
    if (window.chrome && window.chrome.storage) {
        window.chrome.storage.sync.remove(name);
        delete WebUtil.settings[name];
    } else {
        localStorage.removeItem(name);
    }
};

/*
 * Alternate stylesheet selection
 */
WebUtil.getStylesheets = function() { var i, links, sheets = [];
    links = document.getElementsByTagName("link");
    for (i = 0; i < links.length; i += 1) {
        if (links[i].title &&
            links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
            sheets.push(links[i]);
        }
    }
    return sheets;
};

// No sheet means try and use value from cookie, null sheet used to
// clear all alternates.
WebUtil.selectStylesheet = function(sheet) {
    var i, link, sheets = WebUtil.getStylesheets();
    if (typeof sheet === 'undefined') {
        sheet = 'default';
    }
    for (i=0; i < sheets.length; i += 1) {
        link = sheets[i];
        if (link.title === sheet) {
            Util.Debug("Using stylesheet " + sheet);
            link.disabled = false;
        } else {
            //Util.Debug("Skipping stylesheet " + link.title);
            link.disabled = true;
        }
    }
    return sheet;
};
<commit_msg>Spelling: derived<commit_after>/*
 * from noVNC: HTML5 VNC client
 * Copyright (C) 2012 Joel Martin
 * Licensed under MPL 2.0 (see LICENSE.txt)
 *
 * See README.md for usage and integration instructions.
 */

"use strict";
/*jslint bitwise: false, white: false */
/*global Util, window, document */

// Globals defined here
var WebUtil = {}, $D;

/*
 * Simple DOM selector by ID
 */
if (!window.$D) {
    window.$D = function (id) {
        if (document.getElementById) {
            return document.getElementById(id);
        } else if (document.all) {
            return document.all[id];
        } else if (document.layers) {
            return document.layers[id];
        }
        return undefined;
    };
}

/*
 * ------------------------------------------------------
 * Namespaced in WebUtil
 * ------------------------------------------------------
 */

// init log level reading the logging HTTP param
WebUtil.init_logging = function(level) {
    if (typeof level !== "undefined") {
        Util._log_level = level;
    } else {
        Util._log_level = (document.location.href.match(
            /logging=([A-Za-z0-9\._\-]*)/) ||
            ['', Util._log_level])[1];
    }
    Util.init_logging();
};

WebUtil.dirObj = function (obj, depth, parent) {
    var i, msg = "", val = "";
    if (! depth) { depth=2; }
    if (! parent) { parent= ""; }

    // Print the properties of the passed-in object
    for (i in obj) {
        if ((depth > 1) && (typeof obj[i] === "object")) {
            // Recurse attributes that are objects
            msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
        } else {
            //val = new String(obj[i]).replace("\n", " ");
            if (typeof(obj[i]) === "undefined") {
                val = "undefined";
            } else {
                val = obj[i].toString().replace("\n", " ");
            }
            if (val.length > 30) {
                val = val.substr(0,30) + "...";
            }
            msg += parent + "." + i + ": " + val + "\n";
        }
    }
    return msg;
};

// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
    var re = new RegExp('[?][^#]*' + name + '=([^&#]*)'),
        match = document.location.href.match(re);
    if (typeof defVal === 'undefined') { defVal = null; }
    if (match) {
        return decodeURIComponent(match[1]);
    } else {
        return defVal;
    }
};

/*
 * Cookie handling. Derived from: http://www.quirksmode.org/js/cookies.html
 */

// No days means only for this browser session
WebUtil.createCookie = function(name,value,days) {
    var date, expires;
    if (days) {
        date = new Date();
        date.setTime(date.getTime()+(days*24*60*60*1000));
        expires = "; expires="+date.toGMTString();
    }
    else {
        expires = "";
    }
    document.cookie = name+"="+value+expires+"; path=/";
};

WebUtil.readCookie = function(name, defaultValue) {
    var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
    for(i=0; i < ca.length; i += 1) {
        c = ca[i];
        while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
        if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
    }
    return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};

WebUtil.eraseCookie = function(name) {
    WebUtil.createCookie(name,"",-1);
};

/*
 * Setting handling.
 */

WebUtil.initSettings = function(callback) {
    var callbackArgs = Array.prototype.slice.call(arguments, 1);
    if (window.chrome && window.chrome.storage) {
        window.chrome.storage.sync.get(function (cfg) {
            WebUtil.settings = cfg;
            console.log(WebUtil.settings);
            if (callback) {
                callback.apply(this, callbackArgs);
            }
        });
    } else {
        // No-op
        if (callback) {
            callback.apply(this, callbackArgs);
        }
    }
};

// No days means only for this browser session
WebUtil.writeSetting = function(name, value) {
    if (window.chrome && window.chrome.storage) {
        //console.log("writeSetting:", name, value);
        if (WebUtil.settings[name] !== value) {
            WebUtil.settings[name] = value;
            window.chrome.storage.sync.set(WebUtil.settings);
        }
    } else {
        localStorage.setItem(name, value);
    }
};

WebUtil.readSetting = function(name, defaultValue) {
    var value;
    if (window.chrome && window.chrome.storage) {
        value = WebUtil.settings[name];
    } else {
        value = localStorage.getItem(name);
    }
    if (typeof value === "undefined") {
        value = null;
    }
    if (value === null && typeof defaultValue !== undefined) {
        return defaultValue;
    } else {
        return value;
    }
};

WebUtil.eraseSetting = function(name) {
    if (window.chrome && window.chrome.storage) {
        window.chrome.storage.sync.remove(name);
        delete WebUtil.settings[name];
    } else {
        localStorage.removeItem(name);
    }
};

/*
 * Alternate stylesheet selection
 */
WebUtil.getStylesheets = function() { var i, links, sheets = [];
    links = document.getElementsByTagName("link");
    for (i = 0; i < links.length; i += 1) {
        if (links[i].title &&
            links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
            sheets.push(links[i]);
        }
    }
    return sheets;
};

// No sheet means try and use value from cookie, null sheet used to
// clear all alternates.
WebUtil.selectStylesheet = function(sheet) {
    var i, link, sheets = WebUtil.getStylesheets();
    if (typeof sheet === 'undefined') {
        sheet = 'default';
    }
    for (i=0; i < sheets.length; i += 1) {
        link = sheets[i];
        if (link.title === sheet) {
            Util.Debug("Using stylesheet " + sheet);
            link.disabled = false;
        } else {
            //Util.Debug("Skipping stylesheet " + link.title);
            link.disabled = true;
        }
    }
    return sheet;
};
<|endoftext|>


<commit_before>describe( 'ExterminateGlobals.JS', function() {

	it( 'has all the methods', function () {
		expect( window.ExterminateGlobals ).toBeDefined();
		expect( window.ExterminateGlobals.GlobalsCollector ).toBeDefined();
		expect( window.ExterminateGlobals.start ).toBeDefined();
		expect( window.ExterminateGlobals.stop ).toBeDefined();
		expect( window.ExterminateGlobals.collectMembers ).toBeUndefined();
	} );

	describe( 'GlobalsCollector', function () {
		describe( 'constructor', function () {
			it( 'adds "ExterminateGlobals" to the list of ignored keys', function () {
				var gc = new ExterminateGlobals.GlobalsCollector();

				expect( gc._ignoreKeys ).toEqual( [ 'ExterminateGlobals'] );
			} );

			it( 'creates a copy of ignored globals', function () {
				var ignoreKeys = [ 'ExterminateGlobals', 'foo' ];
				var gc = new ExterminateGlobals.GlobalsCollector( ignoreKeys );

				expect( gc._ignoreKeys ).not.toBe( ignoreKeys );
			} );
		} );

		describe( 'Getters', function () {
			it( 'getMonitoredObject() gets the correct monitored object', function () {
				var monitoredObject = { foo: 'bar' };
				var gc = new ExterminateGlobals.GlobalsCollector( [], monitoredObject );

				expect( gc.getMonitoredObject() ).toBe( monitoredObject );
			} );

			describe( 'getIgnoredKeys()', function () {
				it( 'gets the correct ignored keys', function () {
					var gc = new ExterminateGlobals.GlobalsCollector( [ 'foo', 'bar' ] );

					expect( gc.getIgnoredKeys() ).toEqual( [ 'foo', 'bar', 'ExterminateGlobals' ] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyIgnored = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._ignoreKeys = dummyIgnored;

					var returnedIgnored = gc.getIgnoredKeys();
					returnedIgnored.push( 'baz' );

					expect( gc.getIgnoredKeys() ).not.toBe( dummyIgnored );
				} );
			} );

			describe( 'getUnwantedGlobals()', function () {
				it( 'gets an empty array before collecting', function () {
					var gc = new ExterminateGlobals.GlobalsCollector();

					expect( gc.getUnwantedGlobals() ).toEqual( [] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyUnwanted = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._unwantedGlobals = dummyUnwanted;

					var returnedGlobals = gc.getUnwantedGlobals();
					returnedGlobals.push( 'baz' );

					expect( gc.getUnwantedGlobals() ).not.toBe( dummyUnwanted );
				} );
			} );
		} );

		describe( 'startCollecting()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'collect()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'print()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

	describe( 'convenience functions', function () {
		describe( 'start()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'end()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

} );<commit_msg>Some more tests<commit_after>describe( 'ExterminateGlobals.JS', function() {

	it( 'has all the methods', function () {
		expect( window.ExterminateGlobals ).toBeDefined();
		expect( window.ExterminateGlobals.GlobalsCollector ).toBeDefined();
		expect( window.ExterminateGlobals.start ).toBeDefined();
		expect( window.ExterminateGlobals.stop ).toBeDefined();
		expect( window.ExterminateGlobals.collectMembers ).toBeUndefined();
	} );

	describe( 'GlobalsCollector', function () {
		describe( 'constructor', function () {
			it( 'adds "ExterminateGlobals" to the list of ignored keys', function () {
				var gc = new ExterminateGlobals.GlobalsCollector();

				expect( gc._ignoreKeys ).toEqual( [ 'ExterminateGlobals'] );
			} );

			it( 'creates a copy of ignored globals', function () {
				var ignoreKeys = [ 'ExterminateGlobals', 'foo' ];
				var gc = new ExterminateGlobals.GlobalsCollector( ignoreKeys );

				expect( gc._ignoreKeys ).not.toBe( ignoreKeys );
			} );
		} );

		describe( 'Getters', function () {
			it( 'getMonitoredObject() gets the correct monitored object', function () {
				var monitoredObject = { foo: 'bar' };
				var gc = new ExterminateGlobals.GlobalsCollector( [], monitoredObject );

				expect( gc.getMonitoredObject() ).toBe( monitoredObject );
			} );

			describe( 'getIgnoredKeys()', function () {
				it( 'gets the correct ignored keys', function () {
					var gc = new ExterminateGlobals.GlobalsCollector( [ 'foo', 'bar' ] );

					expect( gc.getIgnoredKeys() ).toEqual( [ 'foo', 'bar', 'ExterminateGlobals' ] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyIgnored = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._ignoreKeys = dummyIgnored;

					var returnedIgnored = gc.getIgnoredKeys();
					returnedIgnored.push( 'baz' );

					expect( gc.getIgnoredKeys() ).not.toBe( dummyIgnored );
				} );
			} );

			describe( 'getUnwantedGlobals()', function () {
				it( 'gets an empty array before collecting', function () {
					var gc = new ExterminateGlobals.GlobalsCollector();

					expect( gc.getUnwantedGlobals() ).toEqual( [] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyUnwanted = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._unwantedGlobals = dummyUnwanted;

					var returnedGlobals = gc.getUnwantedGlobals();
					returnedGlobals.push( 'baz' );

					expect( gc.getUnwantedGlobals() ).not.toBe( dummyUnwanted );
				} );
			} );
		} );

		describe( 'startCollecting()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'collect()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'print()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

	describe( 'convenience functions', function () {
		describe( 'start()', function () {
			it( 'throws Error if called at inappropriate time', function () {
				ExterminateGlobals.start();
				expect( ExterminateGlobals.start ).toThrow();
			} );
		} );

		describe( 'stop()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

} );<|endoftext|>


<commit_before>/**
 * Provides the dialog overlay to add a new box.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2016 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLab/WCF/Acp/Ui/Box/Add
 */
define(['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
	"use strict";

	var _link;

	/**
	 * @exports     WoltLab/WCF/Acp/Ui/Box/Add
	 */
	return {
		/**
		 * Initializes the box add handler.
		 *
		 * @param       {string}        link    redirect URL
		 */
		init: function(link) {
			_link = link;

			var buttons = elBySelAll('.jsButtonBoxAdd');
			for (var i = 0, length = buttons.length; i < length; i++) {
				buttons[i].addEventListener(WCF_CLICK_EVENT, this.openDialog.bind(this));
			}
		},

		/**
		 * Opens the 'Add Box' dialog.
		 *
		 * @param       {Event=}        event   event object
		 */
		openDialog: function(event) {
			if (event instanceof Event) {
				event.preventDefault();
			}

			UiDialog.open(this);
		},

		_dialogSetup: function() {
			return {
				id: 'boxAddDialog',
				options: {
					onSetup: function(content) {
						elBySel('button', content).addEventListener(WCF_CLICK_EVENT, function(event) {
							event.preventDefault();

							var boxType = elBySel('input[name="boxType"]:checked', content).value;
							var isMultilingual = elBySel('input[name="isMultilingual"]:checked', content).value;

							window.location = _link.replace(/{\$boxType}/, boxType).replace(/{\$isMultilingual}/, isMultilingual);
						});
					},
					title: Language.get('wcf.acp.box.add')
				}
			};
		}
	}
});
<commit_msg>Fixed creation of multilingual system boxes<commit_after>/**
 * Provides the dialog overlay to add a new box.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2016 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLab/WCF/Acp/Ui/Box/Add
 */
define(['Core', 'Language', 'Ui/Dialog'], function(Core, Language, UiDialog) {
	"use strict";

	var _link;

	/**
	 * @exports     WoltLab/WCF/Acp/Ui/Box/Add
	 */
	return {
		/**
		 * Initializes the box add handler.
		 *
		 * @param       {string}        link    redirect URL
		 */
		init: function(link) {
			_link = link;

			var buttons = elBySelAll('.jsButtonBoxAdd');
			for (var i = 0, length = buttons.length; i < length; i++) {
				buttons[i].addEventListener(WCF_CLICK_EVENT, this.openDialog.bind(this));
			}
		},

		/**
		 * Opens the 'Add Box' dialog.
		 *
		 * @param       {Event=}        event   event object
		 */
		openDialog: function(event) {
			if (event instanceof Event) {
				event.preventDefault();
			}

			UiDialog.open(this);
		},

		_dialogSetup: function() {
			return {
				id: 'boxAddDialog',
				options: {
					onSetup: function(content) {
						elBySel('button', content).addEventListener(WCF_CLICK_EVENT, function(event) {
							event.preventDefault();

							var boxType = elBySel('input[name="boxType"]:checked', content).value;
							var isMultilingual = 0;
							if (boxType !== 'system') isMultilingual = elBySel('input[name="isMultilingual"]:checked', content).value;

							window.location = _link.replace(/{\$boxType}/, boxType).replace(/{\$isMultilingual}/, isMultilingual);
						});

						elBySelAll('input[type="radio"][name="boxType"]', content, function(element) {
							element.addEventListener('change', function(event) {
								elBySelAll('input[type="radio"][name="isMultilingual"]', content, function(element) {
									element.disabled = (event.currentTarget.value === 'system');
								});
							});
						});
					},
					title: Language.get('wcf.acp.box.add')
				}
			};
		}
	}
});
<|endoftext|>


<commit_before>// Generated on 2014-01-14 using generator-webapp 0.4.6
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

    // Load grunt tasks automatically
    require('load-grunt-tasks')(grunt);

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    // Define the configuration for all the tasks
    grunt.initConfig({

        // Project settings
        yeoman: {
            // Configurable paths
            app: 'app',
            dist: 'dist'
        },

        // The actual grunt server settings
        connect: {
            options: {
                port: 9000,
                livereload: 35729,
                // Change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    open: true,
                    base: [
                        '.tmp',
                        '<%%= yeoman.app %>'
                    ]
                }
            },
            test: {
                options: {
                    hostname: 'localhost',
                    port: 9001,
                    base: [
                        '.tmp',
                        'test',
                        '<%%= yeoman.app %>'
                    ]
                }
            },
            dist: {
                options: {
                    open: true,
                    base: '<%%= yeoman.dist %>',
                    livereload: false
                }
            }
        },

        // Watches files for changes and runs tasks based on the changed files
        watch: {
            js: {
                files: ['<%%= yeoman.app %>/scripts/{,*/}*.js'],
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            jstest: {
                files: ['test/spec/{,*/}*.js'],
                tasks: ['test:watch']
            },
            gruntfile: {
                files: ['Gruntfile.js']
            },<% if (cssFramework === 'CompassFramework') { %>
            compass: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer']
            },<% } else if (cssFramework === 'SASSBootstrap') { %>
            scripts: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['sass:server', 'autoprefixer']
            },<% } %>
            styles: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['newer:copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: '<%%= connect.options.livereload %>'
                },
                files: [
                    '<%%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '<%%= yeoman.app %>/images/{,*/}*.{gif,jpeg,jpg,png,svg,webp}'
                ]
            }
        },

        // Empties folders to start fresh
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%%= yeoman.dist %>/*',
                        '!<%%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            afterBuild: {
                files: [{
                    dot: true,
                    src: [
                        '<%%= yeoman.dist %>/scripts/*.js',
                        '!<%%= yeoman.dist %>/scripts/main.js'
                    ]
                }]
            },
            server: '.tmp'
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },

        // Require js config
        bower: {
            target: {
                rjsConfig: '<%%= yeoman.app %>/scripts/main.js'
            }
        },

        // require js
        requirejs: {
            dist: {
                options: {
                    dir: "<%%= yeoman.dist %>/scripts/",
                    baseUrl: '<%%= yeoman.app %>/scripts',                    // Directory to look for the require configuration file
                    mainConfigFile: '<%%= yeoman.app %>/scripts/main.js',    // This is relative to the grunt file
                    modules: [
                        { name: 'main' }                // Create a global bundle
                    ],
                    preserveLicenseComments: false,        // remove all comments
                    removeCombined: true,                // remove files which aren't in bundles
                    optimize: 'uglify',                    // minify bundles with uglify 2
                    useStrict: true
                }
            }
        },
<% if (testFramework === 'jasmine') { %>
        // Jasmine testing framework configuration options
        jasmine: {
            pivotal: {
                src: '<%%= yeoman.app %>/js/**/*.js',
                options: {
                    specs: 'test/spec/*Spec.js',
                    helpers: 'test/spec/*Helper.js'
                }
            }
        },<% } else { %>
        // Mocha tesing framework configuration options
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://<%%= connect.test.options.hostname %>:<%%= connect.test.options.port %>/index.html']
                }
            }
        },<% } %>
<% if (cssFramework === 'CompassFramework') { %>
        // Compiles Sass to CSS and generates necessary files if requested
        compass: {
            options: {
                sassDir: '<%%= yeoman.app %>/styles',
                cssDir: '.tmp/styles',
                generatedImagesDir: '.tmp/images/generated',
                imagesDir: '<%%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%%= yeoman.app %>/styles/fonts',
                importPath: '<%%= yeoman.app %>/bower_components',
                httpImagesPath: '/images',
                httpGeneratedImagesPath: '/images/generated',
                httpFontsPath: '/styles/fonts',
                relativeAssets: false,
                assetCacheBuster: false
            },
            dist: {
                options: {
                    generatedImagesDir: '<%%= yeoman.dist %>/images/generated'
                }
            },
            server: {
                options: {
                    debugInfo: true
                }
            }
        },<% } else if (cssFramework === 'SASSBootstrap') { %>
        sass: {
            dist: {
                 options: {
                    style: 'compressed'
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            },
            server: {
                options: {
                    debugInfo: true,
                    style: 'expanded'
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            }
        },<% } %>
        // Add vendor prefixed styles
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.',
                    app: {
                        html: '<%%= yeoman.app %>/index.html',
                        ignorePath: '<%= yeoman.app %>/'
                    },
                    dest: '.tmp/styles/'
                }]
            }
        },

        // Automatically inject Bower components into the HTML file
        'bower-install': {
            app: {
                html: '<%%= yeoman.app %>/index.html',
                ignorePath: '<%%= yeoman.app %>/'
            }
        },

        // Renames files for browser caching purposes
        rev: {
            dist: {
                files: {
                    src: [
                        '<%%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%%= yeoman.dist %>/images/{,*/}*.{gif,jpeg,jpg,png,webp}',
                        '<%%= yeoman.dist %>/styles/fonts/{,*/}*.*'
                    ]
                }
            }
        },

        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
            options: {
                dest: '<%%= yeoman.dist %>'
            },
            html: '<%%= yeoman.app %>/index.html'
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
            options: {
                assetsDirs: ['<%%= yeoman.dist %>']
            },
            html: ['<%%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%%= yeoman.dist %>/styles/{,*/}*.css']
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/images',
                    src: '{,*/}*.{gif,jpeg,jpg,png}',
                    dest: '<%%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%%= yeoman.dist %>/images'
                }]
            }
        },
        htmlmin: {
            dist: {
                options: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    removeCommentsFromCDATA: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.dist %>',
                    src: '{,*/}*.html',
                    dest: '<%%= yeoman.dist %>'
                }]
            }
        },

        // By default, your `index.html`'s <!-- Usemin block --> will take care of
        // minification. These next options are pre-configured if you do not wish
        // to use the Usemin blocks.
        cssmin: {
            dist: {
                files: {
                    '<%%= yeoman.dist %>/styles/style.css': [
                        '.tmp/styles/{,*/}*.css'
                    ]
                }
            }
        },
        uglify: {
            dist: {
                files: [{
                    src: '<%%= yeoman.app %>/scripts/*.js',  // source files mask
                    dest: '<%%= yeoman.dist %>/scripts/',    // destination folder
                    expand: true,    // allow dynamic building
                    flatten: true   // remove all unnecessary nesting
                }]
            }
        },
        concat: {
            dist: {
                src: ['.tmp/styles/{,*/}*.css'],
                dest: '.tmp/styles/style.css'
            }
        },

        // Copies remaining files to places other tasks can use
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%%= yeoman.app %>',
                    dest: '<%%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.webp',
                        '{,*/}*.html',
                        'styles/fonts/{,*/}*.*',
                        'bower_components/sass-bootstrap/fonts/*.*',
                        'bower_components/font-awesome/fonts/*.*'
                    ]
                }]
            },
            afterBuild: {
                files: [{
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%%= yeoman.dist %>/scripts',
                    dest: '<%%= yeoman.dist %>/scripts/vendor',
                    src: [
                        '*.js',
                        '!main.js'
                    ]
                }]

            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },

        // Generates a custom Modernizr build that includes only the tests you
        // reference in your app
        modernizr: {
            devFile: '<%%= yeoman.app %>/bower_components/modernizr/modernizr.js',
            outputFile: '<%%= yeoman.dist %>/bower_components/modernizr/modernizr.js',
            files: [
                '<%%= yeoman.dist %>/scripts/{,*/}*.js',
                '<%%= yeoman.dist %>/styles/{,*/}*.css',
                '!<%%= yeoman.dist %>/scripts/vendor/*'
            ],
            uglify: true
        },

        // Run some tasks in parallel to speed up build process
        concurrent: {
            server: [ <% if (cssFramework === 'CompassFramework') { %>
                'compass:server',<% } else if (cssFramework === 'SASSBootstrap') { %>
                'sass:server',<% } %>
                'copy:styles',
                'concat'
            ],
            test: [
                'copy:styles'
            ],
            dist: [ <% if (cssFramework === 'CompassFramework') { %>
                'compass',<% } else if (cssFramework === 'SASSBootstrap') { %>
                'sass:dist',<% } %>
                'copy:styles',
                'imagemin',
                'svgmin'
            ]
        }
    });
    grunt.registerTask('bundle-js', ['bower']);

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('server', function () {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run(['serve']);
    });

    grunt.registerTask('test', function(target) {
        if (target !== 'watch') {
            grunt.task.run([
                'clean:server',
                'concurrent:test',
                'autoprefixer',
            ]);
        }

        grunt.task.run([
            'connect:test',<% if (testFramework === 'mocha') { %>
            'mocha'<% } else if (testFramework === 'jasmine') { %>
            'jasmine'<% } %>
        ]);
    });

    grunt.registerTask('requirejs-bundle', function() {
        function replaceBetween(string, start, end, what) {
            return string.substring(0, start) + what + string.substring(end);
        };

        var mainjs = grunt.file.read('dist/scripts/main.js'), first, second, content;

        while (mainjs.indexOf('../bower_components') != -1) {
            first = mainjs.indexOf('../bower_components');
            second = mainjs.indexOf('"', first);
            content = 'vendor/' + mainjs.substring(first, second).split('/').pop();
            mainjs = replaceBetween(mainjs, first, second, content);
        }
        grunt.file.write('dist/scripts/main.js', mainjs);
    });

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'cssmin',
        'requirejs',
        'copy:afterBuild',
        'clean:afterBuild',
        'requirejs-bundle',
        // 'uglify',
        'copy:dist',
        'modernizr',
        // 'rev',
        'usemin',
        'htmlmin'
    ]);

    grunt.registerTask('default', [
        'newer:jshint',
        'test',
        'build'
    ]);
};
<commit_msg>Repair concat task<commit_after>// Generated on 2014-01-14 using generator-webapp 0.4.6
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

    // Load grunt tasks automatically
    require('load-grunt-tasks')(grunt);

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    // Define the configuration for all the tasks
    grunt.initConfig({

        // Project settings
        yeoman: {
            // Configurable paths
            app: 'app',
            dist: 'dist'
        },

        // The actual grunt server settings
        connect: {
            options: {
                port: 9000,
                livereload: 35729,
                // Change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    open: true,
                    base: [
                        '.tmp',
                        '<%%= yeoman.app %>'
                    ]
                }
            },
            test: {
                options: {
                    hostname: 'localhost',
                    port: 9001,
                    base: [
                        '.tmp',
                        'test',
                        '<%%= yeoman.app %>'
                    ]
                }
            },
            dist: {
                options: {
                    open: true,
                    base: '<%%= yeoman.dist %>',
                    livereload: false
                }
            }
        },

        // Watches files for changes and runs tasks based on the changed files
        watch: {
            js: {
                files: ['<%%= yeoman.app %>/scripts/{,*/}*.js'],
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            jstest: {
                files: ['test/spec/{,*/}*.js'],
                tasks: ['test:watch']
            },
            gruntfile: {
                files: ['Gruntfile.js']
            },<% if (cssFramework === 'CompassFramework') { %>
            compass: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['compass:server', 'autoprefixer']
            },<% } else if (cssFramework === 'SASSBootstrap') { %>
            scripts: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['sass:server', 'autoprefixer']
            },<% } %>
            styles: {
                files: ['<%%= yeoman.app %>/styles/{,*/}*.css'],
                tasks: ['newer:copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: '<%%= connect.options.livereload %>'
                },
                files: [
                    '<%%= yeoman.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '<%%= yeoman.app %>/images/{,*/}*.{gif,jpeg,jpg,png,svg,webp}'
                ]
            }
        },

        // Empties folders to start fresh
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%%= yeoman.dist %>/*',
                        '!<%%= yeoman.dist %>/.git*'
                    ]
                }]
            },
            afterBuild: {
                files: [{
                    dot: true,
                    src: [
                        '<%%= yeoman.dist %>/scripts/*.js',
                        '!<%%= yeoman.dist %>/scripts/main.js'
                    ]
                }]
            },
            server: '.tmp'
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%%= yeoman.app %>/scripts/{,*/}*.js',
                '!<%%= yeoman.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },

        // Require js config
        bower: {
            target: {
                rjsConfig: '<%%= yeoman.app %>/scripts/main.js'
            }
        },

        // require js
        requirejs: {
            dist: {
                options: {
                    dir: "<%%= yeoman.dist %>/scripts/",
                    baseUrl: '<%%= yeoman.app %>/scripts',                    // Directory to look for the require configuration file
                    mainConfigFile: '<%%= yeoman.app %>/scripts/main.js',    // This is relative to the grunt file
                    modules: [
                        { name: 'main' }                // Create a global bundle
                    ],
                    preserveLicenseComments: false,        // remove all comments
                    removeCombined: true,                // remove files which aren't in bundles
                    optimize: 'uglify',                    // minify bundles with uglify 2
                    useStrict: true
                }
            }
        },
<% if (testFramework === 'jasmine') { %>
        // Jasmine testing framework configuration options
        jasmine: {
            pivotal: {
                src: '<%%= yeoman.app %>/js/**/*.js',
                options: {
                    specs: 'test/spec/*Spec.js',
                    helpers: 'test/spec/*Helper.js'
                }
            }
        },<% } else { %>
        // Mocha tesing framework configuration options
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://<%%= connect.test.options.hostname %>:<%%= connect.test.options.port %>/index.html']
                }
            }
        },<% } %>
<% if (cssFramework === 'CompassFramework') { %>
        // Compiles Sass to CSS and generates necessary files if requested
        compass: {
            options: {
                sassDir: '<%%= yeoman.app %>/styles',
                cssDir: '.tmp/styles',
                generatedImagesDir: '.tmp/images/generated',
                imagesDir: '<%%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%%= yeoman.app %>/styles/fonts',
                importPath: '<%%= yeoman.app %>/bower_components',
                httpImagesPath: '/images',
                httpGeneratedImagesPath: '/images/generated',
                httpFontsPath: '/styles/fonts',
                relativeAssets: false,
                assetCacheBuster: false
            },
            dist: {
                options: {
                    generatedImagesDir: '<%%= yeoman.dist %>/images/generated'
                }
            },
            server: {
                options: {
                    debugInfo: true
                }
            }
        },<% } else if (cssFramework === 'SASSBootstrap') { %>
        sass: {
            dist: {
                 options: {
                    style: 'compressed'
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            },
            server: {
                options: {
                    debugInfo: true,
                    style: 'expanded'
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            }
        },<% } %>
        // Add vendor prefixed styles
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.',
                    app: {
                        html: '<%%= yeoman.app %>/index.html',
                        ignorePath: '<%= yeoman.app %>/'
                    },
                    dest: '.tmp/styles/'
                }]
            }
        },

        // Automatically inject Bower components into the HTML file
        'bower-install': {
            app: {
                html: '<%%= yeoman.app %>/index.html',
                ignorePath: '<%%= yeoman.app %>/'
            }
        },

        // Renames files for browser caching purposes
        rev: {
            dist: {
                files: {
                    src: [
                        '<%%= yeoman.dist %>/scripts/{,*/}*.js',
                        '<%%= yeoman.dist %>/styles/{,*/}*.css',
                        '<%%= yeoman.dist %>/images/{,*/}*.{gif,jpeg,jpg,png,webp}',
                        '<%%= yeoman.dist %>/styles/fonts/{,*/}*.*'
                    ]
                }
            }
        },

        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
            options: {
                dest: '<%%= yeoman.dist %>'
            },
            html: '<%%= yeoman.app %>/index.html'
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
            options: {
                assetsDirs: ['<%%= yeoman.dist %>']
            },
            html: ['<%%= yeoman.dist %>/{,*/}*.html'],
            css: ['<%%= yeoman.dist %>/styles/{,*/}*.css']
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/images',
                    src: '{,*/}*.{gif,jpeg,jpg,png}',
                    dest: '<%%= yeoman.dist %>/images'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%%= yeoman.dist %>/images'
                }]
            }
        },
        htmlmin: {
            dist: {
                options: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    removeCommentsFromCDATA: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true
                },
                files: [{
                    expand: true,
                    cwd: '<%%= yeoman.dist %>',
                    src: '{,*/}*.html',
                    dest: '<%%= yeoman.dist %>'
                }]
            }
        },

        // By default, your `index.html`'s <!-- Usemin block --> will take care of
        // minification. These next options are pre-configured if you do not wish
        // to use the Usemin blocks.
        cssmin: {
            dist: {
                files: {
                    '<%%= yeoman.dist %>/styles/style.css': [
                        '.tmp/styles/{,*/}*.css'
                    ]
                }
            }
        },
        uglify: {
            dist: {
                files: [{
                    src: '<%%= yeoman.app %>/scripts/*.js',  // source files mask
                    dest: '<%%= yeoman.dist %>/scripts/',    // destination folder
                    expand: true,    // allow dynamic building
                    flatten: true   // remove all unnecessary nesting
                }]
            }
        },
        concat: {
            dist: {
                src: ['.tmp/styles/{,*/}*.css'],
                dest: '.tmp/styles/style.css'
            }
        },

        // Copies remaining files to places other tasks can use
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%%= yeoman.app %>',
                    dest: '<%%= yeoman.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.webp',
                        '{,*/}*.html',
                        'styles/fonts/{,*/}*.*',
                        'bower_components/sass-bootstrap/fonts/*.*',
                        'bower_components/font-awesome/fonts/*.*'
                    ]
                }]
            },
            afterBuild: {
                files: [{
                    expand: true,
                    flatten: true,
                    dot: true,
                    cwd: '<%%= yeoman.dist %>/scripts',
                    dest: '<%%= yeoman.dist %>/scripts/vendor',
                    src: [
                        '*.js',
                        '!main.js'
                    ]
                }]

            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%%= yeoman.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },

        // Generates a custom Modernizr build that includes only the tests you
        // reference in your app
        modernizr: {
            devFile: '<%%= yeoman.app %>/bower_components/modernizr/modernizr.js',
            outputFile: '<%%= yeoman.dist %>/bower_components/modernizr/modernizr.js',
            files: [
                '<%%= yeoman.dist %>/scripts/{,*/}*.js',
                '<%%= yeoman.dist %>/styles/{,*/}*.css',
                '!<%%= yeoman.dist %>/scripts/vendor/*'
            ],
            uglify: true
        },

        // Run some tasks in parallel to speed up build process
        concurrent: {
            server: [ <% if (cssFramework === 'CompassFramework') { %>
                'compass:server',<% } else if (cssFramework === 'SASSBootstrap') { %>
                'sass:server',<% } %>
                'copy:styles'
            ],
            test: [
                'copy:styles'
            ],
            dist: [ <% if (cssFramework === 'CompassFramework') { %>
                'compass',<% } else if (cssFramework === 'SASSBootstrap') { %>
                'sass:dist',<% } %>
                'copy:styles',
                'imagemin',
                'svgmin'
            ]
        }
    });
    grunt.registerTask('bundle-js', ['bower']);

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'concat',
            'autoprefixer',
            'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('server', function () {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run(['serve']);
    });

    grunt.registerTask('test', function(target) {
        if (target !== 'watch') {
            grunt.task.run([
                'clean:server',
                'concurrent:test',
                'autoprefixer',
            ]);
        }

        grunt.task.run([
            'connect:test',<% if (testFramework === 'mocha') { %>
            'mocha'<% } else if (testFramework === 'jasmine') { %>
            'jasmine'<% } %>
        ]);
    });

    grunt.registerTask('requirejs-bundle', function() {
        function replaceBetween(string, start, end, what) {
            return string.substring(0, start) + what + string.substring(end);
        };

        var mainjs = grunt.file.read('dist/scripts/main.js'), first, second, content;

        while (mainjs.indexOf('../bower_components') != -1) {
            first = mainjs.indexOf('../bower_components');
            second = mainjs.indexOf('"', first);
            content = 'vendor/' + mainjs.substring(first, second).split('/').pop();
            mainjs = replaceBetween(mainjs, first, second, content);
        }
        grunt.file.write('dist/scripts/main.js', mainjs);
    });

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'cssmin',
        'requirejs',
        'copy:afterBuild',
        'clean:afterBuild',
        'requirejs-bundle',
        // 'uglify',
        'copy:dist',
        'modernizr',
        // 'rev',
        'usemin',
        'htmlmin'
    ]);

    grunt.registerTask('default', [
        'newer:jshint',
        'test',
        'build'
    ]);
};
<|endoftext|>