<commit_before>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

import '@ciscospark/internal-plugin-encryption';
import '@ciscospark/internal-plugin-conversation';

import {registerInternalPlugin} from '@ciscospark/spark-core';
import {has} from 'lodash';

import EDiscovery from './ediscovery';
import InvalidEmailAddressError from './ediscovery-error';

registerInternalPlugin('ediscovery', EDiscovery, {
  payloadTransformer: {
    predicates: [
      {
        name: 'decryptReportRequest',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.reportRequest'));
        },
        extract(object) {
          return Promise.resolve(object.body.reportRequest);
        }
      },
      {
        name: 'decryptReportRequestArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].reportRequest'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'transformReportRequestEmailsToUUIDS',
        direction: 'outbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.emails'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'encryptReportRequest',
        direction: 'outbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.keywords'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContent',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.activityId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContentArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].activityId'));
        },
        extract(object) {
          return Promise.resolve(object);
        }
      },
      {
        name: 'decryptReportContentSummary',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.spaceId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContentSummaryArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].spaceId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      }
    ],
    transforms: [
      {
        name: 'decryptReportRequest',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object || !object.encryptionKeyUrl) {
            return Promise.resolve();
          }

          let keywordPromises = [];
          if (object.keywords) {
            keywordPromises = Promise.all(object.keywords.map((keyword) => ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, keyword)))
              .then((decryptedKeywords) => {
                object.keywords = decryptedKeywords;
              });
          }

          let emailPromises = [];
          if (object.emails) {
            emailPromises = Promise.all(object.emails.map((email) => ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, email)))
              .then((decryptedEmails) => {
                object.emails = decryptedEmails;
              });
          }

          return Promise.all([
            ctx.transform('decryptTextProp', 'name', object.encryptionKeyUrl, object),
            ctx.transform('decryptTextProp', 'description', object.encryptionKeyUrl, object)
          ].concat(keywordPromises, emailPromises));
        }
      },
      {
        name: 'decryptReportRequestArray',
        direction: 'inbound',
        fn(ctx, array) {
          if (!array) {
            return Promise.resolve();
          }

          return Promise.all(array.map((item) => ctx.transform('decryptReportRequest', item.reportRequest)));
        }
      },
      {
        name: 'transformReportRequestEmailsToUUIDS',
        direction: 'outbound',
        fn(ctx, object) {
          if (!object || object.emails.length <= 0) {
            return Promise.resolve();
          }

          const promises = [];
          const invalidEmails = [];
          object.emails.forEach((userEmail) => {
            promises.push(
              // The people list API only supports a single email
              ctx.spark.people.list({email: userEmail})
                .then((res) => {
                  let uuid = '';
                  if (res.items.length <= 0) {
                    ctx.spark.logger.error(`Unable to find user with email ${userEmail}`);
                    invalidEmails.push(userEmail);
                  }
                  else {
                    // User id is base64 encoded and of the format Y2lzY29zcGFyazovL3VzL1BFT1BMRS81ZDU5Yjc5NS02ZmEyLTQ2NTQtOGVjMi03NjlkYjE1YzBkOWU
                    const decodedId = Buffer.from(res.items[0].id, 'base64').toString();
                    // Decode and strip out the uuid ciscospark://us/PEOPLE/5d59b795-6fa2-4654-8ec2-769db15c0d9e
                    uuid = decodedId.substring(decodedId.lastIndexOf('/') + 1, decodedId.length);
                  }
                  return uuid;
                })
                .catch((reason) => Promise.reject(reason))
            );
          });
          return Promise.all(promises)
            .then((uuids) => {
              if (invalidEmails.length) {
                return Promise.reject(new InvalidEmailAddressError(invalidEmails));
              }
              object.userIds = uuids;
              return Promise.resolve();
            });
        }
      },
      {
        name: 'encryptReportRequest',
        direction: 'outbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          return ctx.spark.internal.encryption.kms.createUnboundKeys({count: 1})
            .then((keys) => {
              if (keys && keys.length > 0 && keys[0]) {
                object.encryptionKeyUrl = keys[0].uri;
                return ctx.spark.internal.encryption.kms.createResource({userIds: [keys[0].userId], keys})
                  .then(() => {
                    const keywordPromises = Promise.all(object.keywords.map((keyword) => ctx.spark.internal.encryption.encryptText(keys[0], keyword)))
                      .then((encryptedKeywords) => {
                        object.keywords = encryptedKeywords;
                      });

                    const emailPromises = Promise.all(object.emails.map((email) => ctx.spark.internal.encryption.encryptText(keys[0], email)))
                      .then((encryptedEmails) => {
                        object.emails = encryptedEmails;
                      });

                    return Promise.all([
                      ctx.transform('encryptTextProp', 'name', keys[0], object),
                      ctx.transform('encryptTextProp', 'description', keys[0], object)
                    ].concat(keywordPromises, emailPromises));
                  });
              }
              return Promise.resolve();
            })
            .catch((reason) => Promise.reject(reason));
        }
      },
      {
        name: 'decryptReportContent',
        direction: 'inbound',
        fn(ctx, object, reportId = '') {
          if (!object) {
            return Promise.resolve();
          }

          const promises = [];
          return ctx.spark.internal.ediscovery._getContentSummaryBySpaceId(reportId, object.targetId)
            .then((spaceSummary) => {
              const spaceParticipantId = spaceSummary.participants[0] || object.actorId;
              if (object.verb === 'post' && object.objectDisplayName && object.encryptionKeyUrl) {
                promises.push(ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.objectDisplayName, {onBehalfOf: spaceParticipantId})
                  .then((decryptedMessage) => {
                    object.objectDisplayName = decryptedMessage;
                  })
                  .catch((reason) => {
                    ctx.spark.logger.error(`Decrypt message error for activity ${object.activityId}: ${reason}`);
                    return object; // TODO - determine correct behaviour when an individual operation fails
                  }));
              }
              else if (object.verb === 'share' && object.files && object.files.length && object.encryptionKeyUrl) {
                for (let i = 0; i < object.files.length; i += 1) {
                  if (object.files[i] && object.files[i].displayName && object.files[i].scr) {
                    if (object.files[i].displayName !== '<Missing>') { // shared whiteboards are given this displayname by the ED service, if unchecked it causes a parsing error
                      promises.push(ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.files[i].displayName, {onBehalfOf: spaceParticipantId})
                        .then((decryptedFilename) => {
                          object.files[i].displayName = decryptedFilename;
                        })
                        .catch((reason) => {
                          ctx.spark.logger.error(`Decrypt filename error for activity ${object.activityId}: ${reason}`);
                          return object; // TODO - determine correct behaviour when an individual operation fails
                        }));
                    }
                    promises.push(ctx.spark.internal.encryption.decryptScr(object.encryptionKeyUrl, object.files[i].scr, {onBehalfOf: spaceParticipantId})
                      .then((decryptedScr) => {
                        object.files[i].scr = decryptedScr;
                      })
                      .catch((reason) => {
                        ctx.spark.logger.error(`Decrypt file scr error for activity ${object.activityId}: ${reason}`);
                        return object; // TODO - determine correct behaviour when an individual operation fails
                      }));
                  }
                }
              }
              else if (object.verb === 'share' && object.whiteboards && object.whiteboards.length && object.encryptionKeyUrl && reportId) {
                for (let i = 0; i < object.whiteboards.length; i += 1) {
                  if (object.whiteboards[i] && object.whiteboards[i].scr) {
                    promises.push(ctx.spark.internal.encryption.decryptScr(object.encryptionKeyUrl, object.whiteboards[i].scr, {onBehalfOf: spaceParticipantId})
                      .then((decryptedScr) => {
                        object.whiteboards[i].scr = decryptedScr;
                      })
                      .catch((reason) => {
                        ctx.spark.logger.error(`Decrypt whiteboard scr error for activity ${object.activityId}: ${reason}`);
                        return object; // TODO - determine correct behaviour when an individual operation fails
                      }));
                  }
                }
              }

              return Promise.all(promises);
            });
        }
      },
      {
        name: 'decryptReportContentArray',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          // the report id is needed in order to retrieve space information for activities with whiteboards so that
          // the list of users in those spaces can be accessed and the content can be decrypted on their behalf
          const reportId = object.options.resource.split('/')[1];
          return Promise.all(object.body.map((item) => ctx.transform('decryptReportContent', item, reportId)));
        }
      },
      {
        name: 'decryptReportContentSummary',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object || !object.spaceName || !object.participants || !object.participants.length) {
            return Promise.resolve();
          }
          return ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.spaceName, {onBehalfOf: object.participants[0]})
            .then((decryptedSpaceName) => {
              object.spaceName = decryptedSpaceName;
            })
            .catch((reason) => {
              ctx.spark.logger.error(`Decrypt space name error for space ${object.spaceId}: ${reason}`);
              return object; // TODO - determine correct behaviour when an individual operation fails
            });
        }
      },
      {
        name: 'decryptReportContentSummaryArray',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          return Promise.all(object.map((item) => ctx.transform('decryptReportContentSummary', item)));
        }
      }
    ]
  }
});

export default EDiscovery;

export {config} from './config';
export {default as ReportGenerator} from './report-generator';
export {default as ReportRequest} from './report-request';
export {default as EdiscoveryError, InvalidEmailAddressError} from './ediscovery-error';
<commit_msg>feat(edicovery): encrypting space names in report request<commit_after>/*!
 * Copyright (c) 2015-2017 Cisco Systems, Inc. See LICENSE file.
 */

import '@ciscospark/internal-plugin-encryption';
import '@ciscospark/internal-plugin-conversation';

import {registerInternalPlugin} from '@ciscospark/spark-core';
import {has} from 'lodash';

import EDiscovery from './ediscovery';
import InvalidEmailAddressError from './ediscovery-error';

registerInternalPlugin('ediscovery', EDiscovery, {
  payloadTransformer: {
    predicates: [
      {
        name: 'decryptReportRequest',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.reportRequest'));
        },
        extract(object) {
          return Promise.resolve(object.body.reportRequest);
        }
      },
      {
        name: 'decryptReportRequestArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].reportRequest'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'transformReportRequestEmailsToUUIDS',
        direction: 'outbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.emails'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'encryptReportRequest',
        direction: 'outbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.keywords'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContent',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.activityId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContentArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].activityId'));
        },
        extract(object) {
          return Promise.resolve(object);
        }
      },
      {
        name: 'decryptReportContentSummary',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body.spaceId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      },
      {
        name: 'decryptReportContentSummaryArray',
        direction: 'inbound',
        test(ctx, object) {
          return Promise.resolve(has(object, 'body[0].spaceId'));
        },
        extract(object) {
          return Promise.resolve(object.body);
        }
      }
    ],
    transforms: [
      {
        name: 'decryptReportRequest',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object || !object.encryptionKeyUrl) {
            return Promise.resolve();
          }

          let keywordPromises = [];
          if (object.keywords) {
            keywordPromises = Promise.all(object.keywords.map((keyword) => ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, keyword)))
              .then((decryptedKeywords) => {
                object.keywords = decryptedKeywords;
              });
          }

          let emailPromises = [];
          if (object.emails) {
            emailPromises = Promise.all(object.emails.map((email) => ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, email)))
              .then((decryptedEmails) => {
                object.emails = decryptedEmails;
              });
          }

          return Promise.all([
            ctx.transform('decryptTextProp', 'name', object.encryptionKeyUrl, object),
            ctx.transform('decryptTextProp', 'description', object.encryptionKeyUrl, object)
          ].concat(keywordPromises, emailPromises));
        }
      },
      {
        name: 'decryptReportRequestArray',
        direction: 'inbound',
        fn(ctx, array) {
          if (!array) {
            return Promise.resolve();
          }

          return Promise.all(array.map((item) => ctx.transform('decryptReportRequest', item.reportRequest)));
        }
      },
      {
        name: 'transformReportRequestEmailsToUUIDS',
        direction: 'outbound',
        fn(ctx, object) {
          if (!object || object.emails.length <= 0) {
            return Promise.resolve();
          }

          const promises = [];
          const invalidEmails = [];
          object.emails.forEach((userEmail) => {
            promises.push(
              // The people list API only supports a single email
              ctx.spark.people.list({email: userEmail})
                .then((res) => {
                  let uuid = '';
                  if (res.items.length <= 0) {
                    ctx.spark.logger.error(`Unable to find user with email ${userEmail}`);
                    invalidEmails.push(userEmail);
                  }
                  else {
                    // User id is base64 encoded and of the format Y2lzY29zcGFyazovL3VzL1BFT1BMRS81ZDU5Yjc5NS02ZmEyLTQ2NTQtOGVjMi03NjlkYjE1YzBkOWU
                    const decodedId = Buffer.from(res.items[0].id, 'base64').toString();
                    // Decode and strip out the uuid ciscospark://us/PEOPLE/5d59b795-6fa2-4654-8ec2-769db15c0d9e
                    uuid = decodedId.substring(decodedId.lastIndexOf('/') + 1, decodedId.length);
                  }
                  return uuid;
                })
                .catch((reason) => Promise.reject(reason))
            );
          });
          return Promise.all(promises)
            .then((uuids) => {
              if (invalidEmails.length) {
                return Promise.reject(new InvalidEmailAddressError(invalidEmails));
              }
              object.userIds = uuids;
              return Promise.resolve();
            });
        }
      },
      {
        name: 'encryptReportRequest',
        direction: 'outbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          return ctx.spark.internal.encryption.kms.createUnboundKeys({count: 1})
            .then((keys) => {
              if (keys && keys.length > 0 && keys[0]) {
                object.encryptionKeyUrl = keys[0].uri;
                return ctx.spark.internal.encryption.kms.createResource({userIds: [keys[0].userId], keys})
                  .then(() => {
                    const spaceNamePromises = Promise.all(object.spaceNames.map((spaceName) => ctx.spark.internal.encryption.encryptText(keys[0], spaceName)))
                      .then((encryptedSpaceNames) => {
                        object.spaceNames = encryptedSpaceNames;
                      });

                    const keywordPromises = Promise.all(object.keywords.map((keyword) => ctx.spark.internal.encryption.encryptText(keys[0], keyword)))
                      .then((encryptedKeywords) => {
                        object.keywords = encryptedKeywords;
                      });

                    const emailPromises = Promise.all(object.emails.map((email) => ctx.spark.internal.encryption.encryptText(keys[0], email)))
                      .then((encryptedEmails) => {
                        object.emails = encryptedEmails;
                      });

                    return Promise.all([
                      ctx.transform('encryptTextProp', 'name', keys[0], object),
                      ctx.transform('encryptTextProp', 'description', keys[0], object)
                    ].concat(spaceNamePromises, keywordPromises, emailPromises));
                  });
              }
              return Promise.resolve();
            })
            .catch((reason) => Promise.reject(reason));
        }
      },
      {
        name: 'decryptReportContent',
        direction: 'inbound',
        fn(ctx, object, reportId = '') {
          if (!object) {
            return Promise.resolve();
          }

          const promises = [];
          return ctx.spark.internal.ediscovery._getContentSummaryBySpaceId(reportId, object.targetId)
            .then((spaceSummary) => {
              const spaceParticipantId = spaceSummary.participants[0] || object.actorId;
              if (object.verb === 'post' && object.objectDisplayName && object.encryptionKeyUrl) {
                promises.push(ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.objectDisplayName, {onBehalfOf: spaceParticipantId})
                  .then((decryptedMessage) => {
                    object.objectDisplayName = decryptedMessage;
                  })
                  .catch((reason) => {
                    ctx.spark.logger.error(`Decrypt message error for activity ${object.activityId}: ${reason}`);
                    return object; // TODO - determine correct behaviour when an individual operation fails
                  }));
              }
              else if (object.verb === 'share' && object.files && object.files.length && object.encryptionKeyUrl) {
                for (let i = 0; i < object.files.length; i += 1) {
                  if (object.files[i] && object.files[i].displayName && object.files[i].scr) {
                    if (object.files[i].displayName !== '<Missing>') { // shared whiteboards are given this displayname by the ED service, if unchecked it causes a parsing error
                      promises.push(ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.files[i].displayName, {onBehalfOf: spaceParticipantId})
                        .then((decryptedFilename) => {
                          object.files[i].displayName = decryptedFilename;
                        })
                        .catch((reason) => {
                          ctx.spark.logger.error(`Decrypt filename error for activity ${object.activityId}: ${reason}`);
                          return object; // TODO - determine correct behaviour when an individual operation fails
                        }));
                    }
                    promises.push(ctx.spark.internal.encryption.decryptScr(object.encryptionKeyUrl, object.files[i].scr, {onBehalfOf: spaceParticipantId})
                      .then((decryptedScr) => {
                        object.files[i].scr = decryptedScr;
                      })
                      .catch((reason) => {
                        ctx.spark.logger.error(`Decrypt file scr error for activity ${object.activityId}: ${reason}`);
                        return object; // TODO - determine correct behaviour when an individual operation fails
                      }));
                  }
                }
              }
              else if (object.verb === 'share' && object.whiteboards && object.whiteboards.length && object.encryptionKeyUrl && reportId) {
                for (let i = 0; i < object.whiteboards.length; i += 1) {
                  if (object.whiteboards[i] && object.whiteboards[i].scr) {
                    promises.push(ctx.spark.internal.encryption.decryptScr(object.encryptionKeyUrl, object.whiteboards[i].scr, {onBehalfOf: spaceParticipantId})
                      .then((decryptedScr) => {
                        object.whiteboards[i].scr = decryptedScr;
                      })
                      .catch((reason) => {
                        ctx.spark.logger.error(`Decrypt whiteboard scr error for activity ${object.activityId}: ${reason}`);
                        return object; // TODO - determine correct behaviour when an individual operation fails
                      }));
                  }
                }
              }

              return Promise.all(promises);
            });
        }
      },
      {
        name: 'decryptReportContentArray',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          // the report id is needed in order to retrieve space information for activities with whiteboards so that
          // the list of users in those spaces can be accessed and the content can be decrypted on their behalf
          const reportId = object.options.resource.split('/')[1];
          return Promise.all(object.body.map((item) => ctx.transform('decryptReportContent', item, reportId)));
        }
      },
      {
        name: 'decryptReportContentSummary',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object || !object.spaceName || !object.participants || !object.participants.length) {
            return Promise.resolve();
          }
          return ctx.spark.internal.encryption.decryptText(object.encryptionKeyUrl, object.spaceName, {onBehalfOf: object.participants[0]})
            .then((decryptedSpaceName) => {
              object.spaceName = decryptedSpaceName;
            })
            .catch((reason) => {
              ctx.spark.logger.error(`Decrypt space name error for space ${object.spaceId}: ${reason}`);
              return object; // TODO - determine correct behaviour when an individual operation fails
            });
        }
      },
      {
        name: 'decryptReportContentSummaryArray',
        direction: 'inbound',
        fn(ctx, object) {
          if (!object) {
            return Promise.resolve();
          }
          return Promise.all(object.map((item) => ctx.transform('decryptReportContentSummary', item)));
        }
      }
    ]
  }
});

export default EDiscovery;

export {config} from './config';
export {default as ReportGenerator} from './report-generator';
export {default as ReportRequest} from './report-request';
export {default as EdiscoveryError, InvalidEmailAddressError} from './ediscovery-error';
<|endoftext|>


<commit_before>define(["backbone","jquery","handlebar"], function(){

  Handlebars.registerHelper('toUpper', function(options) {
    return options.toUpperCase();
});

  var Controller = Backbone.View.extend({

    template: Handlebars.compile("<button class='{{feature}}'>{{toUpper feature}}</button>"),

    initialize: function() {
      this.model.on('inactivate', this.inactivate, this);
    },

    events: {
      'click button': "renderTextTag"
    },

    inactivate: function(eventName) {
      if (!this.$("button").hasClass('inactive')) {
        this.$("button").addClass('inactive');
      }
    },

    renderTextTag: function(e) {
      this.model.collection.trigger('showTextTags', this.model);
      if (this.$("button").hasClass('inactive')) {
        this.$("button").removeClass('inactive');
      }
    },

    render: function () {
      this.$el.html(this.template(this.model.attributes));
      //to fix. position
      return this;
    }

  });

  return Controller;
});<commit_msg>copy singlePage branch code over<commit_after>define(["backbone","jquery","handlebar"], function(){

  Handlebars.registerHelper('toUpper', function(options) {
    return options.toUpperCase();
});

  var Controller = Backbone.View.extend({

    template: Handlebars.compile("<button class='{{feature}}'>{{toUpper feature}}</button>"),

    initialize: function() {
      this.model.on('inactivate', this.inactivate, this);
    },

    events: {
      'click button': "renderTextTag"
    },

    inactivate: function(eventName) {
      if (this.$("button").hasClass('active')) {
        this.$("button").removeClass('active');
      }
    },

    renderTextTag: function(e) {
      this.model.collection.trigger('showTextTags', this.model);
      if (!this.$("button").hasClass('active')) {
        this.$("button").addClass('active');
      }
    },

    render: function () {
      this.$el.html(this.template(this.model.attributes));
      //to fix. position
      return this;
    }

  });

  return Controller;
});<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import {Avatar, Button, SpaceListItem} from '@collab-ui/react';
import Timer from '@ciscospark/react-component-timer';
import {getTeamColor} from '@ciscospark/react-component-utils';

import './styles.scss';

const propTypes = {
  avatarUrl: PropTypes.string,
  callStartTime: PropTypes.number,
  hasCalling: PropTypes.bool,
  hasJoinedOnThisDevice: PropTypes.bool,
  id: PropTypes.string,
  isDecrypting: PropTypes.bool,
  isUnread: PropTypes.bool,
  name: PropTypes.string,
  onCallClick: PropTypes.func,
  onClick: PropTypes.func,
  teamColor: PropTypes.string,
  teamName: PropTypes.string,
  type: PropTypes.string
};

const defaultProps = {
  avatarUrl: '',
  callStartTime: undefined,
  hasCalling: false,
  hasJoinedOnThisDevice: undefined,
  id: '',
  isDecrypting: false,
  isUnread: false,
  name: '',
  onCallClick: () => {},
  onClick: () => {},
  teamColor: '',
  teamName: '',
  type: ''
};

function SpaceItem({
  avatarUrl,
  callStartTime,
  hasCalling,
  hasJoinedOnThisDevice,
  id,
  isUnread,
  name,
  onClick,
  onCallClick,
  teamName,
  teamColor,
  type,
  isDecrypting
}) {
  function handleClick() {
    return onClick(id);
  }

  function handleCallClick(e) {
    if (type === 'direct' || hasCalling) {
      e.stopPropagation();
      return onCallClick(id);
    }
    return false;
  }

  // Show hover call and join in progress buttons
  const hasCallSupport = hasCalling && typeof onCallClick === 'function';

  const avatarElement = (
    <Avatar
      backgroundColor={teamColor ? getTeamColor(teamColor, false) : '#E0E0E0'}
      isDecrypting={isDecrypting}
      src={avatarUrl}
      title={name}
      type={type === 'group' ? 'group' : ''}
    />
  );
  const subheaderElement = (

{teamName}

  );
  const joinButton = (
    hasCallSupport && callStartTime &&
    <Button
      ariaLabel="Join Call"
      callStartTime={callStartTime}
      color="green"
      size={28}
      onClick={handleCallClick}
    >
      {
      hasJoinedOnThisDevice
      ? <Timer startTime={callStartTime} />
        :
Now

      }
    </Button>

  );

  return (
    <SpaceListItem
      childrenLeft={avatarElement}
      childrenRight={joinButton}
      header={name}
      isBold={!isDecrypting && isUnread}
      isDecrypting={isDecrypting}
      isUnread={!isDecrypting && isUnread}
      subheader={subheaderElement}
      onClick={handleClick}
    />
  );
}

SpaceItem.propTypes = propTypes;
SpaceItem.defaultProps = defaultProps;

export default SpaceItem;
<commit_msg>feat(react-component-space-item): Add notification level icon support (SPARK-38152)<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import {Avatar, Button, SpaceListItem} from '@collab-ui/react';
import Timer from '@ciscospark/react-component-timer';
import {
  getTeamColor,
  NOTIFICATIONS_BADGE_MENTION,
  NOTIFICATIONS_BADGE_MUTE,
  NOTIFICATIONS_BADGE_NONE,
  NOTIFICATIONS_BADGE_UNMUTE,
  NOTIFICATIONS_BADGE_UNREAD
} from '@ciscospark/react-component-utils';

import './styles.scss';

const propTypes = {
  avatarUrl: PropTypes.string,
  badge: PropTypes.string,
  callStartTime: PropTypes.number,
  hasCalling: PropTypes.bool,
  hasJoinedOnThisDevice: PropTypes.bool,
  id: PropTypes.string,
  isDecrypting: PropTypes.bool,
  isUnread: PropTypes.bool,
  name: PropTypes.string,
  onCallClick: PropTypes.func,
  onClick: PropTypes.func,
  teamColor: PropTypes.string,
  teamName: PropTypes.string,
  type: PropTypes.string
};

const defaultProps = {
  avatarUrl: '',
  badge: NOTIFICATIONS_BADGE_NONE,
  callStartTime: undefined,
  hasCalling: false,
  hasJoinedOnThisDevice: undefined,
  id: '',
  isDecrypting: false,
  isUnread: false,
  name: '',
  onCallClick: () => {},
  onClick: () => {},
  teamColor: '',
  teamName: '',
  type: ''
};

function SpaceItem({
  avatarUrl,
  badge,
  callStartTime,
  hasCalling,
  hasJoinedOnThisDevice,
  id,
  isUnread,
  name,
  onClick,
  onCallClick,
  teamName,
  teamColor,
  type,
  isDecrypting
}) {
  function handleClick() {
    return onClick(id);
  }

  function handleCallClick(e) {
    if (type === 'direct' || hasCalling) {
      e.stopPropagation();
      return onCallClick(id);
    }
    return false;
  }

  // Show hover call and join in progress buttons
  const hasCallSupport = hasCalling && typeof onCallClick === 'function';

  const avatarElement = (
    <Avatar
      backgroundColor={teamColor ? getTeamColor(teamColor, false) : '#E0E0E0'}
      isDecrypting={isDecrypting}
      src={avatarUrl}
      title={name}
      type={type === 'group' ? 'group' : ''}
    />
  );
  const subheaderElement = (

{teamName}

  );
  const joinButton = (
    hasCallSupport && callStartTime &&
    <Button
      ariaLabel="Join Call"
      callStartTime={callStartTime}
      color="green"
      size={28}
      onClick={handleCallClick}
    >
      {
      hasJoinedOnThisDevice
      ? <Timer startTime={callStartTime} />
        :
Now

      }
    </Button>

  );

  return (
    <SpaceListItem
      childrenLeft={avatarElement}
      childrenRight={joinButton}
      header={name}
      isBold={!isDecrypting && isUnread}
      isAlertOn={badge === NOTIFICATIONS_BADGE_UNMUTE}
      isUnread={badge === NOTIFICATIONS_BADGE_UNREAD}
      isMentioned={badge === NOTIFICATIONS_BADGE_MENTION}
      isMuted={badge === NOTIFICATIONS_BADGE_MUTE}
      isDecrypting={isDecrypting}
      subheader={subheaderElement}
      onClick={handleClick}
    />
  );
}

SpaceItem.propTypes = propTypes;
SpaceItem.defaultProps = defaultProps;

export default SpaceItem;
<|endoftext|>


<commit_before>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classnames from 'classnames';
import { settings } from 'carbon-components';
import {
  CheckmarkOutline16,
  Warning16,
  RadioButton16,
  CircleFilled16,
} from '@carbon/icons-react';
import { keys, matches } from '../../internal/keyboard';

const { prefix } = settings;

const defaultRenderLabel = (props) =>
;

const defaultTranslations = {
  'carbon.progress-step.complete': 'Complete',
  'carbon.progress-step.incomplete': 'Incomplete',
  'carbon.progress-step.current': 'Current',
  'carbon.progress-step.invalid': 'Invalid',
};

function translateWithId(messageId) {
  return defaultTranslations[messageId];
}

export function ProgressStep({
  label,
  description,
  className,
  current,
  complete,
  invalid,
  secondaryLabel,
  disabled,
  onClick,
  renderLabel: ProgressStepLabel,
  translateWithId: t,
  ...rest
}) {
  const classes = classnames({
    [`${prefix}--progress-step`]: true,
    [`${prefix}--progress-step--current`]: current,
    [`${prefix}--progress-step--complete`]: complete,
    [`${prefix}--progress-step--incomplete`]: !complete && !current,
    [`${prefix}--progress-step--disabled`]: disabled,
    [className]: className,
  });

  const handleKeyDown = (e) => {
    if (matches(e, [keys.Enter, keys.Space])) {
      onClick();
    }
  };

  // eslint-disable-next-line react/prop-types
  const SVGIcon = ({ complete, current, description, invalid, prefix }) => {
    if (invalid) {
      return <Warning16 className={`${prefix}--progress__warning`} />;
    }
    if (current) {
      return (
        <CircleFilled16>
          <title>{description}</title>
        </CircleFilled16>
      );
    }
    if (complete) {
      return (
        <CheckmarkOutline16>
          <title>{description}</title>
        </CheckmarkOutline16>
      );
    }
    return (
      <RadioButton16>
        <title>{description}</title>
      </RadioButton16>
    );
  };

  let message = t('carbon.progress-step.incomplete');

  if (current) {
    message = t('carbon.progress-step.current');
  }

  if (complete) {
    message = t('carbon.progress-step.complete');
  }

  if (invalid) {
    message = t('carbon.progress-step.invalid');
  }

  return (

      <button
        type="button"
        className={classnames(`${prefix}--progress-step-button`, {
          [`${prefix}--progress-step-button--unclickable`]: !onClick || current,
        })}
        disabled={disabled}
        aria-disabled={disabled}
        tabIndex={!current && onClick && !disabled ? 0 : -1}
        onClick={!current ? onClick : undefined}
        onKeyDown={handleKeyDown}
        title={label}
        {...rest}>
        <span className={`${prefix}--assistive-text`}>{message}
        <SVGIcon
          complete={complete}
          current={current}
          description={description}
          invalid={invalid}
          prefix={prefix}
        />
        <ProgressStepLabel className={`${prefix}--progress-label`}>
          {label}
        </ProgressStepLabel>
        {secondaryLabel !== null && secondaryLabel !== undefined ? (
          <p className={`${prefix}--progress-optional`}>{secondaryLabel}

        ) : null}
        <span className={`${prefix}--progress-line`} />
      </button>

  );
}

ProgressStep.propTypes = {
  /**
   * Provide an optional className to be applied to the containing `<li>` node
   */
  className: PropTypes.string,

  /**
   * Specify whether the step has been completed
   */
  complete: PropTypes.bool,

  /**
   * Specify whether the step is the current step
   */
  current: PropTypes.bool,

  /**
   * Provide a description for the <ProgressStep>
   */
  description: PropTypes.string,

  /**
   * Specify whether the step is disabled
   */
  disabled: PropTypes.bool,

  /**
   * Index of the current step within the ProgressIndicator
   */
  index: PropTypes.number,

  /**
   * Specify whether the step is invalid
   */
  invalid: PropTypes.bool,

  /**
   * Provide the label for the <ProgressStep>
   */
  label: PropTypes.node.isRequired,

  /**
   * A callback called if the step is clicked or the enter key is pressed
   */
  onClick: PropTypes.func,

  /**
   * Provide the props that describe a progress step tooltip
   */
  overflowTooltipProps: PropTypes.object,

  /*
   * An optional parameter to allow for overflow content to be rendered in a
   * tooltip.
   */
  renderLabel: PropTypes.func,

  /**
   * Provide an optional secondary label
   */
  secondaryLabel: PropTypes.string,

  /**
   * The ID of the tooltip content.
   */
  tooltipId: PropTypes.string,

  /**
   * Optional method that takes in a message id and returns an
   * internationalized string.
   */
  translateWithId: PropTypes.func,
};

ProgressStep.defaultProps = {
  renderLabel: defaultRenderLabel,
  translateWithId,
};

export class ProgressIndicator extends Component {
  state = {};

  static propTypes = {
    /**
     * Provide <ProgressStep> components to be rendered in the
     * <ProgressIndicator>
     */
    children: PropTypes.node,

    /**
     * Provide an optional className to be applied to the containing node
     */
    className: PropTypes.string,

    /**
     * Optionally specify the current step array index
     */
    currentIndex: PropTypes.number,

    /**
     * Optional callback called if a ProgressStep is clicked on.  Returns the index of the step.
     */
    onChange: PropTypes.func,

    /**
     * Specify whether the progress steps should be split equally in size in the div
     */
    spaceEqually: PropTypes.bool,
    /**
     * Determines whether or not the ProgressIndicator should be rendered vertically.
     */
    vertical: PropTypes.bool,
  };

  static defaultProps = {
    currentIndex: 0,
  };

  static getDerivedStateFromProps({ currentIndex }, state) {
    const { prevCurrentIndex } = state;
    return prevCurrentIndex === currentIndex
      ? null
      : {
          currentIndex,
          prevCurrentIndex: currentIndex,
        };
  }

  renderSteps = () => {
    const { onChange } = this.props;

    return React.Children.map(this.props.children, (child, index) => {
      // only setup click handlers if onChange event is passed
      const onClick = onChange ? () => onChange(index) : undefined;
      if (index === this.state.currentIndex) {
        return React.cloneElement(child, {
          current: true,
          index,
          onClick,
        });
      }
      if (index < this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: true,
          index,
          onClick,
        });
      }
      if (index > this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: false,
          index,
          onClick,
        });
      }
      return null;
    });
  };

  render() {
    const {
      className,
      currentIndex, // eslint-disable-line no-unused-vars
      vertical,
      spaceEqually,
      ...other
    } = this.props;
    const classes = classnames({
      [`${prefix}--progress`]: true,
      [`${prefix}--progress--vertical`]: vertical,
      [`${prefix}--progress--space-equal`]: spaceEqually && !vertical,
      [className]: className,
    });
    return (

        {this.renderSteps()}

    );
  }
}
<commit_msg>fix(progress-indicator): don't overwrite explicit "complete" on step (#7345)<commit_after>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classnames from 'classnames';
import { settings } from 'carbon-components';
import {
  CheckmarkOutline16,
  Warning16,
  RadioButton16,
  CircleFilled16,
} from '@carbon/icons-react';
import { keys, matches } from '../../internal/keyboard';

const { prefix } = settings;

const defaultRenderLabel = (props) =>
;

const defaultTranslations = {
  'carbon.progress-step.complete': 'Complete',
  'carbon.progress-step.incomplete': 'Incomplete',
  'carbon.progress-step.current': 'Current',
  'carbon.progress-step.invalid': 'Invalid',
};

function translateWithId(messageId) {
  return defaultTranslations[messageId];
}

export function ProgressStep({
  label,
  description,
  className,
  current,
  complete,
  invalid,
  secondaryLabel,
  disabled,
  onClick,
  renderLabel: ProgressStepLabel,
  translateWithId: t,
  ...rest
}) {
  const classes = classnames({
    [`${prefix}--progress-step`]: true,
    [`${prefix}--progress-step--current`]: current,
    [`${prefix}--progress-step--complete`]: complete,
    [`${prefix}--progress-step--incomplete`]: !complete && !current,
    [`${prefix}--progress-step--disabled`]: disabled,
    [className]: className,
  });

  const handleKeyDown = (e) => {
    if (matches(e, [keys.Enter, keys.Space])) {
      onClick();
    }
  };

  // eslint-disable-next-line react/prop-types
  const SVGIcon = ({ complete, current, description, invalid, prefix }) => {
    if (invalid) {
      return <Warning16 className={`${prefix}--progress__warning`} />;
    }
    if (current) {
      return (
        <CircleFilled16>
          <title>{description}</title>
        </CircleFilled16>
      );
    }
    if (complete) {
      return (
        <CheckmarkOutline16>
          <title>{description}</title>
        </CheckmarkOutline16>
      );
    }
    return (
      <RadioButton16>
        <title>{description}</title>
      </RadioButton16>
    );
  };

  let message = t('carbon.progress-step.incomplete');

  if (current) {
    message = t('carbon.progress-step.current');
  }

  if (complete) {
    message = t('carbon.progress-step.complete');
  }

  if (invalid) {
    message = t('carbon.progress-step.invalid');
  }

  return (

      <button
        type="button"
        className={classnames(`${prefix}--progress-step-button`, {
          [`${prefix}--progress-step-button--unclickable`]: !onClick || current,
        })}
        disabled={disabled}
        aria-disabled={disabled}
        tabIndex={!current && onClick && !disabled ? 0 : -1}
        onClick={!current ? onClick : undefined}
        onKeyDown={handleKeyDown}
        title={label}
        {...rest}>
        <span className={`${prefix}--assistive-text`}>{message}
        <SVGIcon
          complete={complete}
          current={current}
          description={description}
          invalid={invalid}
          prefix={prefix}
        />
        <ProgressStepLabel className={`${prefix}--progress-label`}>
          {label}
        </ProgressStepLabel>
        {secondaryLabel !== null && secondaryLabel !== undefined ? (
          <p className={`${prefix}--progress-optional`}>{secondaryLabel}

        ) : null}
        <span className={`${prefix}--progress-line`} />
      </button>

  );
}

ProgressStep.propTypes = {
  /**
   * Provide an optional className to be applied to the containing `<li>` node
   */
  className: PropTypes.string,

  /**
   * Specify whether the step has been completed
   */
  complete: PropTypes.bool,

  /**
   * Specify whether the step is the current step
   */
  current: PropTypes.bool,

  /**
   * Provide a description for the <ProgressStep>
   */
  description: PropTypes.string,

  /**
   * Specify whether the step is disabled
   */
  disabled: PropTypes.bool,

  /**
   * Index of the current step within the ProgressIndicator
   */
  index: PropTypes.number,

  /**
   * Specify whether the step is invalid
   */
  invalid: PropTypes.bool,

  /**
   * Provide the label for the <ProgressStep>
   */
  label: PropTypes.node.isRequired,

  /**
   * A callback called if the step is clicked or the enter key is pressed
   */
  onClick: PropTypes.func,

  /**
   * Provide the props that describe a progress step tooltip
   */
  overflowTooltipProps: PropTypes.object,

  /*
   * An optional parameter to allow for overflow content to be rendered in a
   * tooltip.
   */
  renderLabel: PropTypes.func,

  /**
   * Provide an optional secondary label
   */
  secondaryLabel: PropTypes.string,

  /**
   * The ID of the tooltip content.
   */
  tooltipId: PropTypes.string,

  /**
   * Optional method that takes in a message id and returns an
   * internationalized string.
   */
  translateWithId: PropTypes.func,
};

ProgressStep.defaultProps = {
  renderLabel: defaultRenderLabel,
  translateWithId,
};

export class ProgressIndicator extends Component {
  state = {};

  static propTypes = {
    /**
     * Provide <ProgressStep> components to be rendered in the
     * <ProgressIndicator>
     */
    children: PropTypes.node,

    /**
     * Provide an optional className to be applied to the containing node
     */
    className: PropTypes.string,

    /**
     * Optionally specify the current step array index
     */
    currentIndex: PropTypes.number,

    /**
     * Optional callback called if a ProgressStep is clicked on.  Returns the index of the step.
     */
    onChange: PropTypes.func,

    /**
     * Specify whether the progress steps should be split equally in size in the div
     */
    spaceEqually: PropTypes.bool,
    /**
     * Determines whether or not the ProgressIndicator should be rendered vertically.
     */
    vertical: PropTypes.bool,
  };

  static defaultProps = {
    currentIndex: 0,
  };

  static getDerivedStateFromProps({ currentIndex }, state) {
    const { prevCurrentIndex } = state;
    return prevCurrentIndex === currentIndex
      ? null
      : {
          currentIndex,
          prevCurrentIndex: currentIndex,
        };
  }

  renderSteps = () => {
    const { onChange } = this.props;

    return React.Children.map(this.props.children, (child, index) => {
      // only setup click handlers if onChange event is passed
      const onClick = onChange ? () => onChange(index) : undefined;
      if (index === this.state.currentIndex) {
        return React.cloneElement(child, {
          current: true,
          index,
          onClick,
        });
      }
      if (index < this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: true,
          index,
          onClick,
        });
      }
      if (index > this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: child.props.complete || false,
          index,
          onClick,
        });
      }
      return null;
    });
  };

  render() {
    const {
      className,
      currentIndex, // eslint-disable-line no-unused-vars
      vertical,
      spaceEqually,
      ...other
    } = this.props;
    const classes = classnames({
      [`${prefix}--progress`]: true,
      [`${prefix}--progress--vertical`]: vertical,
      [`${prefix}--progress--space-equal`]: spaceEqually && !vertical,
      [className]: className,
    });
    return (

        {this.renderSteps()}

    );
  }
}
<|endoftext|>


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

/**
 * @fileoverview Implements the disposable interface. The dispose method is used
 * to clean up references and resources.
 */

goog.provide('goog.Disposable');
goog.provide('goog.dispose');
goog.provide('goog.disposeAll');

goog.require('goog.disposable.IDisposable');

/**
 * @define {number} The monitoring mode of the goog.Disposable
 *     instances. Default is OFF. Switching on the monitoring is only
 *     recommended for debugging because it has a significant impact on
 *     performance and memory usage. If switched off, the monitoring code
 *     compiles down to 0 bytes.
 */
goog.Disposable.MONITORING_MODE =
    goog.define('goog.Disposable.MONITORING_MODE', 0);

/**
 * @define {boolean} Whether to attach creation stack to each created disposable
 *     instance; This is only relevant for when MonitoringMode != OFF.
 */
goog.Disposable.INCLUDE_STACK_ON_CREATION =
    goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);

/**
 * Class that provides the basic implementation for disposable objects. If your
 * class holds one or more references to COM objects, DOM nodes, or other
 * disposable objects, it should extend this class or implement the disposable
 * interface (defined in goog.disposable.IDisposable).
 * @constructor
 * @implements {goog.disposable.IDisposable}
 */
goog.Disposable = function() {
  /**
   * If monitoring the goog.Disposable instances is enabled, stores the creation
   * stack trace of the Disposable instance.
   * @type {string|undefined}
   */
  this.creationStack;

  if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
    if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
      this.creationStack = new Error().stack;
    }
    goog.Disposable.instances_[goog.getUid(this)] = this;
  }
  // Support sealing
  this.disposed_ = this.disposed_;
  this.onDisposeCallbacks_ = this.onDisposeCallbacks_;
  this.registeredDisposables_ = this.registeredDisposables_;
};

/**
 * @enum {number} Different monitoring modes for Disposable.
 */
goog.Disposable.MonitoringMode = {
  /**
   * No monitoring.
   */
  OFF: 0,
  /**
   * Creating and disposing the goog.Disposable instances is monitored. All
   * disposable objects need to call the `goog.Disposable` base
   * constructor. The PERMANENT mode must be switched on before creating any
   * goog.Disposable instances.
   */
  PERMANENT: 1,
  /**
   * INTERACTIVE mode can be switched on and off on the fly without producing
   * errors. It also doesn't warn if the disposable objects don't call the
   * `goog.Disposable` base constructor.
   */
  INTERACTIVE: 2
};

/**
 * Maps the unique ID of every undisposed `goog.Disposable` object to
 * the object itself.
 * @type {!Object<number, !goog.Disposable>}
 * @private
 */
goog.Disposable.instances_ = {};

/**
 * @return {!Array<!goog.Disposable>} All `goog.Disposable` objects that
 *     haven't been disposed of.
 */
goog.Disposable.getUndisposedObjects = function() {
  var ret = [];
  for (var id in goog.Disposable.instances_) {
    if (goog.Disposable.instances_.hasOwnProperty(id)) {
      ret.push(goog.Disposable.instances_[Number(id)]);
    }
  }
  return ret;
};

/**
 * Clears the registry of undisposed objects but doesn't dispose of them.
 */
goog.Disposable.clearUndisposedObjects = function() {
  goog.Disposable.instances_ = {};
};

/**
 * Whether the object has been disposed of.
 * @type {boolean}
 * @private
 */
goog.Disposable.prototype.disposed_ = false;

/**
 * Callbacks to invoke when this object is disposed.
 * @type {Array<!Function>}
 * @private
 */
goog.Disposable.prototype.onDisposeCallbacks_;

/**
 * Other Disposables to dispose when this object is disposed.
 * @type {Array<goog.disposable.IDisposable>}
 * @private
 */
goog.Disposable.prototype.registeredDisposables_;

/**
 * @return {boolean} Whether the object has been disposed of.
 * @override
 */
goog.Disposable.prototype.isDisposed = function() {
  return this.disposed_;
};

/**
 * @return {boolean} Whether the object has been disposed of.
 * @deprecated Use {@link #isDisposed} instead.
 */
goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;

/**
 * Added to disposed objects. This allows you to do a heap dump and search for
 * all disposed objects (by finding their DisposedMarker instances) and track
 * down the retaining paths, to clean up memory leaks.
 * @constructor
 */
goog.DisposedMarker = function() {};

/**
 * Disposes of the object. If the object hasn't already been disposed of, calls
 * {@link #disposeInternal}. Classes that extend `goog.Disposable` should
 * override {@link #disposeInternal} in order to delete references to COM
 * objects, DOM nodes, and other disposable objects. Reentrant.
 *
 * @return {void} Nothing.
 * @override
 */
goog.Disposable.prototype.dispose = function() {
  if (!this.disposed_) {
    // Set disposed_ to true first, in case during the chain of disposal this
    // gets disposed recursively.
    this.disposed_ = true;
    this.disposeInternal();
    if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
      var uid = goog.getUid(this);
      if (goog.Disposable.MONITORING_MODE ==
              goog.Disposable.MonitoringMode.PERMANENT &&
          !goog.Disposable.instances_.hasOwnProperty(uid)) {
        throw new Error(
            this + ' did not call the goog.Disposable base ' +
            'constructor or was disposed of after a clearUndisposedObjects ' +
            'call');
      }
      if (goog.Disposable.MONITORING_MODE !=
              goog.Disposable.MonitoringMode.OFF &&
          this.onDisposeCallbacks_ && this.onDisposeCallbacks_.length > 0) {
        throw new Error(
            this + ' did not empty its onDisposeCallbacks queue. This ' +
            'probably means it overrode dispose() or disposeInternal() ' +
            'without calling the superclass\' method.');
      }
      delete goog.Disposable.instances_[uid];
    }

    // In debug mode, disallow all access to disposed object members.
    // Never allow this to happen in compiled mode, even for unit tests,
    // because the compiler can sometimes reorder statements in a way that
    // causes this to fail.
    if (goog.DEBUG && !COMPILED) {
      // The only thing a disposed object can do is return true from
      // isDisposed().
      let whitelist = {
        isDisposed: 1,
        disposed_: 1,
      };

      // Keep a reference to the old values, so we can determine what object is
      // throwing these errors more easily.
      let oldValues = {};
      for (let key in this) {
        if (whitelist[key]) {
          continue;
        }

        oldValues[key] = this[key];
        Object.defineProperty(this, key, {
          get: function() {
            throw new Error(
                'Cannot access member ' + key + ' of disposed object');
          },
          set: function(value) {
            if (!goog.isFunction(value)) {
                throw new Error('Cannot set member ' + key + ' of disposed object');
            }
          }
        });
      }
      this.__oldValues__ = oldValues;
      this.__disposedMarker__ = new goog.DisposedMarker();
    }
  }
};

/**
 * Associates a disposable object with this object so that they will be disposed
 * together.
 * @param {goog.disposable.IDisposable} disposable that will be disposed when
 *     this object is disposed.
 */
goog.Disposable.prototype.registerDisposable = function(disposable) {
  if (this.disposed_) {
    disposable.dispose();
    return;
  }
  if (!this.registeredDisposables_) {
    this.registeredDisposables_ = [];
  }

  this.registeredDisposables_.push(disposable);
};

/**
 * Disassociates a disposable object with this object so that they won't be
 * disposed together.
 * @param {goog.disposable.IDisposable} disposable that won't be disposed when
 *     this object is disposed.
 */
goog.Disposable.prototype.unregisterDisposable = function(disposable) {
  if (this.disposed_ || !this.registeredDisposables_) {
    return;
  }

  goog.array.remove(this.registeredDisposables_, disposable);
};

/**
 * Invokes a callback function when this object is disposed. Callbacks are
 * invoked in the order in which they were added. If a callback is added to
 * an already disposed Disposable, it will be called immediately.
 * @param {function(this:T):?} callback The callback function.
 * @param {T=} opt_scope An optional scope to call the callback in.
 * @return {function(this:T):?} A callback you can pass to
 *     removeOnDisposeCallback to undo this addOnDisposeCallback.
 * @template T
 */
goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
  if (this.disposed_) {
    opt_scope !== undefined ? callback.call(opt_scope) : callback();
    return callback;
  }
  if (!this.onDisposeCallbacks_) {
    this.onDisposeCallbacks_ = [];
  }

  this.onDisposeCallbacks_.push(
      opt_scope !== undefined ? goog.bind(callback, opt_scope) : callback);
  return callback;
};

/**
 * Remove a callback added with addOnDisposeCallback.
 * @param {function(this:T):?} callback The callback function.
 * @template T
 */
goog.Disposable.prototype.removeOnDisposeCallback = function(callback) {
  if (this.disposed_ || !this.onDisposeCallbacks_) {
    return;
  }

  goog.array.remove(this.onDisposeCallbacks_, callback);
};

/**
 * Deletes or nulls out any references to COM objects, DOM nodes, or other
 * disposable objects. Classes that extend `goog.Disposable` should
 * override this method.
 * Not reentrant. To avoid calling it twice, it must only be called from the
 * subclass' `disposeInternal` method. Everywhere else the public
 * `dispose` method must be used.
 * For example:
 * <pre>
 *   mypackage.MyClass = function() {
 *     mypackage.MyClass.base(this, 'constructor');
 *     // Constructor logic specific to MyClass.
 *     ...
 *   };
 *   goog.inherits(mypackage.MyClass, goog.Disposable);
 *
 *   mypackage.MyClass.prototype.disposeInternal = function() {
 *     // Dispose logic specific to MyClass.
 *     ...
 *     // Call superclass's disposeInternal at the end of the subclass's, like
 *     // in C++, to avoid hard-to-catch issues.
 *     mypackage.MyClass.base(this, 'disposeInternal');
 *   };
 * </pre>
 * @protected
 */
goog.Disposable.prototype.disposeInternal = function() {
  if (this.onDisposeCallbacks_) {
    while (this.onDisposeCallbacks_.length) {
      this.onDisposeCallbacks_.shift()();
    }
  }
  if (this.registeredDisposables_) {
    while (this.registeredDisposables_.length) {
      this.registeredDisposables_.shift().dispose();
    }
  }
};

/**
 * Returns True if we can verify the object is disposed.
 * Calls `isDisposed` on the argument if it supports it.  If obj
 * is not an object with an isDisposed() method, return false.
 * @param {*} obj The object to investigate.
 * @return {boolean} True if we can verify the object is disposed.
 */
goog.Disposable.isDisposed = function(obj) {
  if (obj && typeof obj.isDisposed == 'function') {
    return obj.isDisposed();
  }
  return false;
};

/**
 * Calls `dispose` on the argument if it supports it. If obj is not an
 *     object with a dispose() method, this is a no-op.
 * @param {*} obj The object to dispose of.
 */
goog.dispose = function(obj) {
  if (obj && typeof obj.dispose == 'function') {
    obj.dispose();
  }
};

/**
 * Calls `dispose` on each member of the list that supports it. (If the
 * member is an ArrayLike, then `goog.disposeAll()` will be called
 * recursively on each of its members.) If the member is not an object with a
 * `dispose()` method, then it is ignored.
 * @param {...*} var_args The list.
 */
goog.disposeAll = function(var_args) {
  for (var i = 0, len = arguments.length; i < len; ++i) {
    var disposable = arguments[i];
    if (goog.isArrayLike(disposable)) {
      goog.disposeAll.apply(null, disposable);
    } else {
      goog.dispose(disposable);
    }
  }
};
<commit_msg>Turn off Object.seal() support for Disposable<commit_after>// Copyright 2005 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Implements the disposable interface. The dispose method is used
 * to clean up references and resources.
 */

goog.provide('goog.Disposable');
goog.provide('goog.dispose');
goog.provide('goog.disposeAll');

goog.require('goog.disposable.IDisposable');

/**
 * @define {number} The monitoring mode of the goog.Disposable
 *     instances. Default is OFF. Switching on the monitoring is only
 *     recommended for debugging because it has a significant impact on
 *     performance and memory usage. If switched off, the monitoring code
 *     compiles down to 0 bytes.
 */
goog.Disposable.MONITORING_MODE =
    goog.define('goog.Disposable.MONITORING_MODE', 0);

/**
 * @define {boolean} Whether to attach creation stack to each created disposable
 *     instance; This is only relevant for when MonitoringMode != OFF.
 */
goog.Disposable.INCLUDE_STACK_ON_CREATION =
    goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);

/**
 * Class that provides the basic implementation for disposable objects. If your
 * class holds one or more references to COM objects, DOM nodes, or other
 * disposable objects, it should extend this class or implement the disposable
 * interface (defined in goog.disposable.IDisposable).
 * @constructor
 * @implements {goog.disposable.IDisposable}
 */
goog.Disposable = function() {
  /**
   * If monitoring the goog.Disposable instances is enabled, stores the creation
   * stack trace of the Disposable instance.
   * @type {string|undefined}
   */
  this.creationStack;

  if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
    if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
      this.creationStack = new Error().stack;
    }
    goog.Disposable.instances_[goog.getUid(this)] = this;
  }
  // Support sealing
  // LUCID modification: We don't use Object.seal(), or the goog.base stuff that does it. We save about 4% of our total
  // JS heap size on large documents by not setting these properties aggressively.
  // this.disposed_ = this.disposed_;
  // this.onDisposeCallbacks_ = this.onDisposeCallbacks_;
  // this.registeredDisposables_ = this.registeredDisposables_;
};

/**
 * @enum {number} Different monitoring modes for Disposable.
 */
goog.Disposable.MonitoringMode = {
  /**
   * No monitoring.
   */
  OFF: 0,
  /**
   * Creating and disposing the goog.Disposable instances is monitored. All
   * disposable objects need to call the `goog.Disposable` base
   * constructor. The PERMANENT mode must be switched on before creating any
   * goog.Disposable instances.
   */
  PERMANENT: 1,
  /**
   * INTERACTIVE mode can be switched on and off on the fly without producing
   * errors. It also doesn't warn if the disposable objects don't call the
   * `goog.Disposable` base constructor.
   */
  INTERACTIVE: 2
};

/**
 * Maps the unique ID of every undisposed `goog.Disposable` object to
 * the object itself.
 * @type {!Object<number, !goog.Disposable>}
 * @private
 */
goog.Disposable.instances_ = {};

/**
 * @return {!Array<!goog.Disposable>} All `goog.Disposable` objects that
 *     haven't been disposed of.
 */
goog.Disposable.getUndisposedObjects = function() {
  var ret = [];
  for (var id in goog.Disposable.instances_) {
    if (goog.Disposable.instances_.hasOwnProperty(id)) {
      ret.push(goog.Disposable.instances_[Number(id)]);
    }
  }
  return ret;
};

/**
 * Clears the registry of undisposed objects but doesn't dispose of them.
 */
goog.Disposable.clearUndisposedObjects = function() {
  goog.Disposable.instances_ = {};
};

/**
 * Whether the object has been disposed of.
 * @type {boolean}
 * @private
 */
goog.Disposable.prototype.disposed_ = false;

/**
 * Callbacks to invoke when this object is disposed.
 * @type {Array<!Function>}
 * @private
 */
goog.Disposable.prototype.onDisposeCallbacks_;

/**
 * Other Disposables to dispose when this object is disposed.
 * @type {Array<goog.disposable.IDisposable>}
 * @private
 */
goog.Disposable.prototype.registeredDisposables_;

/**
 * @return {boolean} Whether the object has been disposed of.
 * @override
 */
goog.Disposable.prototype.isDisposed = function() {
  return this.disposed_;
};

/**
 * @return {boolean} Whether the object has been disposed of.
 * @deprecated Use {@link #isDisposed} instead.
 */
goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;

/**
 * Added to disposed objects. This allows you to do a heap dump and search for
 * all disposed objects (by finding their DisposedMarker instances) and track
 * down the retaining paths, to clean up memory leaks.
 * @constructor
 */
goog.DisposedMarker = function() {};

/**
 * Disposes of the object. If the object hasn't already been disposed of, calls
 * {@link #disposeInternal}. Classes that extend `goog.Disposable` should
 * override {@link #disposeInternal} in order to delete references to COM
 * objects, DOM nodes, and other disposable objects. Reentrant.
 *
 * @return {void} Nothing.
 * @override
 */
goog.Disposable.prototype.dispose = function() {
  if (!this.disposed_) {
    // Set disposed_ to true first, in case during the chain of disposal this
    // gets disposed recursively.
    this.disposed_ = true;
    this.disposeInternal();
    if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
      var uid = goog.getUid(this);
      if (goog.Disposable.MONITORING_MODE ==
              goog.Disposable.MonitoringMode.PERMANENT &&
          !goog.Disposable.instances_.hasOwnProperty(uid)) {
        throw new Error(
            this + ' did not call the goog.Disposable base ' +
            'constructor or was disposed of after a clearUndisposedObjects ' +
            'call');
      }
      if (goog.Disposable.MONITORING_MODE !=
              goog.Disposable.MonitoringMode.OFF &&
          this.onDisposeCallbacks_ && this.onDisposeCallbacks_.length > 0) {
        throw new Error(
            this + ' did not empty its onDisposeCallbacks queue. This ' +
            'probably means it overrode dispose() or disposeInternal() ' +
            'without calling the superclass\' method.');
      }
      delete goog.Disposable.instances_[uid];
    }

    // In debug mode, disallow all access to disposed object members.
    // Never allow this to happen in compiled mode, even for unit tests,
    // because the compiler can sometimes reorder statements in a way that
    // causes this to fail.
    if (goog.DEBUG && !COMPILED) {
      // The only thing a disposed object can do is return true from
      // isDisposed().
      let whitelist = {
        isDisposed: 1,
        disposed_: 1,
      };

      // Keep a reference to the old values, so we can determine what object is
      // throwing these errors more easily.
      let oldValues = {};
      for (let key in this) {
        if (whitelist[key]) {
          continue;
        }

        oldValues[key] = this[key];
        Object.defineProperty(this, key, {
          get: function() {
            throw new Error(
                'Cannot access member ' + key + ' of disposed object');
          },
          set: function(value) {
            if (!goog.isFunction(value)) {
                throw new Error('Cannot set member ' + key + ' of disposed object');
            }
          }
        });
      }
      this.__oldValues__ = oldValues;
      this.__disposedMarker__ = new goog.DisposedMarker();
    }
  }
};

/**
 * Associates a disposable object with this object so that they will be disposed
 * together.
 * @param {goog.disposable.IDisposable} disposable that will be disposed when
 *     this object is disposed.
 */
goog.Disposable.prototype.registerDisposable = function(disposable) {
  if (this.disposed_) {
    disposable.dispose();
    return;
  }
  if (!this.registeredDisposables_) {
    this.registeredDisposables_ = [];
  }

  this.registeredDisposables_.push(disposable);
};

/**
 * Disassociates a disposable object with this object so that they won't be
 * disposed together.
 * @param {goog.disposable.IDisposable} disposable that won't be disposed when
 *     this object is disposed.
 */
goog.Disposable.prototype.unregisterDisposable = function(disposable) {
  if (this.disposed_ || !this.registeredDisposables_) {
    return;
  }

  goog.array.remove(this.registeredDisposables_, disposable);
};

/**
 * Invokes a callback function when this object is disposed. Callbacks are
 * invoked in the order in which they were added. If a callback is added to
 * an already disposed Disposable, it will be called immediately.
 * @param {function(this:T):?} callback The callback function.
 * @param {T=} opt_scope An optional scope to call the callback in.
 * @return {function(this:T):?} A callback you can pass to
 *     removeOnDisposeCallback to undo this addOnDisposeCallback.
 * @template T
 */
goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
  if (this.disposed_) {
    opt_scope !== undefined ? callback.call(opt_scope) : callback();
    return callback;
  }
  if (!this.onDisposeCallbacks_) {
    this.onDisposeCallbacks_ = [];
  }

  this.onDisposeCallbacks_.push(
      opt_scope !== undefined ? goog.bind(callback, opt_scope) : callback);
  return callback;
};

/**
 * Remove a callback added with addOnDisposeCallback.
 * @param {function(this:T):?} callback The callback function.
 * @template T
 */
goog.Disposable.prototype.removeOnDisposeCallback = function(callback) {
  if (this.disposed_ || !this.onDisposeCallbacks_) {
    return;
  }

  goog.array.remove(this.onDisposeCallbacks_, callback);
};

/**
 * Deletes or nulls out any references to COM objects, DOM nodes, or other
 * disposable objects. Classes that extend `goog.Disposable` should
 * override this method.
 * Not reentrant. To avoid calling it twice, it must only be called from the
 * subclass' `disposeInternal` method. Everywhere else the public
 * `dispose` method must be used.
 * For example:
 * <pre>
 *   mypackage.MyClass = function() {
 *     mypackage.MyClass.base(this, 'constructor');
 *     // Constructor logic specific to MyClass.
 *     ...
 *   };
 *   goog.inherits(mypackage.MyClass, goog.Disposable);
 *
 *   mypackage.MyClass.prototype.disposeInternal = function() {
 *     // Dispose logic specific to MyClass.
 *     ...
 *     // Call superclass's disposeInternal at the end of the subclass's, like
 *     // in C++, to avoid hard-to-catch issues.
 *     mypackage.MyClass.base(this, 'disposeInternal');
 *   };
 * </pre>
 * @protected
 */
goog.Disposable.prototype.disposeInternal = function() {
  if (this.onDisposeCallbacks_) {
    while (this.onDisposeCallbacks_.length) {
      this.onDisposeCallbacks_.shift()();
    }
  }
  if (this.registeredDisposables_) {
    while (this.registeredDisposables_.length) {
      this.registeredDisposables_.shift().dispose();
    }
  }
};

/**
 * Returns True if we can verify the object is disposed.
 * Calls `isDisposed` on the argument if it supports it.  If obj
 * is not an object with an isDisposed() method, return false.
 * @param {*} obj The object to investigate.
 * @return {boolean} True if we can verify the object is disposed.
 */
goog.Disposable.isDisposed = function(obj) {
  if (obj && typeof obj.isDisposed == 'function') {
    return obj.isDisposed();
  }
  return false;
};

/**
 * Calls `dispose` on the argument if it supports it. If obj is not an
 *     object with a dispose() method, this is a no-op.
 * @param {*} obj The object to dispose of.
 */
goog.dispose = function(obj) {
  if (obj && typeof obj.dispose == 'function') {
    obj.dispose();
  }
};

/**
 * Calls `dispose` on each member of the list that supports it. (If the
 * member is an ArrayLike, then `goog.disposeAll()` will be called
 * recursively on each of its members.) If the member is not an object with a
 * `dispose()` method, then it is ignored.
 * @param {...*} var_args The list.
 */
goog.disposeAll = function(var_args) {
  for (var i = 0, len = arguments.length; i < len; ++i) {
    var disposable = arguments[i];
    if (goog.isArrayLike(disposable)) {
      goog.disposeAll.apply(null, disposable);
    } else {
      goog.dispose(disposable);
    }
  }
};
<|endoftext|>


<commit_before>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classnames from 'classnames';
import { settings } from 'carbon-components';
import { CheckmarkOutline16, Warning16 } from '@carbon/icons-react';
import { keys, matches } from '../../internal/keyboard';

const { prefix } = settings;
const defaultRenderLabel = props =>
;
export const ProgressStep = ({ ...props }) => {
  const {
    label,
    description,
    className,
    current,
    complete,
    invalid,
    secondaryLabel,
    disabled,
    onClick,
    renderLabel: ProgressStepLabel,
  } = props;

  const classes = classnames({
    [`${prefix}--progress-step`]: true,
    [`${prefix}--progress-step--current`]: current,
    [`${prefix}--progress-step--complete`]: complete,
    [`${prefix}--progress-step--incomplete`]: !complete && !current,
    [`${prefix}--progress-step--disabled`]: disabled,
    [className]: className,
  });

  const handleKeyDown = e => {
    if (matches(e, [keys.Enter, keys.Space])) {
      onClick();
    }
  };

  const SVGIcon = ({ complete, current, description, invalid, prefix }) => {
    if (invalid) {
      return <Warning16 className={`${prefix}--progress__warning`} />;
    }
    if (current) {
      return (
        <svg>
          <path d="M 7, 7 m -7, 0 a 7,7 0 1,0 14,0 a 7,7 0 1,0 -14,0" />
          <title>{description}</title>
        </svg>
      );
    }
    if (complete) {
      return (
        <CheckmarkOutline16 role="img">
          <title>{description}</title>
        </CheckmarkOutline16>
      );
    }
    return (
      <svg>
        <title>{description}</title>
        <path d="M8 1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6z" />
      </svg>
    );
  };

  return (

      <div
        className={classnames(`${prefix}--progress-step-button`, {
          [`${prefix}--progress-step-button--unclickable`]: !onClick || current,
        })}
        role="button"
        tabIndex={!current && onClick ? 0 : -1}
        onClick={!current ? onClick : undefined}
        onKeyDown={handleKeyDown}>
        <SVGIcon
          complete={complete}
          current={current}
          description={description}
          invalid={invalid}
          prefix={prefix}
        />
        <ProgressStepLabel className={`${prefix}--progress-label`}>
          {label}
        </ProgressStepLabel>
        {secondaryLabel !== null && secondaryLabel !== undefined ? (
          <p className={`${prefix}--progress-optional`}>{secondaryLabel}

        ) : null}
        <span className={`${prefix}--progress-line`} />

  );
};

ProgressStep.propTypes = {
  /**
   * Index of the current step within the ProgressIndicator
   */
  index: PropTypes.number,

  /**
   * Provide the label for the <ProgressStep>
   */
  label: PropTypes.node.isRequired,

  /**
   * Provide an optional className to be applied to the containing
 node
   */
  className: PropTypes.string,

  /**
   * Specify whether the step is the current step
   */
  current: PropTypes.bool,

  /**
   * Specify whether the step has been completed
   */
  complete: PropTypes.bool,

  /**
   * Provide a description for the <ProgressStep>
   */
  description: PropTypes.string,

  /**
   * Specify whether the step is invalid
   */
  invalid: PropTypes.bool,

  /**
   * Provide an optional secondary label
   */
  secondaryLabel: PropTypes.string,

  /*
   * An optional parameter to allow for overflow content to be rendered in a
   * tooltip.
   */
  renderLabel: PropTypes.func,

  /**
   * Provide the props that describe a progress step tooltip
   */
  overflowTooltipProps: PropTypes.object,

  /**
   * Specify whether the step is disabled
   */
  disabled: PropTypes.bool,

  /**
   * The ID of the tooltip content.
   */
  tooltipId: PropTypes.string,

  /**
   * A callback called if the step is clicked or the enter key is pressed
   */
  onClick: PropTypes.func,
};

ProgressStep.defaultProps = {
  renderLabel: defaultRenderLabel,
};

export class ProgressIndicator extends Component {
  state = {};

  static propTypes = {
    /**
     * Provide <ProgressStep> components to be rendered in the
     * <ProgressIndicator>
     */
    children: PropTypes.node,

    /**
     * Provide an optional className to be applied to the containing node
     */
    className: PropTypes.string,

    /**
     * Optionally specify the current step array index
     */
    currentIndex: PropTypes.number,

    /**
     * Optional callback called if a ProgressStep is clicked on.  Returns the index of the step.
     */
    onChange: PropTypes.func,
  };

  static defaultProps = {
    currentIndex: 0,
  };

  static getDerivedStateFromProps({ currentIndex }, state) {
    const { prevCurrentIndex } = state;
    return prevCurrentIndex === currentIndex
      ? null
      : {
          currentIndex,
          prevCurrentIndex: currentIndex,
        };
  }

  renderSteps = () => {
    const { onChange } = this.props;

    return React.Children.map(this.props.children, (child, index) => {
      // only setup click handlers if onChange event is passed
      const onClick = onChange ? () => onChange(index) : undefined;
      if (index === this.state.currentIndex) {
        return React.cloneElement(child, {
          current: true,
          index,
          onClick,
        });
      }
      if (index < this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: true,
          index,
          onClick,
        });
      }
      if (index > this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: false,
          index,
          onClick,
        });
      }
      return null;
    });
  };

  render() {
    const { className, currentIndex, ...other } = this.props; // eslint-disable-line no-unused-vars
    const classes = classnames({
      [`${prefix}--progress`]: true,
      [className]: className,
    });
    return (

        {this.renderSteps()}

    );
  }
}
<commit_msg>fix(ProgressIndicator): remove `role` attribute from checkmark svg (#4255)<commit_after>/**
 * Copyright IBM Corp. 2016, 2018
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import classnames from 'classnames';
import { settings } from 'carbon-components';
import { CheckmarkOutline16, Warning16 } from '@carbon/icons-react';
import { keys, matches } from '../../internal/keyboard';

const { prefix } = settings;
const defaultRenderLabel = props =>
;
export const ProgressStep = ({ ...props }) => {
  const {
    label,
    description,
    className,
    current,
    complete,
    invalid,
    secondaryLabel,
    disabled,
    onClick,
    renderLabel: ProgressStepLabel,
  } = props;

  const classes = classnames({
    [`${prefix}--progress-step`]: true,
    [`${prefix}--progress-step--current`]: current,
    [`${prefix}--progress-step--complete`]: complete,
    [`${prefix}--progress-step--incomplete`]: !complete && !current,
    [`${prefix}--progress-step--disabled`]: disabled,
    [className]: className,
  });

  const handleKeyDown = e => {
    if (matches(e, [keys.Enter, keys.Space])) {
      onClick();
    }
  };

  const SVGIcon = ({ complete, current, description, invalid, prefix }) => {
    if (invalid) {
      return <Warning16 className={`${prefix}--progress__warning`} />;
    }
    if (current) {
      return (
        <svg>
          <path d="M 7, 7 m -7, 0 a 7,7 0 1,0 14,0 a 7,7 0 1,0 -14,0" />
          <title>{description}</title>
        </svg>
      );
    }
    if (complete) {
      return (
        <CheckmarkOutline16>
          <title>{description}</title>
        </CheckmarkOutline16>
      );
    }
    return (
      <svg>
        <title>{description}</title>
        <path d="M8 1C4.1 1 1 4.1 1 8s3.1 7 7 7 7-3.1 7-7-3.1-7-7-7zm0 13c-3.3 0-6-2.7-6-6s2.7-6 6-6 6 2.7 6 6-2.7 6-6 6z" />
      </svg>
    );
  };

  return (

      <div
        className={classnames(`${prefix}--progress-step-button`, {
          [`${prefix}--progress-step-button--unclickable`]: !onClick || current,
        })}
        role="button"
        tabIndex={!current && onClick ? 0 : -1}
        onClick={!current ? onClick : undefined}
        onKeyDown={handleKeyDown}>
        <SVGIcon
          complete={complete}
          current={current}
          description={description}
          invalid={invalid}
          prefix={prefix}
        />
        <ProgressStepLabel className={`${prefix}--progress-label`}>
          {label}
        </ProgressStepLabel>
        {secondaryLabel !== null && secondaryLabel !== undefined ? (
          <p className={`${prefix}--progress-optional`}>{secondaryLabel}

        ) : null}
        <span className={`${prefix}--progress-line`} />

  );
};

ProgressStep.propTypes = {
  /**
   * Index of the current step within the ProgressIndicator
   */
  index: PropTypes.number,

  /**
   * Provide the label for the <ProgressStep>
   */
  label: PropTypes.node.isRequired,

  /**
   * Provide an optional className to be applied to the containing
 node
   */
  className: PropTypes.string,

  /**
   * Specify whether the step is the current step
   */
  current: PropTypes.bool,

  /**
   * Specify whether the step has been completed
   */
  complete: PropTypes.bool,

  /**
   * Provide a description for the <ProgressStep>
   */
  description: PropTypes.string,

  /**
   * Specify whether the step is invalid
   */
  invalid: PropTypes.bool,

  /**
   * Provide an optional secondary label
   */
  secondaryLabel: PropTypes.string,

  /*
   * An optional parameter to allow for overflow content to be rendered in a
   * tooltip.
   */
  renderLabel: PropTypes.func,

  /**
   * Provide the props that describe a progress step tooltip
   */
  overflowTooltipProps: PropTypes.object,

  /**
   * Specify whether the step is disabled
   */
  disabled: PropTypes.bool,

  /**
   * The ID of the tooltip content.
   */
  tooltipId: PropTypes.string,

  /**
   * A callback called if the step is clicked or the enter key is pressed
   */
  onClick: PropTypes.func,
};

ProgressStep.defaultProps = {
  renderLabel: defaultRenderLabel,
};

export class ProgressIndicator extends Component {
  state = {};

  static propTypes = {
    /**
     * Provide <ProgressStep> components to be rendered in the
     * <ProgressIndicator>
     */
    children: PropTypes.node,

    /**
     * Provide an optional className to be applied to the containing node
     */
    className: PropTypes.string,

    /**
     * Optionally specify the current step array index
     */
    currentIndex: PropTypes.number,

    /**
     * Optional callback called if a ProgressStep is clicked on.  Returns the index of the step.
     */
    onChange: PropTypes.func,
  };

  static defaultProps = {
    currentIndex: 0,
  };

  static getDerivedStateFromProps({ currentIndex }, state) {
    const { prevCurrentIndex } = state;
    return prevCurrentIndex === currentIndex
      ? null
      : {
          currentIndex,
          prevCurrentIndex: currentIndex,
        };
  }

  renderSteps = () => {
    const { onChange } = this.props;

    return React.Children.map(this.props.children, (child, index) => {
      // only setup click handlers if onChange event is passed
      const onClick = onChange ? () => onChange(index) : undefined;
      if (index === this.state.currentIndex) {
        return React.cloneElement(child, {
          current: true,
          index,
          onClick,
        });
      }
      if (index < this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: true,
          index,
          onClick,
        });
      }
      if (index > this.state.currentIndex) {
        return React.cloneElement(child, {
          complete: false,
          index,
          onClick,
        });
      }
      return null;
    });
  };

  render() {
    const { className, currentIndex, ...other } = this.props; // eslint-disable-line no-unused-vars
    const classes = classnames({
      [`${prefix}--progress`]: true,
      [className]: className,
    });
    return (

        {this.renderSteps()}

    );
  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Test helpers to compare goog.proto2.Messages.
 */

goog.setTestOnly('goog.testing.proto2');
goog.provide('goog.testing.proto2');

goog.require('goog.proto2.Message');
goog.require('goog.proto2.ObjectSerializer');
goog.require('goog.testing.asserts');

/**
 * Compares two goog.proto2.Message instances of the same type.
 * @param {!goog.proto2.Message} expected First message.
 * @param {!goog.proto2.Message} actual Second message.
 * @param {string} path Path to the messages.
 * @return {string} A string describing where they differ. Empty string if they
 *     are equal.
 * @private
 */
goog.testing.proto2.findDifferences_ = function(expected, actual, path) {
  'use strict';
  var fields = expected.getDescriptor().getFields();
  for (var i = 0; i < fields.length; i++) {
    var field = fields[i];
    var newPath = (path ? path + '/' : '') + field.getName();

    if (expected.has(field) && !actual.has(field)) {
      return newPath + ' should be present';
    }
    if (!expected.has(field) && actual.has(field)) {
      return newPath + ' should not be present';
    }

    if (expected.has(field)) {
      var isComposite = field.isCompositeType();

      if (field.isRepeated()) {
        var expectedCount = expected.countOf(field);
        var actualCount = actual.countOf(field);
        if (expectedCount != actualCount) {
          return newPath + ' should have ' + expectedCount + ' items, ' +
              'but has ' + actualCount;
        }

        for (var j = 0; j < expectedCount; j++) {
          var expectedItem = expected.get(field, j);
          var actualItem = actual.get(field, j);
          if (isComposite) {
            var itemDiff = goog.testing.proto2.findDifferences_(
                /** @type {!goog.proto2.Message} */ (expectedItem),
                /** @type {!goog.proto2.Message} */ (actualItem),
                newPath + '[' + j + ']');
            if (itemDiff) {
              return itemDiff;
            }
          } else {
            if (expectedItem != actualItem) {
              return newPath + '[' + j + '] should be ' + expectedItem +
                  ', but was ' + actualItem;
            }
          }
        }
      } else {
        var expectedValue = expected.get(field);
        var actualValue = actual.get(field);
        if (isComposite) {
          var diff = goog.testing.proto2.findDifferences_(
              /** @type {!goog.proto2.Message} */ (expectedValue),
              /** @type {!goog.proto2.Message} */ (actualValue), newPath);
          if (diff) {
            return diff;
          }
        } else {
          if (expectedValue != actualValue) {
            return newPath + ' should be ' + expectedValue + ', but was ' +
                actualValue;
          }
        }
      }
    }
  }

  return '';
};

/**
 * Compares two goog.proto2.Message objects. Gives more readable output than
 * assertObjectEquals on mismatch.
 * @param {!goog.proto2.Message} expected Expected proto2 message.
 * @param {!goog.proto2.Message} actual Actual proto2 message.
 * @param {string=} opt_failureMessage Failure message when the values don't
 *     match.
 */
goog.testing.proto2.assertEquals = function(
    expected, actual, opt_failureMessage) {
  'use strict';
  var failureSummary = opt_failureMessage || '';
  if (!(expected instanceof goog.proto2.Message) ||
      !(actual instanceof goog.proto2.Message)) {
    goog.testing.asserts.raiseException(
        failureSummary,
        'Bad arguments were passed to goog.testing.proto2.assertEquals');
  }
  if (expected.constructor != actual.constructor) {
    goog.testing.asserts.raiseException(
        failureSummary, 'Message type mismatch: ' +
            expected.getDescriptor().getFullName() + ' != ' +
            actual.getDescriptor().getFullName());
  }
  var diff = goog.testing.proto2.findDifferences_(expected, actual, '');
  if (diff) {
    goog.testing.asserts.raiseException(failureSummary, diff);
  }
};

/**
 * Helper function to quickly build protocol buffer messages from JSON objects.
 * @param {function(new:MessageType)} messageCtor A constructor that
 *     creates a `goog.proto2.Message` subclass instance.
 * @param {!Object} json JSON object which uses field names as keys.
 * @return {MessageType} The deserialized protocol buffer.
 * @template MessageType
 */
goog.testing.proto2.fromObject = function(messageCtor, json) {
  'use strict';
  var serializer = new goog.proto2.ObjectSerializer(
      goog.proto2.ObjectSerializer.KeyOption.NAME);
  var message = new messageCtor;
  serializer.deserializeTo(message, json);
  return message;
};
<commit_msg>Use ES6 const/let more throughout Closure Library. RELNOTES: n/a<commit_after>/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @fileoverview Test helpers to compare goog.proto2.Messages.
 */

goog.setTestOnly('goog.testing.proto2');
goog.provide('goog.testing.proto2');

goog.require('goog.proto2.Message');
goog.require('goog.proto2.ObjectSerializer');
goog.require('goog.testing.asserts');

/**
 * Compares two goog.proto2.Message instances of the same type.
 * @param {!goog.proto2.Message} expected First message.
 * @param {!goog.proto2.Message} actual Second message.
 * @param {string} path Path to the messages.
 * @return {string} A string describing where they differ. Empty string if they
 *     are equal.
 * @private
 */
goog.testing.proto2.findDifferences_ = function(expected, actual, path) {
  'use strict';
  const fields = expected.getDescriptor().getFields();
  for (let i = 0; i < fields.length; i++) {
    const field = fields[i];
    const newPath = (path ? path + '/' : '') + field.getName();

    if (expected.has(field) && !actual.has(field)) {
      return newPath + ' should be present';
    }
    if (!expected.has(field) && actual.has(field)) {
      return newPath + ' should not be present';
    }

    if (expected.has(field)) {
      const isComposite = field.isCompositeType();

      if (field.isRepeated()) {
        const expectedCount = expected.countOf(field);
        const actualCount = actual.countOf(field);
        if (expectedCount != actualCount) {
          return newPath + ' should have ' + expectedCount + ' items, ' +
              'but has ' + actualCount;
        }

        for (let j = 0; j < expectedCount; j++) {
          const expectedItem = expected.get(field, j);
          const actualItem = actual.get(field, j);
          if (isComposite) {
            const itemDiff = goog.testing.proto2.findDifferences_(
                /** @type {!goog.proto2.Message} */ (expectedItem),
                /** @type {!goog.proto2.Message} */ (actualItem),
                newPath + '[' + j + ']');
            if (itemDiff) {
              return itemDiff;
            }
          } else {
            if (expectedItem != actualItem) {
              return newPath + '[' + j + '] should be ' + expectedItem +
                  ', but was ' + actualItem;
            }
          }
        }
      } else {
        const expectedValue = expected.get(field);
        const actualValue = actual.get(field);
        if (isComposite) {
          const diff = goog.testing.proto2.findDifferences_(
              /** @type {!goog.proto2.Message} */ (expectedValue),
              /** @type {!goog.proto2.Message} */ (actualValue), newPath);
          if (diff) {
            return diff;
          }
        } else {
          if (expectedValue != actualValue) {
            return newPath + ' should be ' + expectedValue + ', but was ' +
                actualValue;
          }
        }
      }
    }
  }

  return '';
};

/**
 * Compares two goog.proto2.Message objects. Gives more readable output than
 * assertObjectEquals on mismatch.
 * @param {!goog.proto2.Message} expected Expected proto2 message.
 * @param {!goog.proto2.Message} actual Actual proto2 message.
 * @param {string=} opt_failureMessage Failure message when the values don't
 *     match.
 */
goog.testing.proto2.assertEquals = function(
    expected, actual, opt_failureMessage) {
  'use strict';
  const failureSummary = opt_failureMessage || '';
  if (!(expected instanceof goog.proto2.Message) ||
      !(actual instanceof goog.proto2.Message)) {
    goog.testing.asserts.raiseException(
        failureSummary,
        'Bad arguments were passed to goog.testing.proto2.assertEquals');
  }
  if (expected.constructor != actual.constructor) {
    goog.testing.asserts.raiseException(
        failureSummary, 'Message type mismatch: ' +
            expected.getDescriptor().getFullName() + ' != ' +
            actual.getDescriptor().getFullName());
  }
  const diff = goog.testing.proto2.findDifferences_(expected, actual, '');
  if (diff) {
    goog.testing.asserts.raiseException(failureSummary, diff);
  }
};

/**
 * Helper function to quickly build protocol buffer messages from JSON objects.
 * @param {function(new:MessageType)} messageCtor A constructor that
 *     creates a `goog.proto2.Message` subclass instance.
 * @param {!Object} json JSON object which uses field names as keys.
 * @return {MessageType} The deserialized protocol buffer.
 * @template MessageType
 */
goog.testing.proto2.fromObject = function(messageCtor, json) {
  'use strict';
  const serializer = new goog.proto2.ObjectSerializer(
      goog.proto2.ObjectSerializer.KeyOption.NAME);
  const message = new messageCtor;
  serializer.deserializeTo(message, json);
  return message;
};
<|endoftext|>


<commit_before>var AudioChannel = function(args) {
  this.context = AudioContext ? new AudioContext() : new webkitAudioContext();
  this.oscill = this.context.createOscillator();
  this.gain = this.context.createGain();

  this.oscill.connect(this.gain);
  this.gain.connect(this.context.destination);

  if(args) {
    this.oscill.frequency.value = args.freq ? args.freq : 220;
    this.oscill.type = args.wave ? args.wave : "triangle";
    this.gain.gain.value = args.gain ? args.gain : 0.05;
  }
}
AudioChannel.prototype.playSong = function(song) {
  this.oscill.start();
  var playNextNote = function(song) {
    this.oscill.frequency.value = song[0].freq;
    if(song[1]) {
      setTimeout(playNextNote.bind(this, song.slice(1)), song[0].length);
    }
    else {
      setTimeout(function() {
        this.gain.gain.value = 0;
      }.bind(this), song[0].length);
    }
  }.bind(this);
  playNextNote(song);
}
<commit_msg>Update AudioChannel to create nodes in member function instead of constructor<commit_after>var AudioChannel = function(args) {
  this.context = AudioContext ? new AudioContext() : new webkitAudioContext();
  this.oscill = this.context.createOscillator();
  this.gain = this.context.createGain();

  if(args) {
    this.frequency = args.freq ? args.freq : 220;
    this.wave = args.wave ? args.wave : "triangle";
    this.volume = args.gain ? args.gain : 0.05;
  }
}
AudioChannel.prototype.setNodes = function() {
  this.oscill = this.context.createOscillator();
  this.gain = this.context.createGain();

  this.oscill.connect(this.gain);
  this.gain.connect(this.context.destination);

  this.oscill.frequency.value = this.frequency;
  this.oscill.type = this.wave;
  this.gain.gain.value = this.volume;
};
AudioChannel.prototype.playSong = function(song) {
  this.oscill.start();
  var playNextNote = function(song) {
    this.oscill.frequency.value = song[0].freq;
    if(song[1]) {
      setTimeout(playNextNote.bind(this, song.slice(1)), song[0].length);
    }
    else {
      setTimeout(function() {
        this.gain.gain.value = 0;
      }.bind(this), song[0].length);
    }
  }.bind(this);
  playNextNote(song);
};
<|endoftext|>