<commit_before>import Ember from 'ember';
import Configuration from '../configuration';
import TokenAuthenticator from './token';

/**
  JWT (JSON Web Token) Authenticator that supports automatic token refresh.

  Inspired by [ember-simple-auth-oauth2](https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2)

  The factory for this authenticator is registered as
  'authenticator:jwt` in Ember's container.

  @class JWT
  @namespace SimpleAuth.Authenticators
  @module ember-simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    The endpoint on the server for refreshing a token.
    @property serverTokenRefreshEndpoint
    @type String
    @default '/api/token-refresh/'
  */
  serverTokenRefreshEndpoint: '/api/token-refresh/',

  /**
    Sets whether the authenticator automatically refreshes access tokens.
    @property refreshAccessTokens
    @type Boolean
    @default true
  */
  refreshAccessTokens: true,

  /**
    The number of seconds to subtract from the token's time of expiration when
    scheduling the automatic token refresh call.
    @property refreshLeeway
    @type Integer
    @default 0 (seconds)
  */
  refreshLeeway: 0,

  /**
    The amount of time to wait before refreshing the token - set automatically.
    @property refreshTokenTimeout
    @private
  */
  refreshTokenTimeout: null,

  /**
    The name for which decoded token field represents the token expire time.
    @property tokenExpireName
    @type String
    @default 'exp'
  */
  tokenExpireName: 'exp',

  /**
    Default time unit.
    @property timeFactor
    @type Integer
    @default 1 (seconds)
  */
  timeFactor: 1,

  /**
    @method init
    @private
  */
  init() {
    this.serverTokenEndpoint = Configuration.serverTokenEndpoint;
    this.serverTokenRefreshEndpoint = Configuration.serverTokenRefreshEndpoint;
    this.identificationField = Configuration.identificationField;
    this.passwordField = Configuration.passwordField;
    this.tokenPropertyName = Configuration.tokenPropertyName;
    this.refreshAccessTokens = Configuration.refreshAccessTokens;
    this.refreshLeeway = Configuration.refreshLeeway;
    this.tokenExpireName = Configuration.tokenExpireName;
    this.timeFactor = Configuration.timeFactor;
    this.headers = Configuration.headers;
  },

  /**
    Restores the session from a set of session properties.

    It will return a resolving promise if one of two conditions is met:

    1) Both `data.token` and `data.expiresAt` are non-empty and `expiresAt`
       is greater than the calculated `now`.
    2) If `data.token` is non-empty and the decoded token has a key for
       `tokenExpireName`.

    If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will
    be called and an automatic token refresh will be initiated.

    @method restore
    @param {Object} data The data to restore the session from
    @return {Ember.RSVP.Promise} A promise that when it resolves results
                                 in the session being authenticated
  */
  restore(data) {
    const dataObject = Ember.Object.create(data);

    return new Ember.RSVP.Promise((resolve, reject) => {
      const now = this.getCurrentTime();
      const token = dataObject.get(this.tokenPropertyName);
      let expiresAt = this.resolveTime(dataObject.get(this.tokenExpireName));

      if (Ember.isEmpty(token)) {
        return reject(new Error('empty token'));
      }
      if (Ember.isEmpty(expiresAt)) {
        // Fetch the expire time from the token data since `expiresAt`
        // wasn't included in the data object that was passed in.
        const tokenData = this.getTokenData(Ember.get(token, this.tokenPropertyName));

        expiresAt = this.resolveTime(tokenData[this.tokenExpireName]);
        if (Ember.isEmpty(expiresAt)) {
          return resolve(data);
        }
      }

      if (expiresAt > now) {
        const wait = expiresAt - now - (this.refreshLeeway * 1000);

        if (wait > 0) {
          if (this.refreshAccessTokens) {
            this.scheduleAccessTokenRefresh(dataObject.get(this.tokenExpireName), token);
          }
          resolve(data);
        } else if (this.refreshAccessTokens) {
          resolve(this.refreshAccessToken(token));
        } else {
          reject(new Error('unable to refresh token'));
        }
      } else {
        reject(new Error('token is expired'));
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`.

    It will return a resolving promise if it successfully posts a request
    to the `JWT.serverTokenEndpoint` with the valid credentials.

    An automatic token refresh will be scheduled with the new expiration date
    from the returned refresh token. That expiration will be merged with the
    response and the promise resolved.

    @method authenticate
    @param {Object} credentials The credentials to authenticate the session with
    @param {Object} headers Additional headers to be sent to server
    @return {Ember.RSVP.Promise} A promise that resolves when an auth token is
                                 successfully acquired from the server and rejects
                                 otherwise
  */
  authenticate(credentials, headers) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      const data = this.getAuthenticateData(credentials);

      this.makeRequest(this.serverTokenEndpoint, data, headers).then(response => {
        Ember.run(() => {
          const sessionData = this.handleAuthResponse(response);
          resolve(sessionData);
        });
      }, xhr => {
        Ember.run(() => { reject(xhr.responseJSON || xhr.responseText); });
      });
    });
  },

  /**
    Schedules a token refresh request to be sent to the backend after a calculated
    `wait` time has passed.

    If both `token` and `expiresAt` are non-empty, and `expiresAt` minus the optional
    refres leeway is greater than the calculated `now`, the token refresh will be scheduled
    through Ember.run.later.

    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh(expiresAt, token) {
    if (this.refreshAccessTokens) {
      expiresAt = this.resolveTime(expiresAt);

      const now = this.getCurrentTime();
      const wait = expiresAt - now - (this.refreshLeeway * 1000);

      if (!Ember.isEmpty(token) && !Ember.isEmpty(expiresAt) && wait > 0) {
        Ember.run.cancel(this._refreshTokenTimeout);

        delete this._refreshTokenTimeout;

        this._refreshTokenTimeout = Ember.run.later(this, this.refreshAccessToken, token, wait);
      }
    }
  },

  /**
    Makes a refresh token request to grab a new authenticated JWT token from the server.

    It will return a resolving promise if a successful POST is made to the
    `JWT.serverTokenRefreshEndpoint`.

    After the new token is obtained it will schedule the next automatic token refresh
    based on the new `expiresAt` time.

    The session will be updated via the trigger `sessionDataUpdated`.

    @method refreshAccessToken
    @private
  */
  refreshAccessToken(token, headers) {
    const data = this.makeRefreshData(token);

    return new Ember.RSVP.Promise((resolve, reject) => {
      this.makeRequest(this.serverTokenRefreshEndpoint, data, headers).then(response => {
        Ember.run(() => {
          const sessionData = this.handleAuthResponse(response);
          this.trigger('sessionDataUpdated', sessionData);
          resolve(sessionData);
        });
      }, (xhr, status, error) => {
        Ember.Logger.warn(`Access token could not be refreshed - server responded with ${error}.`);
        reject();
      });
    });
  },

  /**
    Returns a nested object with the token property name.
    Example:  If `tokenPropertyName` is "data.user.token", `makeRefreshData` will return {data: {user: {token: "token goes here"}}}

    @method makeRefreshData
    @return {object} An object with the nested property name.
  */
  makeRefreshData(token) {
    const data = {};
    let lastObject = data;
    const nestings = this.tokenPropertyName.split('.');
    const tokenPropertyName = nestings.pop();

    nestings.forEach((nesting) => {
      lastObject[nesting] = {};
      lastObject = lastObject[nesting];
    });

    lastObject[tokenPropertyName] = token;

    return data;
  },

  /**
    Returns the decoded token with accessible returned values.

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData(token) {
    const payload = token.split('.')[1];
    const tokenData = decodeURIComponent(window.escape(atob(payload)));

    try {
      return JSON.parse(tokenData);
    } catch (e) {
      return tokenData;
    }
  },

  /**
    Accepts a `url` and `data` to be used in an ajax server request.

    @method makeRequest
    @private
  */
  makeRequest(url, data, headers) {
    return Ember.$.ajax({
      url: url,
      method: 'POST',
      data: JSON.stringify(data),
      dataType: 'json',
      contentType: 'application/json',
      headers: this.headers,
      beforeSend: (xhr, settings) => {
        xhr.setRequestHeader('Accept', settings.accepts.json);

        if (headers) {
          Object.keys(headers).forEach(key => {
            xhr.setRequestHeader(key, headers[key]);
          });
        }
      }
    });
  },

  /**
    Cancels any outstanding automatic token refreshes and returns a resolving
    promise.
    @method invalidate
    @param {Object} data The data of the session to be invalidated
    @return {Ember.RSVP.Promise} A resolving promise
  */
  invalidate() {
    Ember.run.cancel(this._refreshTokenTimeout);

    delete this._refreshTokenTimeout;

    return new Ember.RSVP.resolve();
  },

  getCurrentTime() {
    return (new Date()).getTime();
  },

  /**
    Handles converting between time units for data between different systems.
    Default: seconds(1)
    @method resolveTime
    @private
  */
  resolveTime(time) {
    if (Ember.isEmpty(time)) {
      return time;
    }
    return new Date(time * this.timeFactor).getTime();
  },

  /**
    Handles authentication response from server, and returns session data

    @method handleAuthResponse
    @private
   */
  handleAuthResponse(response) {
    const token = Ember.get(response, this.tokenPropertyName);
    const tokenData = this.getTokenData(token);
    const expiresAt = Ember.get(tokenData, this.tokenExpireName);
    const tokenExpireData = {};
    tokenExpireData[this.tokenExpireName] = expiresAt;

    this.scheduleAccessTokenRefresh(expiresAt, token);

    return Ember.merge(this.getResponseData(response), tokenExpireData);
  }
});
<commit_msg>fix deprecation Ember.merge for 2.5.0<commit_after>import Ember from 'ember';
import Configuration from '../configuration';
import TokenAuthenticator from './token';

const assign = Ember.assign || Ember.merge;

/**
  JWT (JSON Web Token) Authenticator that supports automatic token refresh.

  Inspired by [ember-simple-auth-oauth2](https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-oauth2)

  The factory for this authenticator is registered as
  'authenticator:jwt` in Ember's container.

  @class JWT
  @namespace SimpleAuth.Authenticators
  @module ember-simple-auth-token/authenticators/jwt
  @extends TokenAuthenticator
*/
export default TokenAuthenticator.extend({
  /**
    The endpoint on the server for refreshing a token.
    @property serverTokenRefreshEndpoint
    @type String
    @default '/api/token-refresh/'
  */
  serverTokenRefreshEndpoint: '/api/token-refresh/',

  /**
    Sets whether the authenticator automatically refreshes access tokens.
    @property refreshAccessTokens
    @type Boolean
    @default true
  */
  refreshAccessTokens: true,

  /**
    The number of seconds to subtract from the token's time of expiration when
    scheduling the automatic token refresh call.
    @property refreshLeeway
    @type Integer
    @default 0 (seconds)
  */
  refreshLeeway: 0,

  /**
    The amount of time to wait before refreshing the token - set automatically.
    @property refreshTokenTimeout
    @private
  */
  refreshTokenTimeout: null,

  /**
    The name for which decoded token field represents the token expire time.
    @property tokenExpireName
    @type String
    @default 'exp'
  */
  tokenExpireName: 'exp',

  /**
    Default time unit.
    @property timeFactor
    @type Integer
    @default 1 (seconds)
  */
  timeFactor: 1,

  /**
    @method init
    @private
  */
  init() {
    this.serverTokenEndpoint = Configuration.serverTokenEndpoint;
    this.serverTokenRefreshEndpoint = Configuration.serverTokenRefreshEndpoint;
    this.identificationField = Configuration.identificationField;
    this.passwordField = Configuration.passwordField;
    this.tokenPropertyName = Configuration.tokenPropertyName;
    this.refreshAccessTokens = Configuration.refreshAccessTokens;
    this.refreshLeeway = Configuration.refreshLeeway;
    this.tokenExpireName = Configuration.tokenExpireName;
    this.timeFactor = Configuration.timeFactor;
    this.headers = Configuration.headers;
  },

  /**
    Restores the session from a set of session properties.

    It will return a resolving promise if one of two conditions is met:

    1) Both `data.token` and `data.expiresAt` are non-empty and `expiresAt`
       is greater than the calculated `now`.
    2) If `data.token` is non-empty and the decoded token has a key for
       `tokenExpireName`.

    If `refreshAccessTokens` is true, `scheduleAccessTokenRefresh` will
    be called and an automatic token refresh will be initiated.

    @method restore
    @param {Object} data The data to restore the session from
    @return {Ember.RSVP.Promise} A promise that when it resolves results
                                 in the session being authenticated
  */
  restore(data) {
    const dataObject = Ember.Object.create(data);

    return new Ember.RSVP.Promise((resolve, reject) => {
      const now = this.getCurrentTime();
      const token = dataObject.get(this.tokenPropertyName);
      let expiresAt = this.resolveTime(dataObject.get(this.tokenExpireName));

      if (Ember.isEmpty(token)) {
        return reject(new Error('empty token'));
      }
      if (Ember.isEmpty(expiresAt)) {
        // Fetch the expire time from the token data since `expiresAt`
        // wasn't included in the data object that was passed in.
        const tokenData = this.getTokenData(Ember.get(token, this.tokenPropertyName));

        expiresAt = this.resolveTime(tokenData[this.tokenExpireName]);
        if (Ember.isEmpty(expiresAt)) {
          return resolve(data);
        }
      }

      if (expiresAt > now) {
        const wait = expiresAt - now - (this.refreshLeeway * 1000);

        if (wait > 0) {
          if (this.refreshAccessTokens) {
            this.scheduleAccessTokenRefresh(dataObject.get(this.tokenExpireName), token);
          }
          resolve(data);
        } else if (this.refreshAccessTokens) {
          resolve(this.refreshAccessToken(token));
        } else {
          reject(new Error('unable to refresh token'));
        }
      } else {
        reject(new Error('token is expired'));
      }
    });
  },

  /**
    Authenticates the session with the specified `credentials`.

    It will return a resolving promise if it successfully posts a request
    to the `JWT.serverTokenEndpoint` with the valid credentials.

    An automatic token refresh will be scheduled with the new expiration date
    from the returned refresh token. That expiration will be merged with the
    response and the promise resolved.

    @method authenticate
    @param {Object} credentials The credentials to authenticate the session with
    @param {Object} headers Additional headers to be sent to server
    @return {Ember.RSVP.Promise} A promise that resolves when an auth token is
                                 successfully acquired from the server and rejects
                                 otherwise
  */
  authenticate(credentials, headers) {
    return new Ember.RSVP.Promise((resolve, reject) => {
      const data = this.getAuthenticateData(credentials);

      this.makeRequest(this.serverTokenEndpoint, data, headers).then(response => {
        Ember.run(() => {
          const sessionData = this.handleAuthResponse(response);
          resolve(sessionData);
        });
      }, xhr => {
        Ember.run(() => { reject(xhr.responseJSON || xhr.responseText); });
      });
    });
  },

  /**
    Schedules a token refresh request to be sent to the backend after a calculated
    `wait` time has passed.

    If both `token` and `expiresAt` are non-empty, and `expiresAt` minus the optional
    refres leeway is greater than the calculated `now`, the token refresh will be scheduled
    through Ember.run.later.

    @method scheduleAccessTokenRefresh
    @private
  */
  scheduleAccessTokenRefresh(expiresAt, token) {
    if (this.refreshAccessTokens) {
      expiresAt = this.resolveTime(expiresAt);

      const now = this.getCurrentTime();
      const wait = expiresAt - now - (this.refreshLeeway * 1000);

      if (!Ember.isEmpty(token) && !Ember.isEmpty(expiresAt) && wait > 0) {
        Ember.run.cancel(this._refreshTokenTimeout);

        delete this._refreshTokenTimeout;

        this._refreshTokenTimeout = Ember.run.later(this, this.refreshAccessToken, token, wait);
      }
    }
  },

  /**
    Makes a refresh token request to grab a new authenticated JWT token from the server.

    It will return a resolving promise if a successful POST is made to the
    `JWT.serverTokenRefreshEndpoint`.

    After the new token is obtained it will schedule the next automatic token refresh
    based on the new `expiresAt` time.

    The session will be updated via the trigger `sessionDataUpdated`.

    @method refreshAccessToken
    @private
  */
  refreshAccessToken(token, headers) {
    const data = this.makeRefreshData(token);

    return new Ember.RSVP.Promise((resolve, reject) => {
      this.makeRequest(this.serverTokenRefreshEndpoint, data, headers).then(response => {
        Ember.run(() => {
          const sessionData = this.handleAuthResponse(response);
          this.trigger('sessionDataUpdated', sessionData);
          resolve(sessionData);
        });
      }, (xhr, status, error) => {
        Ember.Logger.warn(`Access token could not be refreshed - server responded with ${error}.`);
        reject();
      });
    });
  },

  /**
    Returns a nested object with the token property name.
    Example:  If `tokenPropertyName` is "data.user.token", `makeRefreshData` will return {data: {user: {token: "token goes here"}}}

    @method makeRefreshData
    @return {object} An object with the nested property name.
  */
  makeRefreshData(token) {
    const data = {};
    let lastObject = data;
    const nestings = this.tokenPropertyName.split('.');
    const tokenPropertyName = nestings.pop();

    nestings.forEach((nesting) => {
      lastObject[nesting] = {};
      lastObject = lastObject[nesting];
    });

    lastObject[tokenPropertyName] = token;

    return data;
  },

  /**
    Returns the decoded token with accessible returned values.

    @method getTokenData
    @return {object} An object with properties for the session.
  */
  getTokenData(token) {
    const payload = token.split('.')[1];
    const tokenData = decodeURIComponent(window.escape(atob(payload)));

    try {
      return JSON.parse(tokenData);
    } catch (e) {
      return tokenData;
    }
  },

  /**
    Accepts a `url` and `data` to be used in an ajax server request.

    @method makeRequest
    @private
  */
  makeRequest(url, data, headers) {
    return Ember.$.ajax({
      url: url,
      method: 'POST',
      data: JSON.stringify(data),
      dataType: 'json',
      contentType: 'application/json',
      headers: this.headers,
      beforeSend: (xhr, settings) => {
        xhr.setRequestHeader('Accept', settings.accepts.json);

        if (headers) {
          Object.keys(headers).forEach(key => {
            xhr.setRequestHeader(key, headers[key]);
          });
        }
      }
    });
  },

  /**
    Cancels any outstanding automatic token refreshes and returns a resolving
    promise.
    @method invalidate
    @param {Object} data The data of the session to be invalidated
    @return {Ember.RSVP.Promise} A resolving promise
  */
  invalidate() {
    Ember.run.cancel(this._refreshTokenTimeout);

    delete this._refreshTokenTimeout;

    return new Ember.RSVP.resolve();
  },

  getCurrentTime() {
    return (new Date()).getTime();
  },

  /**
    Handles converting between time units for data between different systems.
    Default: seconds(1)
    @method resolveTime
    @private
  */
  resolveTime(time) {
    if (Ember.isEmpty(time)) {
      return time;
    }
    return new Date(time * this.timeFactor).getTime();
  },

  /**
    Handles authentication response from server, and returns session data

    @method handleAuthResponse
    @private
   */
  handleAuthResponse(response) {
    const token = Ember.get(response, this.tokenPropertyName);
    const tokenData = this.getTokenData(token);
    const expiresAt = Ember.get(tokenData, this.tokenExpireName);
    const tokenExpireData = {};
    tokenExpireData[this.tokenExpireName] = expiresAt;

    this.scheduleAccessTokenRefresh(expiresAt, token);

    return assign(this.getResponseData(response), tokenExpireData);
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';

const { get, set, guidFor, merge, getOwner } = Ember;

function updateTitle(tokens) {
  set(this, 'title', tokens.toString());
}

export default Ember.Helper.extend({
  pageTitleList: Ember.inject.service(),
  headData: Ember.inject.service(),

  init() {
    this._super();
    let tokens = get(this, 'pageTitleList');
    tokens.push({ id: guidFor(this) });
  },

  compute(params, _hash) {
    let tokens = get(this, 'pageTitleList');
    let hash = merge({}, _hash);
    hash.id = guidFor(this);
    hash.title = params.join('');
    tokens.push(hash);
    Ember.run.scheduleOnce('afterRender', get(this, 'headData'), updateTitle, tokens);
    return '';
  },

  destroy() {
    let tokens = get(this, 'pageTitleList');
    let id = guidFor(this);
    tokens.remove(id);

    let activeTransition = getOwner(this).lookup('router:main').get('router.activeTransition');
    let headData = get(this, 'headData');
    if (activeTransition) {
      activeTransition.promise.finally(function () {
        if (headData.isDestroyed) { return; }
        Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
      });
    } else {
      Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
    }
  }
});
<commit_msg>Fix deprecation warning of router<commit_after>import Ember from 'ember';

const { get, set, guidFor, merge, getOwner } = Ember;

function updateTitle(tokens) {
  set(this, 'title', tokens.toString());
}

export default Ember.Helper.extend({
  pageTitleList: Ember.inject.service(),
  headData: Ember.inject.service(),

  init() {
    this._super();
    let tokens = get(this, 'pageTitleList');
    tokens.push({ id: guidFor(this) });
  },

  compute(params, _hash) {
    let tokens = get(this, 'pageTitleList');
    let hash = merge({}, _hash);
    hash.id = guidFor(this);
    hash.title = params.join('');
    tokens.push(hash);
    Ember.run.scheduleOnce('afterRender', get(this, 'headData'), updateTitle, tokens);
    return '';
  },

  destroy() {
    let tokens = get(this, 'pageTitleList');
    let id = guidFor(this);
    tokens.remove(id);

    let router = getOwner(this).lookup('router:main');
    let routes = router._routerMicrolib || router.router;
    let { activeTransition } = routes;
    let headData = get(this, 'headData');
    if (activeTransition) {
      activeTransition.promise.finally(function () {
        if (headData.isDestroyed) { return; }
        Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
      });
    } else {
      Ember.run.scheduleOnce('afterRender', headData, updateTitle, tokens);
    }
  }
});
<|endoftext|>


<commit_before>/**
 * Provides the basic core functionality.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2015 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLab/WCF/Core
 */
define([], function() {
	"use strict";

	var _clone = function(variable) {
		if (typeof variable === 'object' && variable !== null) {
			return _cloneObject(variable);
		}

		return variable;
	};

	var _cloneArray = function(oldArray) {
		var i = oldArray.length;
		var newArray = new Array(i);

		while (i--) {
			newArray[i] = oldArray[i];
		}

		return newArray;
	};

	var _cloneObject = function(obj) {
		if (!obj) {
			return null;
		}

		if (Array.isArray(obj)) {
			return _cloneArray(obj);
		}

		var newObj = {};
		for (var key in obj) {
			if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
				newObj[key] = _clone(obj[key]);
			}
		}

		return newObj;
	};

	/**
	 * @constructor
	 */
	function Core() {};
	Core.prototype = {
		/**
		 * Deep clones an object.
		 *
		 * @param	{object}	obj	source object
		 * @return	{object}	cloned object
		 */
		clone: function(obj) {
			return _clone(obj);
		},

		/**
		 * Converts WCF 2.0-style URLs into the default URL layout.
		 *
		 * @param	string	url	target url
		 * @return	rewritten url
		 */
		convertLegacyUrl: function(url) {
			if (URL_LEGACY_MODE) {
				return url;
			}

			return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) {
				var parts = controller.split(/([A-Z][a-z0-9]+)/);
				var controller = '';
				for (var i = 0, length = parts.length; i < length; i++) {
					var part = parts[i].trim();
					if (part.length) {
						if (controller.length) controller += '-';
						controller += part.toLowerCase();
					}
				}

				return 'index.php?' + controller + '/&';
			});
		},

		/**
		 * Merges objects with the first argument.
		 *
		 * @param	{object}	out		destination object
		 * @param	{...object}	arguments	variable number of objects to be merged into the destination object
		 * @return	{object}	destination object with all provided objects merged into
		 */
		extend: function(out) {
			out = out || {};

			for (var i = 1, length = arguments.length; i < length; i++) {
				var obj = arguments[i];

				if (!obj) continue;

				for (var key in obj) {
					if (obj.hasOwnProperty(key)) {
						if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
							this.extend(out[key], obj[key]);
						}
						else {
							out[key] = obj[key];
						}
					}
				}
			}

			return out;
		},

		/**
		 * Returns the object's class name.
		 *
		 * @param	{object}	obj	target object
		 * @return	{string}	object class name
		 */
		getType: function(obj) {
			return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1');
		},

		/**
		 * Returns a RFC4122 version 4 compilant UUID.
		 *
		 * @see		http://stackoverflow.com/a/2117523
		 * @return	{string}
		 */
		getUuid: function() {
			return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
				var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
				return v.toString(16);
			});
		},

		/**
		 * Recursively serializes an object into an encoded URI parameter string.
		 *
		 * @param	{object}	obj	target object
		 * @return	encoded parameter string
		 */
		serialize: function(obj) {
			var parameters = [];

			for (var key in obj) {
				if (obj.hasOwnProperty(key)) {
					var value = obj[key];

					if (Array.isArray(value)) {
						for (var i = 0, length = value.length; i < length; i++) {
							parameters.push(key + '[]=' + encodeURIComponent(value[i]));
						}

						continue;
					}
					else if (this.getType(value) === 'Object') {
						parameters.push(this.serialize(value));

						continue;
					}

					parameters.push(key + '=' + encodeURIComponent(value));
				}
			}

			return parameters.join('&');
		},

		triggerEvent: function(el, eventName) {
			var ev;
			if (document.createEvent) {
				ev = new Event(eventName);
				el.dispatchEvent(ev);
			}
			else {
				ev = document.createEventObject();
				el.fireEvent('on' + eventName, ev);
			}
		}
	};

	return new Core();
});
<commit_msg>Using Array.prototype.slice() to clone an array<commit_after>/**
 * Provides the basic core functionality.
 *
 * @author	Alexander Ebert
 * @copyright	2001-2015 WoltLab GmbH
 * @license	GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @module	WoltLab/WCF/Core
 */
define([], function() {
	"use strict";

	var _clone = function(variable) {
		if (typeof variable === 'object' && variable !== null) {
			return _cloneObject(variable);
		}

		return variable;
	};

	var _cloneArray = function(oldArray) {
		return oldArray.slice();
	};

	var _cloneObject = function(obj) {
		if (!obj) {
			return null;
		}

		if (Array.isArray(obj)) {
			return _cloneArray(obj);
		}

		var newObj = {};
		for (var key in obj) {
			if (obj.hasOwnProperty(key) && typeof obj[key] !== 'undefined') {
				newObj[key] = _clone(obj[key]);
			}
		}

		return newObj;
	};

	/**
	 * @constructor
	 */
	function Core() {};
	Core.prototype = {
		/**
		 * Deep clones an object.
		 *
		 * @param	{object}	obj	source object
		 * @return	{object}	cloned object
		 */
		clone: function(obj) {
			return _clone(obj);
		},

		/**
		 * Converts WCF 2.0-style URLs into the default URL layout.
		 *
		 * @param	string	url	target url
		 * @return	rewritten url
		 */
		convertLegacyUrl: function(url) {
			if (URL_LEGACY_MODE) {
				return url;
			}

			return url.replace(/^index\.php\/(.*?)\/\?/, function(match, controller) {
				var parts = controller.split(/([A-Z][a-z0-9]+)/);
				var controller = '';
				for (var i = 0, length = parts.length; i < length; i++) {
					var part = parts[i].trim();
					if (part.length) {
						if (controller.length) controller += '-';
						controller += part.toLowerCase();
					}
				}

				return 'index.php?' + controller + '/&';
			});
		},

		/**
		 * Merges objects with the first argument.
		 *
		 * @param	{object}	out		destination object
		 * @param	{...object}	arguments	variable number of objects to be merged into the destination object
		 * @return	{object}	destination object with all provided objects merged into
		 */
		extend: function(out) {
			out = out || {};

			for (var i = 1, length = arguments.length; i < length; i++) {
				var obj = arguments[i];

				if (!obj) continue;

				for (var key in obj) {
					if (obj.hasOwnProperty(key)) {
						if (!Array.isArray(obj[key]) && typeof obj[key] === 'object') {
							this.extend(out[key], obj[key]);
						}
						else {
							out[key] = obj[key];
						}
					}
				}
			}

			return out;
		},

		/**
		 * Returns the object's class name.
		 *
		 * @param	{object}	obj	target object
		 * @return	{string}	object class name
		 */
		getType: function(obj) {
			return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1');
		},

		/**
		 * Returns a RFC4122 version 4 compilant UUID.
		 *
		 * @see		http://stackoverflow.com/a/2117523
		 * @return	{string}
		 */
		getUuid: function() {
			return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
				var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
				return v.toString(16);
			});
		},

		/**
		 * Recursively serializes an object into an encoded URI parameter string.
		 *
		 * @param	{object}	obj	target object
		 * @return	encoded parameter string
		 */
		serialize: function(obj) {
			var parameters = [];

			for (var key in obj) {
				if (obj.hasOwnProperty(key)) {
					var value = obj[key];

					if (Array.isArray(value)) {
						for (var i = 0, length = value.length; i < length; i++) {
							parameters.push(key + '[]=' + encodeURIComponent(value[i]));
						}

						continue;
					}
					else if (this.getType(value) === 'Object') {
						parameters.push(this.serialize(value));

						continue;
					}

					parameters.push(key + '=' + encodeURIComponent(value));
				}
			}

			return parameters.join('&');
		},

		triggerEvent: function(el, eventName) {
			var ev;
			if (document.createEvent) {
				ev = new Event(eventName);
				el.dispatchEvent(ev);
			}
			else {
				ev = document.createEventObject();
				el.fireEvent('on' + eventName, ev);
			}
		}
	};

	return new Core();
});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { withStyles } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import logger from 'js/utils/logger'
import fetchSearchResults from 'js/components/Search/fetchSearchResults'
import YPAConfiguration from 'js/components/Search/YPAConfiguration'
import { isReactSnapClient } from 'js/utils/search-utils'

const styles = theme => ({
  searchAdsContainer: {
    '& iframe': {
      width: '100%',
    },
    // To address the unnecessary bottom padding in the ads
    // iframe.
    marginBottom: -16,
  },
  searchResultsContainer: {
    '& iframe': {
      width: '100%',
    },
  },
})

class SearchResults extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      searchProviderJSLoaded: !!window.ypaAds,
      noSearchResults: false,
      unexpectedSearchError: false,
    }
  }

  getSearchResults() {
    // TODO: if it does not exist, don't query. Instead, load the
    //   script and wait for it to re-query.
    // console.log('getSearchResults: window.ypaAds exists:', !!window.ypaAds)
    if (!window.ypaAds) {
      logger.error(`
        Search provider Javascript not loaded.
        Could not fetch search results.`)
      this.setState({
        unexpectedSearchError: true,
      })
      return
    }
    const { query } = this.props
    if (!query) {
      return
    }

    // Reset state of search results.
    this.setState({
      noSearchResults: false,
      unexpectedSearchError: false,
    })

    const self = this
    try {
      fetchSearchResults(query, err => {
        if (err.NO_COVERAGE) {
          // No results for this search.
          self.setState({
            noSearchResults: true,
          })
        } else if (err.URL_UNREGISTERED) {
          self.setState({
            unexpectedSearchError: true,
          })
          logger.error(
            new Error('Domain is not registered with our search partner.')
          )
        } else {
          self.setState({
            unexpectedSearchError: true,
          })
          logger.error(new Error('Unexpected search error:', err))
        }
      })
    } catch (e) {
      this.setState({
        unexpectedSearchError: true,
      })
      logger.error(e)
    }
  }

  componentDidMount() {
    // When prerendering the page, add an inline script to fetch
    // search results even before parsing our app JS.
    // TODO: have the inline script handle "no results" and
    //   search errors.
    if (isReactSnapClient()) {
      try {
        // TODO: add tests
        const js = `
          try {
            window.ypaAds.insertMultiAd(${JSON.stringify(YPAConfiguration)})
            window.searchforacause.search.fetchedOnPageLoad = true
          } catch (e) {
            console.error(e)
          }
        `
        const s = document.createElement('script')
        s.type = 'text/javascript'
        s.dataset['testId'] = 'search-inline-script'
        s.innerHTML = js

        // Render the script immediately after our app's DOM root.
        // Important: the target divs for search results must exist
        // in the DOM *before* we call YPA's JS. Otherwise, YPA will
        // not fetch search results.
        const reactRoot = document.getElementById('root')
        reactRoot.parentNode.insertBefore(s, reactRoot.nextSibling)
      } catch (e) {
        console.error(
          'Could not prerender the inline script to fetch search results.'
        )
      }
    }
  }

  componentDidUpdate(prevProps) {
    // Fetch search results if a query exists and the query
    // has changed.
    // TODO: add tests
    if (this.props.query && this.props.query !== prevProps.query) {
      // If this is the first query, we may have already fetched
      // results via inline script. Don't fetch them this time.
      const alreadyFetchedQuery =
        window.searchforacause.search.fetchedOnPageLoad
      if (alreadyFetchedQuery) {
        window.searchforacause.search.fetchedOnPageLoad = false
      } else {
        this.getSearchResults()
      }
    }
  }

  render() {
    const { query, classes, style } = this.props
    // TODO: set the "no results" callback on the Helmet script.
    return (

        <Helmet>
          <link
            rel="stylesheet"
            href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
          />
        </Helmet>

          {this.state.noSearchResults ? (
            <Typography variant={'body1'} gutterBottom>
              No results found for{' '}
              {query}
            </Typography>
          ) : null}
          {this.state.unexpectedSearchError ? (
            <Typography variant={'body1'} gutterBottom>
              Unable to search at this time.
            </Typography>
          ) : null}
          <div
            id="search-ads"
            className={classes.searchAdsContainer}
            // Important: if these containers are unmounted or mutated,
            // YPA's JS will cancel the call to fetch search results.
            // Using dangerouslySetInnerHTML and suppressHydrationWarning
            // prevents rerendering this element during hydration:
            // https://github.com/reactjs/rfcs/pull/46#issuecomment-385182716
            // Related: https://github.com/facebook/react/issues/6622
            dangerouslySetInnerHTML={{
              __html: '',
            }}
            suppressHydrationWarning
          />

    )
  }
}

SearchResults.propTypes = {
  query: PropTypes.string,
  classes: PropTypes.object.isRequired,
  style: PropTypes.object,
}

SearchResults.defaultProps = {
  style: {},
}

export default withStyles(styles)(SearchResults)
<commit_msg>Add comment and remove some unused code<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { Helmet } from 'react-helmet'
import { withStyles } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import logger from 'js/utils/logger'
import fetchSearchResults from 'js/components/Search/fetchSearchResults'
import YPAConfiguration from 'js/components/Search/YPAConfiguration'
import { isReactSnapClient } from 'js/utils/search-utils'

// This component expects the YPA search JS to already have
// executed and for the `searchforacause` global variable
// to be defined.

const styles = theme => ({
  searchAdsContainer: {
    '& iframe': {
      width: '100%',
    },
    // To address the unnecessary bottom padding in the ads
    // iframe.
    marginBottom: -16,
  },
  searchResultsContainer: {
    '& iframe': {
      width: '100%',
    },
  },
})

class SearchResults extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      noSearchResults: false,
      unexpectedSearchError: false,
    }
  }

  getSearchResults() {
    if (!window.ypaAds) {
      logger.error(`
        Search provider Javascript not loaded.
        Could not fetch search results.`)
      this.setState({
        unexpectedSearchError: true,
      })
      return
    }
    const { query } = this.props
    if (!query) {
      return
    }

    // Reset state of search results.
    this.setState({
      noSearchResults: false,
      unexpectedSearchError: false,
    })

    const self = this
    try {
      fetchSearchResults(query, err => {
        if (err.NO_COVERAGE) {
          // No results for this search.
          self.setState({
            noSearchResults: true,
          })
        } else if (err.URL_UNREGISTERED) {
          self.setState({
            unexpectedSearchError: true,
          })
          logger.error(
            new Error('Domain is not registered with our search partner.')
          )
        } else {
          self.setState({
            unexpectedSearchError: true,
          })
          logger.error(new Error('Unexpected search error:', err))
        }
      })
    } catch (e) {
      this.setState({
        unexpectedSearchError: true,
      })
      logger.error(e)
    }
  }

  componentDidMount() {
    // When prerendering the page, add an inline script to fetch
    // search results even before parsing our app JS.
    // TODO: have the inline script handle "no results" and
    //   search errors.
    if (isReactSnapClient()) {
      try {
        // TODO: add tests
        const js = `
          try {
            window.ypaAds.insertMultiAd(${JSON.stringify(YPAConfiguration)})
            window.searchforacause.search.fetchedOnPageLoad = true
          } catch (e) {
            console.error(e)
          }
        `
        const s = document.createElement('script')
        s.type = 'text/javascript'
        s.dataset['testId'] = 'search-inline-script'
        s.innerHTML = js

        // Render the script immediately after our app's DOM root.
        // Important: the target divs for search results must exist
        // in the DOM *before* we call YPA's JS. Otherwise, YPA will
        // not fetch search results.
        const reactRoot = document.getElementById('root')
        reactRoot.parentNode.insertBefore(s, reactRoot.nextSibling)
      } catch (e) {
        console.error(
          'Could not prerender the inline script to fetch search results.'
        )
      }
    }
  }

  componentDidUpdate(prevProps) {
    // Fetch search results if a query exists and the query
    // has changed.
    // TODO: add tests
    if (this.props.query && this.props.query !== prevProps.query) {
      // If this is the first query, we may have already fetched
      // results via inline script. Don't fetch them this time.
      const alreadyFetchedQuery =
        window.searchforacause.search.fetchedOnPageLoad
      if (alreadyFetchedQuery) {
        window.searchforacause.search.fetchedOnPageLoad = false
      } else {
        this.getSearchResults()
      }
    }
  }

  render() {
    const { query, classes, style } = this.props
    return (

        <Helmet>
          <link
            rel="stylesheet"
            href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
          />
        </Helmet>

          {this.state.noSearchResults ? (
            <Typography variant={'body1'} gutterBottom>
              No results found for{' '}
              {query}
            </Typography>
          ) : null}
          {this.state.unexpectedSearchError ? (
            <Typography variant={'body1'} gutterBottom>
              Unable to search at this time.
            </Typography>
          ) : null}
          <div
            id="search-ads"
            className={classes.searchAdsContainer}
            // Important: if these containers are unmounted or mutated,
            // YPA's JS will cancel the call to fetch search results.
            // Using dangerouslySetInnerHTML and suppressHydrationWarning
            // prevents rerendering this element during hydration:
            // https://github.com/reactjs/rfcs/pull/46#issuecomment-385182716
            // Related: https://github.com/facebook/react/issues/6622
            dangerouslySetInnerHTML={{
              __html: '',
            }}
            suppressHydrationWarning
          />

    )
  }
}

SearchResults.propTypes = {
  query: PropTypes.string,
  classes: PropTypes.object.isRequired,
  style: PropTypes.object,
}

SearchResults.defaultProps = {
  style: {},
}

export default withStyles(styles)(SearchResults)
<|endoftext|>


<commit_before>/*
Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';

	// Velocity ${xxx.yyy} expressions
	config.protectedSource.push( /\$\{.*?\}/gi );
};
<commit_msg>http://code.google.com/p/vosao/issues/detail?id=143<commit_after>/*
Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/

CKEDITOR.editorConfig = function( config )
{
	// Define changes to default configuration here. For example:
	// config.language = 'fr';
	// config.uiColor = '#AADC6E';

	// Velocity ${xxx.yyy} expressions
	config.protectedSource.push( /[^\\]\$\{.*?\}/gi );
};
<|endoftext|>


<commit_before>// escience reproducible experiments DSL Create controller
App.DslCreateController = Ember.Controller.extend({
    needs : ['userWelcome'],

    // NYI
    experiment_yaml_static : null,
    experiment_yaml : function(key, value){
        if (arguments.length > 1){//setter
            var formatted = Ember.String.htmlSafe(value);
            this.set('experiment_yaml_static',formatted);
        }
        return Ember.isEmpty(this.get('experiment_yaml_static')) ? '' : this.get('experiment_yaml_static');//getter
    }.property('experiment_yaml_static'),

    // create from cluster, saves metadata to pithos and registers in our DB
    dsl_filename_static : null,
    dsl_filename : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_filename_static', value);
        }
        return Ember.isEmpty(this.get('dsl_filename_static')) ? '' : this.get('dsl_filename_static');//getter
    }.property('dsl_filename_static'),
    dsl_pithos_path_static : null,
    dsl_pithos_path : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_pithos_path_static', value);
        }
        return Ember.isEmpty(this.get('dsl_pithos_path_static')) ? '' : this.get('dsl_pithos_path_static');//getter
    }.property('dsl_pithos_path_static'),
    // import from pithos, registers existing metadata from pithos to our DB
    dsl_filename_import_static : null,
    dsl_filename_import : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_filename_import_static', value);
        }
        return Ember.isEmpty(this.get('dsl_filename_import_static')) ? '' : this.get('dsl_filename_import_static');//getter
    }.property('dsl_filename_import_static'),
    dsl_pithos_path_import_static : null,
    dsl_pithos_path_import : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_pithos_path_import_static', value);
        }
        return Ember.isEmpty(this.get('dsl_pithos_path_import_static')) ? '' : this.get('dsl_pithos_path_import_static');//getter
    }.property('dsl_pithos_path_import_static'),
    dsl_filename_import_placeholder : function(){
        return Ember.isEmpty(this.get('dsl_filename_import')) ? '<placeholder>' : this.get('dsl_filename_import');
    }.property('dsl_filename_import'),
    dsl_pithos_path_import_placeholder : function(){
        return Ember.isEmpty(this.get('dsl_pithos_path_import')) ? '<placeholder>' : this.get('dsl_pithos_path_import');
    }.property('dsl_pithos_path_import'),

    // userclusters block
    filtered_clusters : function(){
        var filter_by = this.get('cluster_active_filter') && ['ACTIVE'] || ['DESTROYED','ACTIVE'];
        return Ember.isEmpty(this.get('user_clusters')) ? [] : this.get('user_clusters').filter(function(item, index, original) {
            return filter_by.contains(item.get('cluster_status_verbose'));
        });
    }.property('user_clusters.[]','user_clusters.isLoaded','cluster_active_filter'),

    boolean_no_cluster : true,
    cluster_select_observer : function(){
        if (Ember.isEmpty(this.get('selected_cluster_id'))){
            this.set('boolean_no_cluster',true);
            this.set('selected_cluster',null);
        }else{
            var selected_cluster = this.get('filtered_clusters').filterBy('id',this.get('selected_cluster_id'));
            if (Ember.isEmpty(selected_cluster)) this.set('selected_cluster_id',null);
            this.set('selected_cluster',selected_cluster);
            this.set('boolean_no_cluster',false);
            this.set('alert_missing_input_dsl_source',null);
        }
    }.observes('selected_cluster_id','filtered_clusters'),
    create_dsl_disabled : function(){
    	return this.get('boolean_no_cluster') ? true : false;
    }.property('boolean_no_cluster'),
    import_dsl_disabled : function(){
    	return Ember.isEmpty(this.get('dsl_filename_import')) && Ember.isEmpty(this.get('dsl_pithos_path_import'));
    }.property('dsl_filename_import','dsl_pithos_path_import'),
    selected_cluster_size : function(){
        return Ember.isEmpty(this.get('selected_cluster')) ? '' : this.get('selected_cluster').objectAt(0).get('cluster_size');
    }.property('selected_cluster'),
    selected_cluster_master_flavor : function(){
        var cluster = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0);
        return cluster && '[CPUx%@, RAM:%@MiB, DISK:%@GiB]'.fmt(cluster.get('cpu_master'),cluster.get('ram_master'),cluster.get('disk_master')) || '';
    }.property('selected_cluster'),
    selected_cluster_slaves_flavor : function(){
        var cluster = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0);
        return cluster && '[CPUx%@, RAM:%@MiB, DISK:%@GiB]'.fmt(cluster.get('cpu_slaves'),cluster.get('ram_slaves'),cluster.get('disk_slaves')) || '';
    }.property('selected_cluster'),
    selected_cluster_slaves_number : function(){
        var cluster_slaves = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0).get('cluster_slaves_num');
        return '%@x '.fmt(cluster_slaves);
    }.property('selected_cluster'),
    selected_cluster_project : function(){
        return Ember.isEmpty(this.get('selected_cluster')) ? '' : this.get('selected_cluster').objectAt(0).get('project_name');
    }.property('selected_cluster'),

    //utility functions
    alert_input_missing_boundto : {
        // data column > alert message property, input control element id
        cluster_id : ['alert_missing_input_dsl_source','#id_dsl_cluster'],
        dsl_name : ['alert_missing_input_dsl_name','#id_dsl_filename'],
        pithos_path : ['alert_missing_input_pithos_path','#id_pithos_destination'],
    },
    alert_input_missing_text : {
        // alert message property > message text
        alert_missing_input_dsl_source : 'Please select a cluster to save config metadata from',
        alert_missing_input_dsl_name : 'Please type in or generate default metadata filename',
        alert_missing_input_pithos_path : 'Please type in or generate default pithos container/path',
    },
    missing_input : function(that, new_dsl){
        var self = that; // get the controller reference into self
        // clear alerts on new check
        for (alert in self.get('alert_input_missing_text')){
            self.set(alert,null);
        }
        for (property in new_dsl) {
            if (Ember.isEmpty(new_dsl[property])) {
                var alert_prop_name = self.get('alert_input_missing_boundto')[property][0];
                self.set(alert_prop_name,self.get('alert_input_missing_text')[alert_prop_name]);
                var input_element = $(self.get('alert_input_missing_boundto')[property][1]);
                window.scrollTo(input_element.offsetLeft, input_element.offsetTop);
                input_element.focus();
                return true;
            }
        }
        return false;
    },

    actions : {
        dsl_create : function(create) {
            var self = this;
            var store = this.get('store');
            var model = this.get('content');
            var cluster_id = this.get('selected_cluster_id');
            var dsl_name = this.get('dsl_filename');
            var pithos_path = this.get('dsl_pithos_path');
            var new_dsl = {
                'cluster_id' : cluster_id,
                'dsl_name' : dsl_name,
                'pithos_path' : pithos_path
            };
            if (this.get('missing_input')(self, new_dsl)){
                return;
            }
            // unload cached records
            store.unloadAll('dsl');
            store.fetch('user', 1).then(function(user) {
                //success
                var new_record = store.createRecord('dsl', new_dsl);
                new_record.save().then(function(data) {
                    var msg = {
                        'msg_type' : 'success',
                        'msg_text' : 'Experiment metadata saved as \"%@\" in %@'.fmt(dsl_name,pithos_path)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.set('controllers.userWelcome.create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                }, function(reason) {
                	var error_msg = !Ember.isEmpty(reason.statusText) ? 'with error: %@'.fmt(reason.statusText) : '';
                    var msg = {
                        'msg_type' : 'danger',
                        'msg_text' : 'Failed to create file \"%@\" in %@ %@'.fmt(dsl_name,pithos_path, error_msg)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.get('controllers.userWelcome').set('create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                });
            }, function(reason) {
                //error
                console.log(reason.message);
            });
        },
        dsl_import : function(){
            var self = this;
            var store = this.get('store');
            var model = this.get('content');
            var cluster_id = -1;
            var dsl_name = this.get('dsl_filename_import');
            var pithos_path = this.get('dsl_pithos_path_import');
            var new_dsl = {
                'cluster_id' : cluster_id,
                'dsl_name' : dsl_name,
                'pithos_path' : pithos_path
            };
            this.set('alert_error_import_file',null);
            // unload cached records
            store.unloadAll('dsl');
            store.fetch('user', 1).then(function(user) {
                //success
                var new_record = store.createRecord('dsl', new_dsl);
                new_record.save().then(function(data) {
                    var msg = {
                        'msg_type' : 'success',
                        'msg_text' : 'Experiment file \"%@\" imported from %@'.fmt(dsl_name,pithos_path)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.set('controllers.userWelcome.create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                }, function(reason) {
                    var error_msg = !Ember.isEmpty(reason.statusText) ? 'with error: %@'.fmt(reason.statusText) : '';
                    var msg = {
                        'msg_type' : 'danger',
                        'msg_text' : 'Failed to import file \"%@\" from %@ %@'.fmt(dsl_name,pithos_path, error_msg)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.get('controllers.userWelcome').set('create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    self.set('alert_error_import_file',msg['msg_text']);
                });
            }, function(reason) {
                //error
                console.log(reason.message);
            });
        },
        dsl_filename_default : function() {
            var model = this.get('content');
            var store = this.get('store');
            var date_now = Ember.String.htmlSafe(moment(Date.now()).format('YYYY-MM-DD_HH-mm-ss'))['string'];
            var cluster_id = null;
            var cluster_name = null;
            if (!Ember.isEmpty(this.get('selected_cluster'))){
                cluster_id = this.get('selected_cluster_id');
                cluster_name = this.get('selected_cluster').objectAt(0).get('cluster_name_noprefix') || '';
            }
            if (Ember.isEmpty(cluster_id) || Ember.isEmpty(cluster_name)){
                this.set('alert_missing_input_dsl_source',this.get('alert_input_missing_text')['alert_missing_input_dsl_source']);
                return;
            }
            var default_filename = "%@_%@_%@-%@".fmt(cluster_name, cluster_id, date_now, 'cluster-metadata');
            this.set('dsl_filename', default_filename);
        },
        dsl_pithospath_default : function() {
            this.set('dsl_pithos_path', 'pithos');
        },
        set_selected_cluster : function(cluster_id){
            var clusterid = cluster_id || this.get('cluster_id') || 0;
            Ember.isEmpty(this.get('selected_cluster_id')) ? this.set('selected_cluster_id',cluster_id) : this.set('selected_cluster_id',this.get('selected_cluster_id'));
        },
        reset : function(){
            var self = this;
            this.set('dsl_filename','');
            this.set('dsl_pithos_path','');
            this.set('selected_cluster_id',null);
            this.set('cluster_active_filter',null);
            this.set('dsl_filename_import','');
            this.set('dsl_pithos_path_import','');
            this.set('alert_error_import_file',null);
            for (alert in self.get('alert_input_missing_text')){
                self.set(alert,null);
            }
        }
    }
});
<commit_msg>Fix Import metadata enable checking (both fields need to be filled)<commit_after>// escience reproducible experiments DSL Create controller
App.DslCreateController = Ember.Controller.extend({
    needs : ['userWelcome'],

    // NYI
    experiment_yaml_static : null,
    experiment_yaml : function(key, value){
        if (arguments.length > 1){//setter
            var formatted = Ember.String.htmlSafe(value);
            this.set('experiment_yaml_static',formatted);
        }
        return Ember.isEmpty(this.get('experiment_yaml_static')) ? '' : this.get('experiment_yaml_static');//getter
    }.property('experiment_yaml_static'),

    // create from cluster, saves metadata to pithos and registers in our DB
    dsl_filename_static : null,
    dsl_filename : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_filename_static', value);
        }
        return Ember.isEmpty(this.get('dsl_filename_static')) ? '' : this.get('dsl_filename_static');//getter
    }.property('dsl_filename_static'),
    dsl_pithos_path_static : null,
    dsl_pithos_path : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_pithos_path_static', value);
        }
        return Ember.isEmpty(this.get('dsl_pithos_path_static')) ? '' : this.get('dsl_pithos_path_static');//getter
    }.property('dsl_pithos_path_static'),
    // import from pithos, registers existing metadata from pithos to our DB
    dsl_filename_import_static : null,
    dsl_filename_import : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_filename_import_static', value);
        }
        return Ember.isEmpty(this.get('dsl_filename_import_static')) ? '' : this.get('dsl_filename_import_static');//getter
    }.property('dsl_filename_import_static'),
    dsl_pithos_path_import_static : null,
    dsl_pithos_path_import : function(key, value) {
        if (arguments.length > 1) {//setter
            this.set('dsl_pithos_path_import_static', value);
        }
        return Ember.isEmpty(this.get('dsl_pithos_path_import_static')) ? '' : this.get('dsl_pithos_path_import_static');//getter
    }.property('dsl_pithos_path_import_static'),
    dsl_filename_import_placeholder : function(){
        return Ember.isEmpty(this.get('dsl_filename_import')) ? '<placeholder>' : this.get('dsl_filename_import');
    }.property('dsl_filename_import'),
    dsl_pithos_path_import_placeholder : function(){
        return Ember.isEmpty(this.get('dsl_pithos_path_import')) ? '<placeholder>' : this.get('dsl_pithos_path_import');
    }.property('dsl_pithos_path_import'),

    // userclusters block
    filtered_clusters : function(){
        var filter_by = this.get('cluster_active_filter') && ['ACTIVE'] || ['DESTROYED','ACTIVE'];
        return Ember.isEmpty(this.get('user_clusters')) ? [] : this.get('user_clusters').filter(function(item, index, original) {
            return filter_by.contains(item.get('cluster_status_verbose'));
        });
    }.property('user_clusters.[]','user_clusters.isLoaded','cluster_active_filter'),

    boolean_no_cluster : true,
    cluster_select_observer : function(){
        if (Ember.isEmpty(this.get('selected_cluster_id'))){
            this.set('boolean_no_cluster',true);
            this.set('selected_cluster',null);
        }else{
            var selected_cluster = this.get('filtered_clusters').filterBy('id',this.get('selected_cluster_id'));
            if (Ember.isEmpty(selected_cluster)) this.set('selected_cluster_id',null);
            this.set('selected_cluster',selected_cluster);
            this.set('boolean_no_cluster',false);
            this.set('alert_missing_input_dsl_source',null);
        }
    }.observes('selected_cluster_id','filtered_clusters'),
    create_dsl_disabled : function(){
    	return this.get('boolean_no_cluster') ? true : false;
    }.property('boolean_no_cluster'),
    import_dsl_disabled : function(){
    	return Ember.isEmpty(this.get('dsl_filename_import')) || Ember.isEmpty(this.get('dsl_pithos_path_import'));
    }.property('dsl_filename_import','dsl_pithos_path_import'),
    selected_cluster_size : function(){
        return Ember.isEmpty(this.get('selected_cluster')) ? '' : this.get('selected_cluster').objectAt(0).get('cluster_size');
    }.property('selected_cluster'),
    selected_cluster_master_flavor : function(){
        var cluster = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0);
        return cluster && '[CPUx%@, RAM:%@MiB, DISK:%@GiB]'.fmt(cluster.get('cpu_master'),cluster.get('ram_master'),cluster.get('disk_master')) || '';
    }.property('selected_cluster'),
    selected_cluster_slaves_flavor : function(){
        var cluster = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0);
        return cluster && '[CPUx%@, RAM:%@MiB, DISK:%@GiB]'.fmt(cluster.get('cpu_slaves'),cluster.get('ram_slaves'),cluster.get('disk_slaves')) || '';
    }.property('selected_cluster'),
    selected_cluster_slaves_number : function(){
        var cluster_slaves = Ember.isEmpty(this.get('selected_cluster')) ? null : this.get('selected_cluster').objectAt(0).get('cluster_slaves_num');
        return '%@x '.fmt(cluster_slaves);
    }.property('selected_cluster'),
    selected_cluster_project : function(){
        return Ember.isEmpty(this.get('selected_cluster')) ? '' : this.get('selected_cluster').objectAt(0).get('project_name');
    }.property('selected_cluster'),

    //utility functions
    alert_input_missing_boundto : {
        // data column > alert message property, input control element id
        cluster_id : ['alert_missing_input_dsl_source','#id_dsl_cluster'],
        dsl_name : ['alert_missing_input_dsl_name','#id_dsl_filename'],
        pithos_path : ['alert_missing_input_pithos_path','#id_pithos_destination'],
    },
    alert_input_missing_text : {
        // alert message property > message text
        alert_missing_input_dsl_source : 'Please select a cluster to save config metadata from',
        alert_missing_input_dsl_name : 'Please type in or generate default metadata filename',
        alert_missing_input_pithos_path : 'Please type in or generate default pithos container/path',
    },
    missing_input : function(that, new_dsl){
        var self = that; // get the controller reference into self
        // clear alerts on new check
        for (alert in self.get('alert_input_missing_text')){
            self.set(alert,null);
        }
        for (property in new_dsl) {
            if (Ember.isEmpty(new_dsl[property])) {
                var alert_prop_name = self.get('alert_input_missing_boundto')[property][0];
                self.set(alert_prop_name,self.get('alert_input_missing_text')[alert_prop_name]);
                var input_element = $(self.get('alert_input_missing_boundto')[property][1]);
                window.scrollTo(input_element.offsetLeft, input_element.offsetTop);
                input_element.focus();
                return true;
            }
        }
        return false;
    },

    actions : {
        dsl_create : function(create) {
            var self = this;
            var store = this.get('store');
            var model = this.get('content');
            var cluster_id = this.get('selected_cluster_id');
            var dsl_name = this.get('dsl_filename');
            var pithos_path = this.get('dsl_pithos_path');
            var new_dsl = {
                'cluster_id' : cluster_id,
                'dsl_name' : dsl_name,
                'pithos_path' : pithos_path
            };
            if (this.get('missing_input')(self, new_dsl)){
                return;
            }
            // unload cached records
            store.unloadAll('dsl');
            store.fetch('user', 1).then(function(user) {
                //success
                var new_record = store.createRecord('dsl', new_dsl);
                new_record.save().then(function(data) {
                    var msg = {
                        'msg_type' : 'success',
                        'msg_text' : 'Experiment metadata saved as \"%@\" in %@'.fmt(dsl_name,pithos_path)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.set('controllers.userWelcome.create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                }, function(reason) {
                	var error_msg = !Ember.isEmpty(reason.statusText) ? 'with error: %@'.fmt(reason.statusText) : '';
                    var msg = {
                        'msg_type' : 'danger',
                        'msg_text' : 'Failed to create file \"%@\" in %@ %@'.fmt(dsl_name,pithos_path, error_msg)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.get('controllers.userWelcome').set('create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                });
            }, function(reason) {
                //error
                console.log(reason.message);
            });
        },
        dsl_import : function(){
            var self = this;
            var store = this.get('store');
            var model = this.get('content');
            var cluster_id = -1;
            var dsl_name = this.get('dsl_filename_import');
            var pithos_path = this.get('dsl_pithos_path_import');
            var new_dsl = {
                'cluster_id' : cluster_id,
                'dsl_name' : dsl_name,
                'pithos_path' : pithos_path
            };
            this.set('alert_error_import_file',null);
            // unload cached records
            store.unloadAll('dsl');
            store.fetch('user', 1).then(function(user) {
                //success
                var new_record = store.createRecord('dsl', new_dsl);
                new_record.save().then(function(data) {
                    var msg = {
                        'msg_type' : 'success',
                        'msg_text' : 'Experiment file \"%@\" imported from %@'.fmt(dsl_name,pithos_path)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.set('controllers.userWelcome.create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    Ember.run.next(function(){self.transitionToRoute('user.welcome');});
                }, function(reason) {
                    var error_msg = !Ember.isEmpty(reason.statusText) ? 'with error: %@'.fmt(reason.statusText) : '';
                    var msg = {
                        'msg_type' : 'danger',
                        'msg_text' : 'Failed to import file \"%@\" from %@ %@'.fmt(dsl_name,pithos_path, error_msg)
                    };
                    self.get('controllers.userWelcome').send('addMessage', msg);
                    self.get('controllers.userWelcome').set('create_cluster_start', true);
                    self.get('controllers.userWelcome').send('setActiveTab','dsls');
                    self.set('alert_error_import_file',msg['msg_text']);
                });
            }, function(reason) {
                //error
                console.log(reason.message);
            });
        },
        dsl_filename_default : function() {
            var model = this.get('content');
            var store = this.get('store');
            var date_now = Ember.String.htmlSafe(moment(Date.now()).format('YYYY-MM-DD_HH-mm-ss'))['string'];
            var cluster_id = null;
            var cluster_name = null;
            if (!Ember.isEmpty(this.get('selected_cluster'))){
                cluster_id = this.get('selected_cluster_id');
                cluster_name = this.get('selected_cluster').objectAt(0).get('cluster_name_noprefix') || '';
            }
            if (Ember.isEmpty(cluster_id) || Ember.isEmpty(cluster_name)){
                this.set('alert_missing_input_dsl_source',this.get('alert_input_missing_text')['alert_missing_input_dsl_source']);
                return;
            }
            var default_filename = "%@_%@_%@-%@".fmt(cluster_name, cluster_id, date_now, 'cluster-metadata');
            this.set('dsl_filename', default_filename);
        },
        dsl_pithospath_default : function() {
            this.set('dsl_pithos_path', 'pithos');
        },
        set_selected_cluster : function(cluster_id){
            var clusterid = cluster_id || this.get('cluster_id') || 0;
            Ember.isEmpty(this.get('selected_cluster_id')) ? this.set('selected_cluster_id',cluster_id) : this.set('selected_cluster_id',this.get('selected_cluster_id'));
        },
        reset : function(){
            var self = this;
            this.set('dsl_filename','');
            this.set('dsl_pithos_path','');
            this.set('selected_cluster_id',null);
            this.set('cluster_active_filter',null);
            this.set('dsl_filename_import','');
            this.set('dsl_pithos_path_import','');
            this.set('alert_error_import_file',null);
            for (alert in self.get('alert_input_missing_text')){
                self.set(alert,null);
            }
        }
    }
});
<|endoftext|>


<commit_before>var mixin    = require('../util/mixin'),
    support  = require('../util/support'),
    uniqueId = require('../util/uniqueId'),
    Events   = require('./events');

var JSONRPCError = function(code, message) {
  this.code = code;
  this.message = '[JSONRPCError] ' + message;
};

JSONRPCError.prototype = Error.prototype;

// Facilitate multiplexed JSON-RPC.
var Channel = module.exports = function(namespace, transport) {
  if (Channel._namespaces.indexOf(namespace) >= 0) {
    throw new Error("Channel with namespace '" + namespace + "' already exists");
  }
  Channel._namespaces.push(namespace);

  this.namespace = namespace;
  this.transport = transport;
  this._callbacks = {};

  this.transport.on('incoming', this._onIncoming, this);
};

mixin(Channel, {

  JSONRPCError: JSONRPCError,

  reset: function() {
    this._namespaces = [];
  },

  _namespaces: []

});

mixin(Channel.prototype, Events, {

  // Send a JSON-RPC-structured message over this channel.
  send: function(data) {
    data || (data = {});
    var message = {
      channel: this.namespace,
      data: mixin(data, { jsonrpc: '2.0' })
    };
    this.transport.send(this.serialize(message));
  },

  // Issue a unique request `id`, associate with callback if provided, and send request.
  request: function(method, params, callback) {
    var data = {
      method: method,
      params: params
    };
    if (typeof callback === 'function') {
      data.id = uniqueId('request');
      this._callbacks[data.id] = callback;
    }
    this.send(data);
  },

  // Build and send a response referencing request `id` and providing result or error.
  respond: function(id, result, error) {
    var data = {
      id: id
    };
    if (result) data.result = result;
    else data.error = error;
    this.send(data);
  },

  serialize: function(object) {
    return support.structuredClones ? object : JSON.stringify(object);
  },

  deserialize: function(message) {
    try {
      return support.structuredClones ? message : JSON.parse(message);
    } catch (e) { return; }
  },

  destroy: function() {
    this.off();
  },

  _onIncoming: function(message) {
    message = this.deserialize(message);
    if (!message || message.channel !== this.namespace) return;
    if (message.data.error) {
      throw new JSONRPCError(message.data.error.code, message.data.error.message);
    } else {
      this._processRPC(message.data);
    }
  },

  // Signal a request or resolve a callback with response.
  // TODO: handle notifications.
  _processRPC: function(data) {
    if (data.method) {
      try {
        this.trigger('request', data.id, data.method, data.params);
      } catch (e) {
        this.respond(data.id, null, {
          code: e.code,
          message: e.message,
          data: e.stack
        });
      }
    } else if (data.id) {
      var callback = this._callbacks[data.id];
      callback(data.result, data.error);
      this._callbacks[data.id] = null;
    }
  },

});
<commit_msg>Fix Channel error test in Chrome<commit_after>var mixin    = require('../util/mixin'),
    support  = require('../util/support'),
    uniqueId = require('../util/uniqueId'),
    Events   = require('./events');

var JSONRPCError = function(code, message) {
  this.code = code;
  this.message = '[JSONRPCError] ' + message;
};

JSONRPCError.prototype = Error.prototype;

// Facilitate multiplexed JSON-RPC.
var Channel = module.exports = function(namespace, transport) {
  if (Channel._namespaces.indexOf(namespace) >= 0) {
    throw new Error("Channel with namespace '" + namespace + "' already exists");
  }
  Channel._namespaces.push(namespace);

  this.namespace = namespace;
  this.transport = transport;
  this._callbacks = {};

  this.transport.on('incoming', this._onIncoming, this);
};

mixin(Channel, {

  JSONRPCError: JSONRPCError,

  reset: function() {
    this._namespaces = [];
  },

  _namespaces: []

});

mixin(Channel.prototype, Events, {

  // Send a JSON-RPC-structured message over this channel.
  send: function(data) {
    data || (data = {});
    var message = {
      channel: this.namespace,
      data: mixin(data, { jsonrpc: '2.0' })
    };
    this.transport.send(this.serialize(message));
  },

  // Issue a unique request `id`, associate with callback if provided, and send request.
  request: function(method, params, callback) {
    var data = {
      method: method,
      params: params
    };
    if (typeof callback === 'function') {
      data.id = uniqueId('request');
      this._callbacks[data.id] = callback;
    }
    this.send(data);
  },

  // Build and send a response referencing request `id` and providing result or error.
  respond: function(id, result, error) {
    var data = {
      id: id
    };
    if (result) data.result = result;
    else data.error = error;
    this.send(data);
  },

  serialize: function(object) {
    return support.structuredClones ? object : JSON.stringify(object);
  },

  deserialize: function(message) {
    try {
      return support.structuredClones ? message : JSON.parse(message);
    } catch (e) { return; }
  },

  destroy: function() {
    this.off();
  },

  _onIncoming: function(message) {
    message = this.deserialize(message);
    if (!message || message.channel !== this.namespace) return;
    if (message.data.error) {
      throw new JSONRPCError(message.data.error.code, message.data.error.message);
    } else {
      this._processRPC(message.data);
    }
  },

  // Signal a request or resolve a callback with response.
  // TODO: handle notifications.
  _processRPC: function(data) {
    if (data.method) {
      try {
        this.trigger('request', data.id, data.method, data.params);
      } catch (e) {
        this.respond(data.id, null, {
          code: e.code,
          message: e.message,
          data: {
            stack: e.stack
          }
        });
      }
    } else if (data.id) {
      var callback = this._callbacks[data.id];
      callback(data.result, data.error);
      this._callbacks[data.id] = null;
    }
  },

});
<|endoftext|>


<commit_before>window.optly                = window.optly || {};
window.optly.mrkt           = window.optly.mrkt || {};
window.optly.mrkt.services  = window.optly.mrkt.services || {};
window.optly.mrkt.user      = window.optly.mrkt.user || {};

window.optly.mrkt.Optly_Q = function(acctData, expData){
  var objectCreateSupported = typeof Object.create === 'function';

  window.optly.PRELOAD = window.optly.PRELOAD = {};

  if(!objectCreateSupported) {
    var ThrowAway = function (a, e) {
      this.acctData = a;
      this.expData = e;
    };

    ThrowAway.prototype = window.optly.mrkt.Optly_Q.prototype;

    if(arguments.length > 0) {
      return new ThrowAway(acctData, expData);
    } else{
      return new ThrowAway();
    }
  }

  if(arguments.length > 0 ){
    window.optly.PRELOAD.token = acctData.csrf_token;

    return Object.create(window.optly.mrkt.Optly_Q.prototype, {
      acctData: {
        value: window.optly.mrkt.user.acctData = acctData,
      },
      expData: {
        value: window.optly.mrkt.user.expData = expData
      }
    });
  } else {
    var acctCache, expCache;

    return Object.create(window.optly.mrkt.Optly_Q.prototype, {
      acctData: {
        get: function(){return acctCache;},
        set: function(val){
          if(!acctCache){
            window.optly.PRELOAD.token = val.csrf_token;
            window.optly.mrkt.user.acctData = val;
            acctCache = val;
          }
        }
      },
      expData: {
        get: function(){return expCache;},
        set: function(val){
          if(!expCache){
            window.optly.mrkt.user.expData = val;
            expCache = val;
          }
        }
      }
    });
  }
};

window.optly.mrkt.Optly_Q.prototype = {

  transformQuedArgs: function(quedArgs) {
    var funcArgs = [];
    $.each(quedArgs, function(index, arg) {
      if (this[ arg ] !== undefined && arg !== 'object') {
        funcArgs.push(this[ arg ]);
      } else if(arg === 'object') {
        funcArgs.push(arg);
      }
    }.bind(this));

    return funcArgs;
  },

  parseQ: function(fnQ, i) {
    var quedArgs, transformedArgs;
    //if not a nested array
    if (typeof fnQ[i] === 'function') {
      quedArgs = fnQ.slice(1);

      //if there are no arguments call the function with the object scope
      if(quedArgs.length === 0) {
        fnQ[i].call(this);
      } else {
        transformedArgs = this.transformQuedArgs(quedArgs);
        fnQ[i].apply( fnQ[i], transformedArgs );
      }

    }
    //if a nested array
    else {
      for(var nestedI = 0; nestedI < fnQ[i].length; nestedI += 1) {

        if (typeof fnQ[i][nestedI] === 'function') {
          quedArgs = fnQ[i].slice(1);

          //if there are no arguments call the function with the object scope
          if(quedArgs.length === 0) {
            fnQ[i][nestedI].call(this);
          } else {
            transformedArgs = this.transformQuedArgs(quedArgs);
            fnQ[i][nestedI].apply( fnQ[i][nestedI], transformedArgs );
          }

        }
      }
    }
  },

  push: function(fnQ) {
    for (var i = 0; i < fnQ.length; i += 1) {
      this.parseQ(fnQ, i);
    }
  }

};

window.optly.mrkt.services.xhr = {
  makeRequest: function(request) {
    var deffereds = [], defferedPromise;

    // check if multiple requests are present
    if ( Array.isArray(request) ) {
      for (var i = 0; i < request.length; i += 1) {
        if (typeof request[i] === 'object') {
          defferedPromise = $.ajax({
            type: request[i].type,
            url: request[i].url
          });
          // parameters passed must be objects with a path and properties keys
          if (request[i].properties !== undefined) {
            this.handleErrors( defferedPromise, request[i].url, request[i].properties );
          }

          deffereds.push( defferedPromise );

        }
      }
      this.resolveDeffereds(deffereds);
      return deffereds;
    }
    // If single request, then return the promise directly
    else {
      defferedPromise = $.ajax({
        type: request.type,
        url: request.url
      });
      if (request.properties !== undefined) {
        this.handleErrors( defferedPromise, request.url, request.properties );
      }
      return defferedPromise;
    }
  },

  logSegmentError: function(url, category, errorMessage) {
    window.analytics.track(url, {
      category: category,
      label: errorMessage
    });
  },

  validateTypes: function(resp, properties, url) {
    var errorMessage;
    $.each(properties, function(property, type) {
      // if property is not nested
      if(typeof type !== 'object') {
        if (typeof resp[ property ] !== type) {
          errorMessage = 'resp.' + property + ' is not a ' + type + ': ' + typeof(resp[ property ]);

          this.logSegmentError(url, 'api error', errorMessage);
        }
      }
      // if property is nested
      else {

        this.validateNestedTypes( resp[ property ], properties[ property ], property, type, url );

      }
    }.bind(this)); // end outer .each
  },

  validateNestedTypes: function(data, nestedData, parentProperty, type, url) {
    var errorMessage,
      propertyType;
    // if the property maps to an array
    if ( Array.isArray(data) ) {

      $.each(data, function(index, innerProperties) {

        $.each(nestedData, function(innerProp, innerType) {
          propertyType = typeof innerProperties[ innerProp ];
          if (propertyType !== innerType) {
            errorMessage = 'resp.' + parentProperty + '.' + innerProp + ' is not a ' + innerType + ': ' + propertyType;

            this.logSegmentError(url, 'api error', errorMessage);
          }
        }.bind(this));

      }.bind(this));
    }
    // if the property maps to an object
    else {
      $.each(type, function(innerProp, innerType) {
        propertyType = typeof data[ innerProp ];
        if (propertyType !== innerType) {
          errorMessage = 'resp.' + parentProperty + '.' + innerProp + ' is not a ' + innerType + ': ' + propertyType;

          this.logSegmentError(url, 'api error', errorMessage);
        }
      }.bind(this));
    }
  },

  handleErrors: function(deffered, url, properties) {
    var parsedRes, errorMessage;

    deffered.always(function(data, textStatus, jqXHR) {

        // check if the last argument is a promise, if so the response was successful
        if( this.isPromise(jqXHR) && jqXHR.status === 200 ) {

          //parse JSON and catch any errors -- if error return immediately
          try {
            parsedRes = $.parseJSON(jqXHR.responseText);
          } catch (error) {

            this.logSegmentError(url, 'api error', 'response contains invalid json ' + error);

            // do not check validations if parse error
            return undefined;
          }

          // validate each property type
          this.validateTypes(parsedRes, properties, url);

        }
        // if the http request fails the jqXHR object will not be promise
        else {
          // in this case the data object is a promise so we parse it's response text
          if ( this.isPromise(data) && data.status === 200) {
            try {
              parsedRes = $.parseJSON(data.responseText);
            } catch (error) {
              errorMessage = error + ', Response Text: ' + data.responseText + ', Status Text: ' + data.statusText + ', Status: ' + data.status;
            }
          }
          if (errorMessage === undefined) {
            errorMessage = 'Response Text: ' + data.responseText + ', Status Text: ' + data.statusText + ', Status: ' + data.status;
          }

          this.logSegmentError(url, 'api error', errorMessage);

        }

    }.bind(this));

  },

  isPromise: function(value) {

    if (typeof value.then !== 'function') {
        return false;
    }
    var promiseThenSrc = String($.Deferred().then);
    var valueThenSrc = String(value.then);
    return promiseThenSrc === valueThenSrc;
  },

  resolveDeffereds: function(deffereds) {
    var responses = [], oldQue;
    $.when.apply($, deffereds).done(function() {
      // get all arguments returned from done
      var tranformedArgs = Array.prototype.slice.call(arguments);
      $.each(tranformedArgs, function(index, resp) {
        var respData = resp[0];
        if( !this.isPromise( respData ) && resp[1] === 'success' ) {
          responses.push(respData);
        }
        if (index === tranformedArgs.length - 1) {
          oldQue = window.optly_q;

          // consume qued data
          window.optly_q = window.optly.mrkt.Optly_Q(responses[0], responses[1]);
          window.optly_q.push(oldQue);
        }
      }.bind(this) );
    }.bind(this) );

    return true;
  },

  readCookie: function (name) {
    // Escape regexp special characters (thanks kangax!)
    name = name.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');

    var regex = new RegExp('(?:^|;)\\s?' + name + '=(.*?)(?:;|$)','i'),
        match = document.cookie.match(regex);

    return match && window.unescape(match[1]);
  },

  getLoginStatus: function(requestParams) {
    var deferreds;

    if ( !!this.readCookie('optimizely_signed_in') ) {
      deferreds = this.makeRequest(requestParams);
    } else {
      window.optly_q = window.optly.mrkt.Optly_Q();
    }

    return deferreds;
  }

};

(function() {
  'use strict';

  var acctParams,
    expParams;

  acctParams = {
    type: 'GET',
    url: '/account/info',
    properties: {
      email: 'string',
      account_id: 'number'
    }
  };

  expParams = {
    type: 'GET',
    url: '/experiment/load_recent?max_experiments=5',
    properties: {
      experiments: {
        id: 'number',
        description: 'string',
        has_started: 'boolean',
        can_edit: 'boolean'
      }
    }
  };

  return window.optly.mrkt.services.xhr.getLoginStatus([acctParams, expParams]);
}());
<commit_msg>quick fix for csrf_token assigment in Q<commit_after>window.optly                = window.optly || {};
window.optly.mrkt           = window.optly.mrkt || {};
window.optly.mrkt.services  = window.optly.mrkt.services || {};
window.optly.mrkt.user      = window.optly.mrkt.user || {};

window.optly.mrkt.Optly_Q = function(acctData, expData){
  var objectCreateSupported = typeof Object.create === 'function';

  window.optly.PRELOAD = window.optly.PRELOAD || {token: null};

  if(!objectCreateSupported) {
    var ThrowAway = function (a, e) {
      this.acctData = a;
      this.expData = e;

      window.optly.PRELOAD.token = this.acctData.csrf_token;
    };

    ThrowAway.prototype = window.optly.mrkt.Optly_Q.prototype;

    if(arguments.length > 0) {
      return new ThrowAway(acctData, expData);
    } else{
      return new ThrowAway();
    }
  }

  if(arguments.length > 0 ){
    window.optly.PRELOAD.token = acctData.csrf_token;

    return Object.create(window.optly.mrkt.Optly_Q.prototype, {
      acctData: {
        value: window.optly.mrkt.user.acctData = acctData,
      },
      expData: {
        value: window.optly.mrkt.user.expData = expData
      }
    });
  } else {
    var acctCache, expCache;

    return Object.create(window.optly.mrkt.Optly_Q.prototype, {
      acctData: {
        get: function(){return acctCache;},
        set: function(val){
          if(!acctCache){
            window.optly.PRELOAD.token = val.csrf_token;
            window.optly.mrkt.user.acctData = val;
            acctCache = val;
          }
        }
      },
      expData: {
        get: function(){return expCache;},
        set: function(val){
          if(!expCache){
            window.optly.mrkt.user.expData = val;
            expCache = val;
          }
        }
      }
    });
  }
};

window.optly.mrkt.Optly_Q.prototype = {

  transformQuedArgs: function(quedArgs) {
    var funcArgs = [];
    $.each(quedArgs, function(index, arg) {
      if (this[ arg ] !== undefined && arg !== 'object') {
        funcArgs.push(this[ arg ]);
      } else if(arg === 'object') {
        funcArgs.push(arg);
      }
    }.bind(this));

    return funcArgs;
  },

  parseQ: function(fnQ, i) {
    var quedArgs, transformedArgs;
    //if not a nested array
    if (typeof fnQ[i] === 'function') {
      quedArgs = fnQ.slice(1);

      //if there are no arguments call the function with the object scope
      if(quedArgs.length === 0) {
        fnQ[i].call(this);
      } else {
        transformedArgs = this.transformQuedArgs(quedArgs);
        fnQ[i].apply( fnQ[i], transformedArgs );
      }

    }
    //if a nested array
    else {
      for(var nestedI = 0; nestedI < fnQ[i].length; nestedI += 1) {

        if (typeof fnQ[i][nestedI] === 'function') {
          quedArgs = fnQ[i].slice(1);

          //if there are no arguments call the function with the object scope
          if(quedArgs.length === 0) {
            fnQ[i][nestedI].call(this);
          } else {
            transformedArgs = this.transformQuedArgs(quedArgs);
            fnQ[i][nestedI].apply( fnQ[i][nestedI], transformedArgs );
          }

        }
      }
    }
  },

  push: function(fnQ) {
    for (var i = 0; i < fnQ.length; i += 1) {
      this.parseQ(fnQ, i);
    }
  }

};

window.optly.mrkt.services.xhr = {
  makeRequest: function(request) {
    var deffereds = [], defferedPromise;

    // check if multiple requests are present
    if ( Array.isArray(request) ) {
      for (var i = 0; i < request.length; i += 1) {
        if (typeof request[i] === 'object') {
          defferedPromise = $.ajax({
            type: request[i].type,
            url: request[i].url
          });
          // parameters passed must be objects with a path and properties keys
          if (request[i].properties !== undefined) {
            this.handleErrors( defferedPromise, request[i].url, request[i].properties );
          }

          deffereds.push( defferedPromise );

        }
      }
      this.resolveDeffereds(deffereds);
      return deffereds;
    }
    // If single request, then return the promise directly
    else {
      defferedPromise = $.ajax({
        type: request.type,
        url: request.url
      });
      if (request.properties !== undefined) {
        this.handleErrors( defferedPromise, request.url, request.properties );
      }
      return defferedPromise;
    }
  },

  logSegmentError: function(url, category, errorMessage) {
    window.analytics.track(url, {
      category: category,
      label: errorMessage
    });
  },

  validateTypes: function(resp, properties, url) {
    var errorMessage;
    $.each(properties, function(property, type) {
      // if property is not nested
      if(typeof type !== 'object') {
        if (typeof resp[ property ] !== type) {
          errorMessage = 'resp.' + property + ' is not a ' + type + ': ' + typeof(resp[ property ]);

          this.logSegmentError(url, 'api error', errorMessage);
        }
      }
      // if property is nested
      else {

        this.validateNestedTypes( resp[ property ], properties[ property ], property, type, url );

      }
    }.bind(this)); // end outer .each
  },

  validateNestedTypes: function(data, nestedData, parentProperty, type, url) {
    var errorMessage,
      propertyType;
    // if the property maps to an array
    if ( Array.isArray(data) ) {

      $.each(data, function(index, innerProperties) {

        $.each(nestedData, function(innerProp, innerType) {
          propertyType = typeof innerProperties[ innerProp ];
          if (propertyType !== innerType) {
            errorMessage = 'resp.' + parentProperty + '.' + innerProp + ' is not a ' + innerType + ': ' + propertyType;

            this.logSegmentError(url, 'api error', errorMessage);
          }
        }.bind(this));

      }.bind(this));
    }
    // if the property maps to an object
    else {
      $.each(type, function(innerProp, innerType) {
        propertyType = typeof data[ innerProp ];
        if (propertyType !== innerType) {
          errorMessage = 'resp.' + parentProperty + '.' + innerProp + ' is not a ' + innerType + ': ' + propertyType;

          this.logSegmentError(url, 'api error', errorMessage);
        }
      }.bind(this));
    }
  },

  handleErrors: function(deffered, url, properties) {
    var parsedRes, errorMessage;

    deffered.always(function(data, textStatus, jqXHR) {

        // check if the last argument is a promise, if so the response was successful
        if( this.isPromise(jqXHR) && jqXHR.status === 200 ) {

          //parse JSON and catch any errors -- if error return immediately
          try {
            parsedRes = $.parseJSON(jqXHR.responseText);
          } catch (error) {

            this.logSegmentError(url, 'api error', 'response contains invalid json ' + error);

            // do not check validations if parse error
            return undefined;
          }

          // validate each property type
          this.validateTypes(parsedRes, properties, url);

        }
        // if the http request fails the jqXHR object will not be promise
        else {
          // in this case the data object is a promise so we parse it's response text
          if ( this.isPromise(data) && data.status === 200) {
            try {
              parsedRes = $.parseJSON(data.responseText);
            } catch (error) {
              errorMessage = error + ', Response Text: ' + data.responseText + ', Status Text: ' + data.statusText + ', Status: ' + data.status;
            }
          }
          if (errorMessage === undefined) {
            errorMessage = 'Response Text: ' + data.responseText + ', Status Text: ' + data.statusText + ', Status: ' + data.status;
          }

          this.logSegmentError(url, 'api error', errorMessage);

        }

    }.bind(this));

  },

  isPromise: function(value) {

    if (typeof value.then !== 'function') {
        return false;
    }
    var promiseThenSrc = String($.Deferred().then);
    var valueThenSrc = String(value.then);
    return promiseThenSrc === valueThenSrc;
  },

  resolveDeffereds: function(deffereds) {
    var responses = [], oldQue;
    $.when.apply($, deffereds).done(function() {
      // get all arguments returned from done
      var tranformedArgs = Array.prototype.slice.call(arguments);
      $.each(tranformedArgs, function(index, resp) {
        var respData = resp[0];
        if( !this.isPromise( respData ) && resp[1] === 'success' ) {
          responses.push(respData);
        }
        if (index === tranformedArgs.length - 1) {
          oldQue = window.optly_q;

          // consume qued data
          window.optly_q = window.optly.mrkt.Optly_Q(responses[0], responses[1]);
          window.optly_q.push(oldQue);
        }
      }.bind(this) );
    }.bind(this) );

    return true;
  },

  readCookie: function (name) {
    // Escape regexp special characters (thanks kangax!)
    name = name.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');

    var regex = new RegExp('(?:^|;)\\s?' + name + '=(.*?)(?:;|$)','i'),
        match = document.cookie.match(regex);

    return match && window.unescape(match[1]);
  },

  getLoginStatus: function(requestParams) {
    var deferreds;

    if ( !!this.readCookie('optimizely_signed_in') ) {
      deferreds = this.makeRequest(requestParams);
    } else {
      window.optly_q = window.optly.mrkt.Optly_Q();
    }

    return deferreds;
  }

};

(function() {
  'use strict';

  var acctParams,
    expParams;

  acctParams = {
    type: 'GET',
    url: '/account/info',
    properties: {
      email: 'string',
      account_id: 'number'
    }
  };

  expParams = {
    type: 'GET',
    url: '/experiment/load_recent?max_experiments=5',
    properties: {
      experiments: {
        id: 'number',
        description: 'string',
        has_started: 'boolean',
        can_edit: 'boolean'
      }
    }
  };

  return window.optly.mrkt.services.xhr.getLoginStatus([acctParams, expParams]);
}());
<|endoftext|>