<commit_before>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import AppConfig from '../config';
import { type Tag } from '../reducers/taglibrary';

export function baseName(dirPath: string) {
  const fileName = dirPath.substring(dirPath.lastIndexOf(AppConfig.dirSeparator) + 1, dirPath.length);
  return fileName || dirPath;
}

export function extractFileExtension(filePath: string) {
  const lastindexDirSeparator = filePath.lastIndexOf(AppConfig.dirSeparator);
  const lastIndexEndTagContainer = filePath.lastIndexOf(AppConfig.endTagContainer);
  const lastindexDot = filePath.lastIndexOf('.');
  if (lastindexDot < 0) {
    return '';
  } else if (lastindexDot < lastindexDirSeparator) {
    // case: "../remote.php/webdav/somefilename"
    return '';
  } else if (lastindexDot < lastIndexEndTagContainer) {
    // case: "[20120125 89.4kg 19.5% 60.5% 39.8% 2.6kg]"
    return '';
  }
  let extension = filePath.substring(lastindexDot + 1, filePath.length).toLowerCase().trim();
  const lastindexQuestionMark = extension.lastIndexOf('?');
  if (lastindexQuestionMark > 0) { // Removing everything after ? in URLs .png?queryParam1=2342
    extension = extension.substring(0, lastindexQuestionMark);
  }
  return extension;

  /* alternative implementation
    const ext = fileURL.split('.').pop();
    return (ext === fileURL) ? '' : ext; */
}

export function getMetaDirectoryPath(directoryPath: string) {
  return normalizePath(directoryPath) + AppConfig.dirSeparator + AppConfig.metaFolder;
}

export function getMetaFileLocationForFile(entryPath: string) {
  const containingFolder = extractContainingDirectoryPath(entryPath);
  const metaFolder = getMetaDirectoryPath(containingFolder);
  return metaFolder + AppConfig.dirSeparator + extractFileName(entryPath) + AppConfig.metaFileExt;
}

export function getThumbFileLocationForFile(entryPath: string) {
  const containingFolder = extractContainingDirectoryPath(entryPath);
  const metaFolder = getMetaDirectoryPath(containingFolder);
  return metaFolder + AppConfig.dirSeparator + extractFileName(entryPath) + AppConfig.thumbFileExt;
}

export function getMetaFileLocationForDir(entryPath: string) {
  const metaFolder = getMetaDirectoryPath(entryPath);
  return metaFolder + AppConfig.dirSeparator + AppConfig.metaFolderFile;
}

export function extractFileName(filePath: string, dirSeparator: string = AppConfig.dirSeparator): string {
  return filePath.substring(filePath.lastIndexOf(dirSeparator) + 1, filePath.length);
}

export function cleanTrailingDirSeparator(dirPath: string): string {
  if (dirPath) {
    if (dirPath.lastIndexOf('\\') === dirPath.length - 1) {
      return dirPath.substring(0, dirPath.length - 1);
    } else if (dirPath.lastIndexOf('/') === dirPath.length - 1) {
      return dirPath.substring(0, dirPath.length - 1);
    }
    return dirPath;
  }
  console.error('Directory Path ' + dirPath + ' undefined');
  return '';
}

/**
 *
 * @param path -> root//subFolder/
 * @returns {string} -> root/subFolder
 */
export function normalizePath(path: string): string {
  return cleanTrailingDirSeparator(path.replace(/\/\//g, '/'));
}

export function extractFileNameWithoutExt(filePath: string): string {
  const fileName = extractFileName(filePath);
  const indexOfDot = fileName.lastIndexOf('.');
  const lastIndexBeginTagContainer = fileName.lastIndexOf(AppConfig.beginTagContainer);
  const lastIndexEndTagContainer = fileName.lastIndexOf(AppConfig.endTagContainer);
  if (lastIndexBeginTagContainer === 0 && lastIndexEndTagContainer + 1 === fileName.length) {
    // case: "[tag1 tag.2]"
    return '';
  } else if (indexOfDot > 0) {
    // case: regular
    return fileName.substring(0, indexOfDot);
  } else if (indexOfDot === 0) {
    // case ".txt"
    return '';
  }
  return fileName;
}

export function extractContainingDirectoryPath(filePath: string) {
  return filePath.substring(0, filePath.lastIndexOf(AppConfig.dirSeparator));
}

export function extractParentDirectoryPath(dirPath: string) {
  if (dirPath.endsWith(AppConfig.dirSeparator)) {
    dirPath = dirPath.substring(0, dirPath.lastIndexOf(AppConfig.dirSeparator));
  }
  return dirPath.substring(0, dirPath.lastIndexOf(AppConfig.dirSeparator));
}

export function extractDirectoryName(dirPath: string) {
  let directoryName;
  if (dirPath.endsWith(AppConfig.dirSeparator)) {
    directoryName = dirPath.substring(0, dirPath.lastIndexOf(AppConfig.dirSeparator));
  }
  directoryName = dirPath.substring(dirPath.lastIndexOf(AppConfig.dirSeparator) + 1, dirPath.length);
  return directoryName;
}

export function extractContainingDirectoryName(filePath: string) {
  const tmpStr = filePath.substring(0, filePath.lastIndexOf(AppConfig.dirSeparator));
  return tmpStr.substring(tmpStr.lastIndexOf(AppConfig.dirSeparator) + 1, tmpStr.length);
}

export function extractTitle(entryPath: string, isDirectory?: boolean = false) {
  let title;
  if (isDirectory) {
    title = extractDirectoryName(entryPath);
    return title;
  }
  title = extractFileNameWithoutExt(entryPath);

  const beginTagContainer = title.indexOf(AppConfig.beginTagContainer);
  const endTagContainer = title.lastIndexOf(AppConfig.endTagContainer);
  /* cases like "", "t", "["
      if( fileName.length <= 1) {
      // cases like "asd ] asd ["
      else if (beginTagContainer > endTagContainer) {
      // case: [ not found in the filename
      else if ( beginTagContainer < 0 )
      else if ( endTagContainer < 0 ) */
  if (beginTagContainer >= 0 && beginTagContainer < endTagContainer) {
    if (beginTagContainer === 0 && endTagContainer === title.trim().length) {
      // case: "[tag1, tag2]"
      return '';
    } else if (endTagContainer === title.trim().length) {
      // case: "asd[tag1, tag2]"
      return title.slice(0, beginTagContainer);
    }
    // case: "title1 [tag1 tag2] title2"
    return title.slice(0, beginTagContainer) + title.slice(endTagContainer + 1, title.length);
  }
  try {
    title = decodeURIComponent(title);
  } catch (e) {
    console.warn('Decoding URI failed on: ' + title + ' with ' + e);
  }
  return title;
}

export function extractTagsAsObjects(filePath: string, tagDelimiter: string): Array<Tag> {
  const tagsInFileName = extractTags(filePath, tagDelimiter);
  const tagArray = [];
  tagsInFileName.map((tag) => {
    tagArray.push({
      title: '' + tag,
      type: 'plain'
    });
    return true;
  });
  return tagArray;
}

export function extractTags(filePath: string, tagDelimiter: string) {
  if (tagDelimiter === undefined) { // TODO get tagDelimiter from settings reducer only
    // eslint-disable-next-line no-param-reassign
    tagDelimiter = AppConfig.tagDelimiter;
  }
  // console.log('Extracting tags from: ' + filePath);
  const fileName = extractFileName(filePath);
  // WithoutExt
  let tags = [];
  const beginTagContainer = fileName.indexOf(AppConfig.beginTagContainer);
  const endTagContainer = fileName.indexOf(AppConfig.endTagContainer);
  if (beginTagContainer < 0 || endTagContainer < 0 || beginTagContainer >= endTagContainer) {
    // console.log('Filename does not contains tags. Aborting extraction.');
    return tags;
  }
  const cleanedTags = [];
  const tagContainer = fileName.slice(beginTagContainer + 1, endTagContainer).trim();
  tags = tagContainer.split(tagDelimiter);
  for (let i = 0; i < tags.length; i += 1) {
    // Min tag length set to 1 character
    if (tags[i].trim().length > 0) {
      cleanedTags.push(tags[i]);
    }
  }
  return cleanedTags;
}
<commit_msg>added dirseparator as argument to some functions<commit_after>/**
 * TagSpaces - universal file and folder organizer
 * Copyright (C) 2017-present TagSpaces UG (haftungsbeschraenkt)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License (version 3) as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * @flow
 */

import AppConfig from '../config';
import { type Tag } from '../reducers/taglibrary';

export function baseName(dirPath: string) {
  const fileName = dirPath.substring(dirPath.lastIndexOf(AppConfig.dirSeparator) + 1, dirPath.length);
  return fileName || dirPath;
}

export function extractFileExtension(filePath: string) {
  const lastindexDirSeparator = filePath.lastIndexOf(AppConfig.dirSeparator);
  const lastIndexEndTagContainer = filePath.lastIndexOf(AppConfig.endTagContainer);
  const lastindexDot = filePath.lastIndexOf('.');
  if (lastindexDot < 0) {
    return '';
  } else if (lastindexDot < lastindexDirSeparator) {
    // case: "../remote.php/webdav/somefilename"
    return '';
  } else if (lastindexDot < lastIndexEndTagContainer) {
    // case: "[20120125 89.4kg 19.5% 60.5% 39.8% 2.6kg]"
    return '';
  }
  let extension = filePath.substring(lastindexDot + 1, filePath.length).toLowerCase().trim();
  const lastindexQuestionMark = extension.lastIndexOf('?');
  if (lastindexQuestionMark > 0) { // Removing everything after ? in URLs .png?queryParam1=2342
    extension = extension.substring(0, lastindexQuestionMark);
  }
  return extension;

  /* alternative implementation
    const ext = fileURL.split('.').pop();
    return (ext === fileURL) ? '' : ext; */
}

export function getMetaDirectoryPath(directoryPath: string, dirSeparator: string = AppConfig.dirSeparator) {
  return normalizePath(directoryPath) + dirSeparator + AppConfig.metaFolder;
}

export function getMetaFileLocationForFile(entryPath: string, dirSeparator: string = AppConfig.dirSeparator) {
  const containingFolder = extractContainingDirectoryPath(entryPath, dirSeparator);
  const metaFolder = getMetaDirectoryPath(containingFolder, dirSeparator);
  return metaFolder + dirSeparator + extractFileName(entryPath, dirSeparator) + AppConfig.metaFileExt;
}

export function getThumbFileLocationForFile(entryPath: string, dirSeparator: string = AppConfig.dirSeparator) {
  const containingFolder = extractContainingDirectoryPath(entryPath, dirSeparator);
  const metaFolder = getMetaDirectoryPath(containingFolder, dirSeparator);
  return metaFolder + dirSeparator + extractFileName(entryPath, dirSeparator) + AppConfig.thumbFileExt;
}

export function getMetaFileLocationForDir(entryPath: string) {
  const metaFolder = getMetaDirectoryPath(entryPath);
  return metaFolder + AppConfig.dirSeparator + AppConfig.metaFolderFile;
}

export function extractFileName(filePath: string, dirSeparator: string = AppConfig.dirSeparator): string {
  return filePath.substring(filePath.lastIndexOf(dirSeparator) + 1, filePath.length);
}

export function cleanTrailingDirSeparator(dirPath: string): string {
  if (dirPath) {
    if (dirPath.lastIndexOf('\\') === dirPath.length - 1) {
      return dirPath.substring(0, dirPath.length - 1);
    } else if (dirPath.lastIndexOf('/') === dirPath.length - 1) {
      return dirPath.substring(0, dirPath.length - 1);
    }
    return dirPath;
  }
  console.error('Directory Path ' + dirPath + ' undefined');
  return '';
}

/**
 *
 * @param path -> root//subFolder/
 * @returns {string} -> root/subFolder
 */
export function normalizePath(path: string): string {
  return cleanTrailingDirSeparator(path.replace(/\/\//g, '/'));
}

export function extractFileNameWithoutExt(filePath: string): string {
  const fileName = extractFileName(filePath);
  const indexOfDot = fileName.lastIndexOf('.');
  const lastIndexBeginTagContainer = fileName.lastIndexOf(AppConfig.beginTagContainer);
  const lastIndexEndTagContainer = fileName.lastIndexOf(AppConfig.endTagContainer);
  if (lastIndexBeginTagContainer === 0 && lastIndexEndTagContainer + 1 === fileName.length) {
    // case: "[tag1 tag.2]"
    return '';
  } else if (indexOfDot > 0) {
    // case: regular
    return fileName.substring(0, indexOfDot);
  } else if (indexOfDot === 0) {
    // case ".txt"
    return '';
  }
  return fileName;
}

export function extractContainingDirectoryPath(filePath: string, dirSeparator: string = AppConfig.dirSeparator) {
  return filePath.substring(0, filePath.lastIndexOf(dirSeparator));
}

export function extractParentDirectoryPath(dirPath: string, dirSeparator: string = AppConfig.dirSeparator) {
  if (dirPath.endsWith(dirSeparator)) {
    dirPath = dirPath.substring(0, dirPath.lastIndexOf(dirSeparator));
  }
  return dirPath.substring(0, dirPath.lastIndexOf(dirSeparator));
}

export function extractDirectoryName(dirPath: string) {
  let directoryName;
  if (dirPath.endsWith(AppConfig.dirSeparator)) {
    directoryName = dirPath.substring(0, dirPath.lastIndexOf(AppConfig.dirSeparator));
  }
  directoryName = dirPath.substring(dirPath.lastIndexOf(AppConfig.dirSeparator) + 1, dirPath.length);
  return directoryName;
}

export function extractContainingDirectoryName(filePath: string) {
  const tmpStr = filePath.substring(0, filePath.lastIndexOf(AppConfig.dirSeparator));
  return tmpStr.substring(tmpStr.lastIndexOf(AppConfig.dirSeparator) + 1, tmpStr.length);
}

export function extractTitle(entryPath: string, isDirectory?: boolean = false) {
  let title;
  if (isDirectory) {
    title = extractDirectoryName(entryPath);
    return title;
  }
  title = extractFileNameWithoutExt(entryPath);

  const beginTagContainer = title.indexOf(AppConfig.beginTagContainer);
  const endTagContainer = title.lastIndexOf(AppConfig.endTagContainer);
  /* cases like "", "t", "["
      if( fileName.length <= 1) {
      // cases like "asd ] asd ["
      else if (beginTagContainer > endTagContainer) {
      // case: [ not found in the filename
      else if ( beginTagContainer < 0 )
      else if ( endTagContainer < 0 ) */
  if (beginTagContainer >= 0 && beginTagContainer < endTagContainer) {
    if (beginTagContainer === 0 && endTagContainer === title.trim().length) {
      // case: "[tag1, tag2]"
      return '';
    } else if (endTagContainer === title.trim().length) {
      // case: "asd[tag1, tag2]"
      return title.slice(0, beginTagContainer);
    }
    // case: "title1 [tag1 tag2] title2"
    return title.slice(0, beginTagContainer) + title.slice(endTagContainer + 1, title.length);
  }
  try {
    title = decodeURIComponent(title);
  } catch (e) {
    console.warn('Decoding URI failed on: ' + title + ' with ' + e);
  }
  return title;
}

export function extractTagsAsObjects(filePath: string, tagDelimiter: string): Array<Tag> {
  const tagsInFileName = extractTags(filePath, tagDelimiter);
  const tagArray = [];
  tagsInFileName.map((tag) => {
    tagArray.push({
      title: '' + tag,
      type: 'plain'
    });
    return true;
  });
  return tagArray;
}

export function extractTags(filePath: string, tagDelimiter: string) {
  if (tagDelimiter === undefined) { // TODO get tagDelimiter from settings reducer only
    // eslint-disable-next-line no-param-reassign
    tagDelimiter = AppConfig.tagDelimiter;
  }
  // console.log('Extracting tags from: ' + filePath);
  const fileName = extractFileName(filePath);
  // WithoutExt
  let tags = [];
  const beginTagContainer = fileName.indexOf(AppConfig.beginTagContainer);
  const endTagContainer = fileName.indexOf(AppConfig.endTagContainer);
  if (beginTagContainer < 0 || endTagContainer < 0 || beginTagContainer >= endTagContainer) {
    // console.log('Filename does not contains tags. Aborting extraction.');
    return tags;
  }
  const cleanedTags = [];
  const tagContainer = fileName.slice(beginTagContainer + 1, endTagContainer).trim();
  tags = tagContainer.split(tagDelimiter);
  for (let i = 0; i < tags.length; i += 1) {
    // Min tag length set to 1 character
    if (tags[i].trim().length > 0) {
      cleanedTags.push(tags[i]);
    }
  }
  return cleanedTags;
}
<|endoftext|>


<commit_before>/* global ciscospark */
import React, {Component} from 'react';
import classNames from 'classnames';
import {Cookies, withCookies} from 'react-cookie';
import {instanceOf} from 'prop-types';
import {autobind} from 'core-decorators';

import {Tabs, Tab} from 'material-ui/Tabs';
import RaisedButton from 'material-ui/RaisedButton';
import AppBar from 'material-ui/AppBar';
import Toggle from 'material-ui/Toggle';
import {RadioButtonGroup, RadioButton} from 'material-ui/RadioButton';
import {Card, CardActions, CardTitle, CardText} from 'material-ui/Card';
import Checkbox from 'material-ui/Checkbox';

import SyntaxHighlighter from 'react-syntax-highlighter/prism';
import {coy} from 'react-syntax-highlighter/styles/prism';

import TokenInput from '../token-input';

import styles from './styles.css';

const MODE_ONE_ON_ONE = 'email';
const MODE_SPACE = 'spaceId';

const widgetElementId = 'my-ciscospark-widget';

class DemoWidgetSpace extends Component {
  constructor(props) {
    super(props);
    const {cookies} = this.props;
    const toPersonEmail = cookies.get('toPersonEmail') || '';
    const spaceId = cookies.get('spaceId') || '';
    let mode;
    if (spaceId) {
      mode = MODE_SPACE;
    }
    else if (toPersonEmail) {
      mode = MODE_ONE_ON_ONE;
    }
    this.state = {
      accessToken: cookies.get('accessToken') || '',
      activities: {
        files: true,
        meet: true,
        message: true,
        people: true
      },
      displayToken: false,
      initialActivity: 'message',
      mode,
      running: false,
      toPersonEmail,
      spaceId
    };
  }

  shouldComponentUpdate() {
    return true;
  }

  @autobind
  handleSubmit(e) {
    e.preventDefault();
    const {cookies} = this.props;
    cookies.set('accessToken', this.state.accessToken);
    if (this.state.mode === MODE_ONE_ON_ONE) {
      cookies.set('toPersonEmail', this.state.toPersonEmail);
    }
    else {
      cookies.set('spaceId', this.state.spaceId);
    }
    const destinationId = this.state.mode === MODE_ONE_ON_ONE ? this.state.toPersonEmail : this.state.spaceId;
    const widgetEl = document.getElementById(widgetElementId);
    ciscospark.widget(widgetEl).spaceWidget({
      accessToken: this.state.accessToken,
      onEvent: (eventName, detail) => {
        window.ciscoSparkEvents.push({eventName, detail});
      },
      destinationId,
      destinationType: this.state.mode,
      initialActivity: this.state.initialActivity,
      spaceActivities: this.state.activities
    });

    this.setState({running: true});
  }

  @autobind
  handleRemove() {
    const widgetEl = document.getElementById(widgetElementId);
    ciscospark.widget(widgetEl).remove();
    this.setState({running: false});
  }

  @autobind
  handleAccessTokenChange(accessToken) {
    return this.setState({accessToken});
  }

  @autobind
  handleDisplayAccessTokenChange(e, displayToken) {
    return this.setState({displayToken});
  }

  @autobind
  handleEmailChange(e) {
    return this.setState({toPersonEmail: e.target.value});
  }

  @autobind
  handleInitialActivityChange(e, value) {
    return this.setState({initialActivity: value});
  }

  @autobind
  handleModeChange(e, value) {
    return this.setState({mode: value});
  }

  @autobind
  handleActivitiesChange(event, isInputChecked) {
    const {activities} = this.state;
    activities[event.target.value] = isInputChecked;
    return this.setState({activities});
  }

  @autobind
  handleSpaceChange(e) {
    return this.setState({spaceId: e.target.value});
  }

  generateExampleCode() {
    const {
      accessToken, activities, displayToken, spaceId, toPersonEmail
    } = this.state;
    const displayedAccessToken = displayToken ? accessToken : 'YOUR_ACCESS_TOKEN';
    const globalActivityTypesField = `activities: ${JSON.stringify(activities)}`;
    let globalDestinationIdField, globalDestinationTypeField, inlineDestinationIdField, inlineDestinationTypeField;
    if (this.state.mode === MODE_ONE_ON_ONE) {
      inlineDestinationIdField = `data-destination-id="${toPersonEmail || 'TO_PERSON_EMAIL'}"`;
      inlineDestinationTypeField = 'data-destination-type="email"';
      globalDestinationIdField = `destinationId: '${toPersonEmail || 'TO_PERSON_EMAIL'}'`;
      globalDestinationTypeField = 'destinationType: "email"';
    }
    else {
      globalDestinationIdField = `destinationId: '${spaceId || 'SPACE_ID'}'`;
      globalDestinationTypeField = 'destinationType: "spaceId"';
      inlineDestinationIdField = `data-destination-id="${spaceId || 'SPACE_ID'}"`;
      inlineDestinationTypeField = 'data-destination-type="spaceId"';
    }

    const inlineCode = `
`;
    const globalCode = `

`;
    return {
      globalCode,
      inlineCode
    };
  }

  render() {
    const displayActivityWarning = !this.state.activities[this.state.initialActivity];
    const hasToPerson = this.state.mode === MODE_ONE_ON_ONE && this.state.toPersonEmail;
    const hasToSpace = this.state.mode === MODE_SPACE && this.state.spaceId;
    const loadButtonEnabled = this.state.accessToken && (hasToPerson || hasToSpace);
    const {globalCode, inlineCode} = this.generateExampleCode(this.state);
    const componentContainerClassNames = [
      styles.widgetComponentContainer
    ];
    if (!this.state.running) {
      componentContainerClassNames.push(styles.hidden);
    }
    // eslint-disable-reason should not apply to jsx/html markup
    /* eslint-disable max-len */
    return (

        <AppBar title="Cisco Spark Space Widget" />

          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="The Spark Space widget allows developers to easily incorporate Cisco Spark Space messaging into an application."
              title="Cisco Spark Space Widget Demo"
            />
            <CardText expandable>
              This widget handles coordination between your application and the Spark APIs, and provides components of the Spark space experience without having to build all of the front end UI yourself.

              Our widget is built using React, Redux, and the Spark Javascript SDK .
            </CardText>
          </Card>
          <TokenInput onLogin={this.handleAccessTokenChange} token={this.state.accessToken} />
          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="Cisco Spark Space Widget can open to a space or person."
              title="Choose Widget Destination"
            />
            <CardText expandable>

 Widget Destination Type

                <RadioButtonGroup
                  aria-label="Widget 'To' Type"
                  name="toType"
                  onChange={this.handleModeChange}
                  valueSelected={this.state.mode}
                >
                  <RadioButton
                    aria-label="To Space"
                    label="To Space"
                    value={MODE_SPACE}
                  />
                  <RadioButton
                    aria-label="To Person"
                    label="To Person"
                    value={MODE_ONE_ON_ONE}
                  />
                </RadioButtonGroup>

 Widget Initial Activity

                <RadioButtonGroup
                  aria-label="Initial Activity"
                  name="initialActivity"
                  onChange={this.handleInitialActivityChange}
                  valueSelected={this.state.initialActivity}
                >
                  <RadioButton
                    aria-label="Message"
                    label="Message"
                    value="message"
                  />
                  <RadioButton
                    aria-label="Meet"
                    label="Meet"
                    value="meet"
                  />
                </RadioButtonGroup>

 Widget Activities

                {
                  displayActivityWarning &&

 Warning: The initial activity is not enabled

                }
                <Checkbox
                  checked={this.state.activities.files}
                  label="Files"
                  onCheck={this.handleActivitiesChange}
                  value="files"
                />
                <Checkbox
                  checked={this.state.activities.meet}
                  label="Meet"
                  onCheck={this.handleActivitiesChange}
                  value="meet"
                />
                <Checkbox
                  checked={this.state.activities.message}
                  label="Message"
                  onCheck={this.handleActivitiesChange}
                  value="message"
                />
                <Checkbox
                  checked={this.state.activities.people}
                  label="People"
                  onCheck={this.handleActivitiesChange}
                  value="people"
                />

                {
                  this.state.mode === MODE_SPACE &&

                    <input
                      aria-label="To Space ID"
                      className={styles.textInput}
                      id="toSpaceId"
                      onChange={this.handleSpaceChange}
                      placeholder="Spark Space Id"
                      value={this.state.spaceId}
                    />

                }
                {
                  this.state.mode === MODE_ONE_ON_ONE &&

                    <input
                      aria-label="To User Email"
                      className={styles.textInput}
                      id="toUserEmail"
                      onChange={this.handleEmailChange}
                      placeholder="Spark User Email (For 1:1)"
                      value={this.state.toPersonEmail}
                    />

                }

            </CardText>
            <CardActions expandable>
              <RaisedButton
                aria-label="Open Widget"
                disabled={!loadButtonEnabled}
                id="openWidgetButton"
                label="Open Embedded Widget"
                onClick={this.handleSubmit}
                primary
              />
              <RaisedButton
                aria-label="Remove Widget"
                disabled={!this.state.running}
                id="removeWidgetButton"
                label="Remove Embedded Widget"
                onClick={this.handleRemove}
                primary
              />
            </CardActions>
          </Card>
          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="Cisco Spark Space Widget can be implemented multiple ways."
              title="Widget Example Code"
            />
            <CardText expandable>

                <Tabs>
                  <Tab label="Inline Mode">

                      <SyntaxHighlighter language="jsx" style={coy}>
                        {inlineCode}
                      </SyntaxHighlighter>

                  </Tab>
                  <Tab label="Global Mode">

                      <SyntaxHighlighter language="jsx" style={coy}>
                        {globalCode}
                      </SyntaxHighlighter>

                  </Tab>
                </Tabs>

                    <Toggle
                      label="Display Access Token"
                      labelPosition="right"
                      onToggle={this.handleDisplayAccessTokenChange}
                    />

            </CardText>
          </Card>

    );
  }
}

DemoWidgetSpace.propTypes = {
  cookies: instanceOf(Cookies).isRequired
};

DemoWidgetSpace.title = 'Widget Space';
DemoWidgetSpace.path = '/widget-space-demo';

export default withCookies(DemoWidgetSpace);
<commit_msg>feat(widget-space-demo): save activities and initialActivity in cookies<commit_after>/* global ciscospark */
import React, {Component} from 'react';
import classNames from 'classnames';
import {Cookies, withCookies} from 'react-cookie';
import {instanceOf} from 'prop-types';
import {autobind} from 'core-decorators';

import {Tabs, Tab} from 'material-ui/Tabs';
import RaisedButton from 'material-ui/RaisedButton';
import AppBar from 'material-ui/AppBar';
import Toggle from 'material-ui/Toggle';
import {RadioButtonGroup, RadioButton} from 'material-ui/RadioButton';
import {Card, CardActions, CardTitle, CardText} from 'material-ui/Card';
import Checkbox from 'material-ui/Checkbox';

import SyntaxHighlighter from 'react-syntax-highlighter/prism';
import {coy} from 'react-syntax-highlighter/styles/prism';

import TokenInput from '../token-input';

import styles from './styles.css';

const MODE_ONE_ON_ONE = 'email';
const MODE_SPACE = 'spaceId';

const widgetElementId = 'my-ciscospark-widget';

class DemoWidgetSpace extends Component {
  constructor(props) {
    super(props);
    const {cookies} = this.props;
    const toPersonEmail = cookies.get('toPersonEmail') || '';
    const spaceId = cookies.get('spaceId') || '';
    let mode;
    if (spaceId) {
      mode = MODE_SPACE;
    }
    else if (toPersonEmail) {
      mode = MODE_ONE_ON_ONE;
    }
    this.state = {
      accessToken: cookies.get('accessToken') || '',
      activities: cookies.get('activities') || {
        files: true,
        meet: true,
        message: true,
        people: true
      },
      displayToken: false,
      initialActivity: cookies.get('initialActivity') || 'message',
      mode,
      running: false,
      toPersonEmail,
      spaceId
    };
  }

  shouldComponentUpdate() {
    return true;
  }

  @autobind
  handleSubmit(e) {
    e.preventDefault();
    const {cookies} = this.props;
    cookies.set('accessToken', this.state.accessToken);
    cookies.set('activities', this.state.activities);
    cookies.set('initialActivity', this.state.initialActivity);
    if (this.state.mode === MODE_ONE_ON_ONE) {
      cookies.set('toPersonEmail', this.state.toPersonEmail);
    }
    else {
      cookies.set('spaceId', this.state.spaceId);
    }
    const destinationId = this.state.mode === MODE_ONE_ON_ONE ? this.state.toPersonEmail : this.state.spaceId;
    const widgetEl = document.getElementById(widgetElementId);
    ciscospark.widget(widgetEl).spaceWidget({
      accessToken: this.state.accessToken,
      onEvent: (eventName, detail) => {
        window.ciscoSparkEvents.push({eventName, detail});
      },
      destinationId,
      destinationType: this.state.mode,
      initialActivity: this.state.initialActivity,
      spaceActivities: this.state.activities
    });

    this.setState({running: true});
  }

  @autobind
  handleRemove() {
    const widgetEl = document.getElementById(widgetElementId);
    ciscospark.widget(widgetEl).remove();
    this.setState({running: false});
  }

  @autobind
  handleAccessTokenChange(accessToken) {
    return this.setState({accessToken});
  }

  @autobind
  handleDisplayAccessTokenChange(e, displayToken) {
    return this.setState({displayToken});
  }

  @autobind
  handleEmailChange(e) {
    return this.setState({toPersonEmail: e.target.value});
  }

  @autobind
  handleInitialActivityChange(e, value) {
    return this.setState({initialActivity: value});
  }

  @autobind
  handleModeChange(e, value) {
    return this.setState({mode: value});
  }

  @autobind
  handleActivitiesChange(event, isInputChecked) {
    const {activities} = this.state;
    activities[event.target.value] = isInputChecked;
    return this.setState({activities});
  }

  @autobind
  handleSpaceChange(e) {
    return this.setState({spaceId: e.target.value});
  }

  generateExampleCode() {
    const {
      accessToken, activities, displayToken, spaceId, toPersonEmail
    } = this.state;
    const displayedAccessToken = displayToken ? accessToken : 'YOUR_ACCESS_TOKEN';
    const globalActivityTypesField = `activities: ${JSON.stringify(activities)}`;
    let globalDestinationIdField, globalDestinationTypeField, inlineDestinationIdField, inlineDestinationTypeField;
    if (this.state.mode === MODE_ONE_ON_ONE) {
      inlineDestinationIdField = `data-destination-id="${toPersonEmail || 'TO_PERSON_EMAIL'}"`;
      inlineDestinationTypeField = 'data-destination-type="email"';
      globalDestinationIdField = `destinationId: '${toPersonEmail || 'TO_PERSON_EMAIL'}'`;
      globalDestinationTypeField = 'destinationType: "email"';
    }
    else {
      globalDestinationIdField = `destinationId: '${spaceId || 'SPACE_ID'}'`;
      globalDestinationTypeField = 'destinationType: "spaceId"';
      inlineDestinationIdField = `data-destination-id="${spaceId || 'SPACE_ID'}"`;
      inlineDestinationTypeField = 'data-destination-type="spaceId"';
    }

    const inlineCode = `
`;
    const globalCode = `

`;
    return {
      globalCode,
      inlineCode
    };
  }

  render() {
    const displayActivityWarning = !this.state.activities[this.state.initialActivity];
    const hasToPerson = this.state.mode === MODE_ONE_ON_ONE && this.state.toPersonEmail;
    const hasToSpace = this.state.mode === MODE_SPACE && this.state.spaceId;
    const loadButtonEnabled = this.state.accessToken && (hasToPerson || hasToSpace);
    const {globalCode, inlineCode} = this.generateExampleCode(this.state);
    const componentContainerClassNames = [
      styles.widgetComponentContainer
    ];
    if (!this.state.running) {
      componentContainerClassNames.push(styles.hidden);
    }
    // eslint-disable-reason should not apply to jsx/html markup
    /* eslint-disable max-len */
    return (

        <AppBar title="Cisco Spark Space Widget" />

          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="The Spark Space widget allows developers to easily incorporate Cisco Spark Space messaging into an application."
              title="Cisco Spark Space Widget Demo"
            />
            <CardText expandable>
              This widget handles coordination between your application and the Spark APIs, and provides components of the Spark space experience without having to build all of the front end UI yourself.

              Our widget is built using React, Redux, and the Spark Javascript SDK .
            </CardText>
          </Card>
          <TokenInput onLogin={this.handleAccessTokenChange} token={this.state.accessToken} />
          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="Cisco Spark Space Widget can open to a space or person."
              title="Choose Widget Destination"
            />
            <CardText expandable>

 Widget Destination Type

                <RadioButtonGroup
                  aria-label="Widget 'To' Type"
                  name="toType"
                  onChange={this.handleModeChange}
                  valueSelected={this.state.mode}
                >
                  <RadioButton
                    aria-label="To Space"
                    label="To Space"
                    value={MODE_SPACE}
                  />
                  <RadioButton
                    aria-label="To Person"
                    label="To Person"
                    value={MODE_ONE_ON_ONE}
                  />
                </RadioButtonGroup>

 Widget Initial Activity

                <RadioButtonGroup
                  aria-label="Initial Activity"
                  name="initialActivity"
                  onChange={this.handleInitialActivityChange}
                  valueSelected={this.state.initialActivity}
                >
                  <RadioButton
                    aria-label="Message"
                    label="Message"
                    value="message"
                  />
                  <RadioButton
                    aria-label="Meet"
                    label="Meet"
                    value="meet"
                  />
                </RadioButtonGroup>

 Widget Activities

                {
                  displayActivityWarning &&

 Warning: The initial activity is not enabled

                }
                <Checkbox
                  checked={this.state.activities.files}
                  label="Files"
                  onCheck={this.handleActivitiesChange}
                  value="files"
                />
                <Checkbox
                  checked={this.state.activities.meet}
                  label="Meet"
                  onCheck={this.handleActivitiesChange}
                  value="meet"
                />
                <Checkbox
                  checked={this.state.activities.message}
                  label="Message"
                  onCheck={this.handleActivitiesChange}
                  value="message"
                />
                <Checkbox
                  checked={this.state.activities.people}
                  label="People"
                  onCheck={this.handleActivitiesChange}
                  value="people"
                />

                {
                  this.state.mode === MODE_SPACE &&

                    <input
                      aria-label="To Space ID"
                      className={styles.textInput}
                      id="toSpaceId"
                      onChange={this.handleSpaceChange}
                      placeholder="Spark Space Id"
                      value={this.state.spaceId}
                    />

                }
                {
                  this.state.mode === MODE_ONE_ON_ONE &&

                    <input
                      aria-label="To User Email"
                      className={styles.textInput}
                      id="toUserEmail"
                      onChange={this.handleEmailChange}
                      placeholder="Spark User Email (For 1:1)"
                      value={this.state.toPersonEmail}
                    />

                }

            </CardText>
            <CardActions expandable>
              <RaisedButton
                aria-label="Open Widget"
                disabled={!loadButtonEnabled}
                id="openWidgetButton"
                label="Open Embedded Widget"
                onClick={this.handleSubmit}
                primary
              />
              <RaisedButton
                aria-label="Remove Widget"
                disabled={!this.state.running}
                id="removeWidgetButton"
                label="Remove Embedded Widget"
                onClick={this.handleRemove}
                primary
              />
            </CardActions>
          </Card>
          <Card initiallyExpanded style={{margin: '10px'}}>
            <CardTitle
              actAsExpander
              showExpandableButton
              subtitle="Cisco Spark Space Widget can be implemented multiple ways."
              title="Widget Example Code"
            />
            <CardText expandable>

                <Tabs>
                  <Tab label="Inline Mode">

                      <SyntaxHighlighter language="jsx" style={coy}>
                        {inlineCode}
                      </SyntaxHighlighter>

                  </Tab>
                  <Tab label="Global Mode">

                      <SyntaxHighlighter language="jsx" style={coy}>
                        {globalCode}
                      </SyntaxHighlighter>

                  </Tab>
                </Tabs>

                    <Toggle
                      label="Display Access Token"
                      labelPosition="right"
                      onToggle={this.handleDisplayAccessTokenChange}
                    />

            </CardText>
          </Card>

    );
  }
}

DemoWidgetSpace.propTypes = {
  cookies: instanceOf(Cookies).isRequired
};

DemoWidgetSpace.title = 'Widget Space';
DemoWidgetSpace.path = '/widget-space-demo';

export default withCookies(DemoWidgetSpace);
<|endoftext|>


<commit_before>import * as clickTracking from './clickTracking';

const document = global.document;
global.document = {
	body: {
		clientWidth: 100,
	}
};

describe('trackStopPropagation', () => {
	it('adds Event.prototype.stopPropAndTrack', () => {
		clickTracking.trackStopPropagation(() => {});
		expect(Event.prototype.stopPropAndTrack).toBeDefined();
	});
	it('overrides Event.prototype.stopPropagation', () => {
		clickTracking.trackStopPropagation(() => {});
	});
	it('calls supplied event handler on stopPropagation', () => {
		const spyable = {
			dummyListener() {}
		};
		spyOn(spyable, 'dummyListener');
		clickTracking.trackStopPropagation(spyable.dummyListener);
		const evt = document.createEvent('HTMLEvents');
		evt.initEvent('click', false, true);
		document.body.dispatchEvent(evt);
		evt.stopPropagation();
		expect(spyable.dummyListener).toHaveBeenCalled();
	});
	describe('stopPropAndTrack', () => {
		it('calls trackClick', () => {
			const spyable = {
				dummyListener() {}
			};
			spyOn(spyable, 'dummyListener');
			clickTracking.trackStopPropagation(spyable.dummyListener);
			const evt = document.createEvent('HTMLEvents');
			evt.initEvent('click', false, true);
			document.body.dispatchEvent(evt);
			evt.stopPropAndTrack();
			expect(spyable.dummyListener).toHaveBeenCalled();
		});
	});
});

global.document = document;

<commit_msg>beforeeach/aftereach<commit_after>import * as clickTracking from './clickTracking';

describe('trackStopPropagation', () => {
	let document;
	beforeEach(() => {
		document = global.document;
		global.document = {
			body: {
				clientWidth: 100,
			}
		};
	});
	afterEach(() => {
		global.document = document;
	});
	it('adds Event.prototype.stopPropAndTrack', () => {
		clickTracking.trackStopPropagation(() => {});
		expect(Event.prototype.stopPropAndTrack).toBeDefined();
	});
	it('overrides Event.prototype.stopPropagation', () => {
		clickTracking.trackStopPropagation(() => {});
	});
	it('calls supplied event handler on stopPropagation', () => {
		const spyable = {
			dummyListener() {}
		};
		spyOn(spyable, 'dummyListener');
		clickTracking.trackStopPropagation(spyable.dummyListener);
		const evt = document.createEvent('HTMLEvents');
		evt.initEvent('click', false, true);
		document.body.dispatchEvent(evt);
		evt.stopPropagation();
		expect(spyable.dummyListener).toHaveBeenCalled();
	});
	describe('stopPropAndTrack', () => {
		it('calls trackClick', () => {
			const spyable = {
				dummyListener() {}
			};
			spyOn(spyable, 'dummyListener');
			clickTracking.trackStopPropagation(spyable.dummyListener);
			const evt = document.createEvent('HTMLEvents');
			evt.initEvent('click', false, true);
			document.body.dispatchEvent(evt);
			evt.stopPropAndTrack();
			expect(spyable.dummyListener).toHaveBeenCalled();
		});
	});
	global.document = document;
});

<|endoftext|>


<commit_before>/**
 * Copyright 2018 The AMP HTML 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.
 */

import {dict} from '../src/utils/object';
import {getData, listen} from '../src/event-helper';
import {getMode} from '../src/mode';
import {isFiniteNumber} from '../src/types';
import {once} from '../src/utils/function';
import {tryParseJson} from '../src/json';
import {tryResolve} from '../src/utils/promise';

/** @fileoverview Entry point for documents inside an <amp-video-iframe>. */

/**
 * @typedef {{
 *   sourceUrl: string,
 *   canonicalUrl: string,
 * }}
 */
let DocMetadataDef;

/**
 * @typedef {{
 *   on: function(string, function()),
 *   play: function(),
 *   pause: function(),
 *   setMuted: function(boolean),
 *   setControls: function(boolean),
 *   setFullscreen: function(boolean),
 * }}
 */
let JwplayerPartialInterfaceDef;

/**
 * @param {T} shouldBeTrueish
 * @param {...*} args
 * @return {T}
 * @template T
 */
function userAssert(shouldBeTrueish, ...args) {
  if (!shouldBeTrueish) {
    throw new Error(args.join(' '));
  }
  return shouldBeTrueish;
}

const validMethods = [
  'pause',
  'play',
  'mute',
  'unmute',
  'fullscreenenter',
  'fullscreenexit',
  'showcontrols',
  'hidecontrols',
];

const validEvents = [
  'canplay',
  'load',
  'playing',
  'pause',
  'ended',
  'muted',
  'unmuted',
];

/** @visibleForTesting */
export class AmpVideoIntegration {

  /** @param {!Window} win */
  constructor(win) {

    /**
     * Used for checking callback return type.
     * @visibleForTesting
     */
    this.isAmpVideoIntegration_ = true;

    /** @private @const */
    this.callCounter_ = 0;

    /** @private @const {!Object<number, function()>} */
    this.callbacks_ = {};

    /** @private @const {!Window} */
    this.win_ = win;

    /** @private @const {!Object<string, function()>} */
    this.methods_ = {};

    /** @private @const {function()} */
    this.listenToOnce_ = once(() => {
      listenTo(this.win_, e => this.onMessage_(e));
    });

    /** @private {boolean} */
    this.muted_ = false;

    /**
     * @return {!DocMetadataDef}
     * @private
     */
    this.getMetadataOnce_ = once(() => {
      const {canonicalUrl, sourceUrl} = tryParseJson(this.win_.name);
      return {canonicalUrl, sourceUrl};
    });
  }

  /** @return {!DocMetadataDef} */
  getMetadata() {
    return this.getMetadataOnce_();
  }

  /**
   * @param {string} name
   * @param {function()} callback
   */
  method(name, callback) {
    userAssert(validMethods.indexOf(name) > -1, `Invalid method ${name}.`);

    const wrappedCallback =
        (name == 'play' || name == 'pause') ?
          this.safePlayOrPause_(callback) :
          callback;

    this.methods_[name] = wrappedCallback;
    this.listenToOnce_();
  }

  /**
   * @param {!JsonObject} data
   * @private
   */
  onMessage_(data) {
    const id = data['id'];
    if (isFiniteNumber(id) && this.callbacks_[id]) {
      this.handleResponse_(id, data['args']);
      return;
    }

    if (data['event'] == 'method') {
      this.maybeExecute_(data['method']);
    }
  }

  /**
   * @param {number} id
   * @param {!JsonObject} args
   * @private
   */
  handleResponse_(id, args) {
    const callback = this.callbacks_[id];
    callback(args);
    delete this.callbacks_[id];
  }

  /**
   * @param {string} method
   * @private
   */
  maybeExecute_(method) {
    if (!(method in this.methods_)) {
      return;
    }
    this.methods_[method].call();
  }

  /**
   * @param {string} type
   * @param {*} obj
   */
  listenTo(type, obj) {
    switch (type.toLowerCase()) {
      case 'jwplayer':
        this.listenToJwPlayer_(obj);
        break;
      case 'videojs':
        this.listenToVideoJs_(obj);
        break;
      default:
        userAssert(false, `Invalid listener type ${type}.`);
    }
  }

  /**
   * @param {!JwplayerPartialInterfaceDef} player
   * @private
   */
  listenToJwPlayer_(player) {
    ['error', 'setupError'].forEach(e => {
      player.on(e, () => {
        userAssert.apply(null, [false].concat(arguments));
        this.postEvent('error');
      });
    });

    ['adSkipped', 'adComplete', 'adError'].forEach(e => {
      player.on(e, () => this.postEvent('ad_end'));
    });

    player.on('adStarted', () => this.postEvent('ad_start'));

    const redispatchAs = {
      'play': 'playing',
      'ready': 'canplay',
      'pause': 'pause',
    };

    Object.keys(redispatchAs).forEach(e => {
      player.on(e, () => this.postEvent(redispatchAs[e]));
    });

    player.on('volume', e => this.onVolumeChange_(e.volume));

    this.method('play', () => player.play());
    this.method('pause', () => player.pause());
    this.method('mute', () => player.setMute(true));
    this.method('unmute', () => player.setMute(false));
    this.method('showcontrols', () => player.setControls(true));
    this.method('hidecontrols', () => player.setControls(false));
    this.method('fullscreenenter', () => player.setFullscreen(true));
    this.method('fullscreenexit', () => player.setFullscreen(false));
  }

  /**
   * @param {!Element} element
   * @private
   */
  listenToVideoJs_(element) {
    userAssert(this.win_.videojs, 'Video.JS not imported.');

    // Retrieve lazily.
    const player = once(() =>
      this.win_.videojs.getPlayer(element));

    ['canplay', 'playing', 'pause', 'ended'].forEach(e => {
      listen(element, e, () => this.postEvent(e));
    });

    listen(element, 'volumechange', () =>
      this.onVolumeChange_(player().volume()));

    this.method('play', () => player().play());
    this.method('pause', () => player().pause());
    this.method('mute', () => player().muted(true));
    this.method('unmute', () => player().muted(false));
    this.method('showcontrols', () => player().controls(true));
    this.method('hidecontrols', () => player().controls(false));
    this.method('fullscreenenter', () => player().requestFullscreen());
    this.method('fullscreenexit', () => player().exitFullscreen());
  }

  /**
   * @param {number} newVolume
   * @private
   */
  onVolumeChange_(newVolume) {
    if (newVolume < 0.01) {
      this.muted_ = true;
      this.postEvent('muted');
      return;
    }
    if (this.muted_) {
      this.muted_ = false;
      this.postEvent('unmuted');
    }
  }

  /**
   * @param {function()} callback
   * @private
   */
  safePlayOrPause_(callback) {
    return () => {
      try {
        tryResolve(() => callback());
      } catch (e) {
        // OK to dismiss errors as they are expected.
      }
    };
  }

  /**
   * Posts a playback event.
   * @param {string} event
   */
  postEvent(event) {
    userAssert(validEvents.indexOf(event) > -1, `Invalid event ${event}`);
    this.postToParent_(dict({'event': event}));
  }

  /**
   * @param {!JsonObject} data
   * @param {function()=} opt_callback
   * @private
   */
  postToParent_(data, opt_callback) {
    const id = this.callCounter_++;
    const completeData = Object.assign({id}, data);

    if (!getMode(this.win_).test && this.win_.parent) {
      this.win_.parent./*OK*/postMessage(completeData, '*');
    }

    if (opt_callback) {
      this.callbacks_[id] = opt_callback;
    }
    return id;
  }

  /**
   * Gets the video's intersection with the document's viewport.
   * @param {function(!JsonObject)} callback
   */
  getIntersection(callback) {
    this.listenToOnce_();
    this.getIntersectionForTesting_(callback);
  }

  /**
   * Returns message id for testing. Private as message id is an implementation
   * detail that others should not rely on.
   * @param {function(!JsonObject)} callback
   * @private
   */
  getIntersectionForTesting_(callback) {
    return this.postToParent_(dict({'method': 'getIntersection'}), callback);
  }
}

/**
 * @param {!Window} win
 * @param {function(!JsonObject)} onMessage
 */
function listenTo(win, onMessage) {
  listen(win, 'message', e => onMessage(tryParseJson(getData(e))));
}

/**
 * Adopt window asynchronously.
 * This follows the same method as AMP.push (see runtime).
 * @param {!Window} global
 * @visibleForTesting
 */
export function adopt(global) {
  // Hacky way to make AMP errors (e.g. from listenFor) do *something*.
  global.reportError = console.error.bind(console);

  // Initialize one object per frame.
  const integration = new AmpVideoIntegration(global);

  // Create array if the script loaded before callback push
  // (otherwise already created).
  const callbacks = (global.AmpVideoIframe = global.AmpVideoIframe || []);

  // Rewrite push to execute callbacks are added after adoption.
  callbacks.push = callback => callback(integration);

  // Execute callbacks created before adoption.
  callbacks.forEach(callbacks.push);
}

if (!getMode(self).test) {
  adopt(self);
}
<commit_msg>🐛 `amp-video-iframe`: Ignore postMessages that aren't valid JSON. (#18803)<commit_after>/**
 * Copyright 2018 The AMP HTML 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.
 */

import {dict} from '../src/utils/object';
import {getData, listen} from '../src/event-helper';
import {getMode} from '../src/mode';
import {isFiniteNumber} from '../src/types';
import {once} from '../src/utils/function';
import {tryParseJson} from '../src/json';
import {tryResolve} from '../src/utils/promise';

/** @fileoverview Entry point for documents inside an <amp-video-iframe>. */

/**
 * @typedef {{
 *   sourceUrl: string,
 *   canonicalUrl: string,
 * }}
 */
let DocMetadataDef;

/**
 * @typedef {{
 *   on: function(string, function()),
 *   play: function(),
 *   pause: function(),
 *   setMuted: function(boolean),
 *   setControls: function(boolean),
 *   setFullscreen: function(boolean),
 * }}
 */
let JwplayerPartialInterfaceDef;

/**
 * @param {T} shouldBeTrueish
 * @param {...*} args
 * @return {T}
 * @template T
 */
function userAssert(shouldBeTrueish, ...args) {
  if (!shouldBeTrueish) {
    throw new Error(args.join(' '));
  }
  return shouldBeTrueish;
}

const validMethods = [
  'pause',
  'play',
  'mute',
  'unmute',
  'fullscreenenter',
  'fullscreenexit',
  'showcontrols',
  'hidecontrols',
];

const validEvents = [
  'canplay',
  'load',
  'playing',
  'pause',
  'ended',
  'muted',
  'unmuted',
];

/** @visibleForTesting */
export class AmpVideoIntegration {

  /** @param {!Window} win */
  constructor(win) {

    /**
     * Used for checking callback return type.
     * @visibleForTesting
     */
    this.isAmpVideoIntegration_ = true;

    /** @private @const */
    this.callCounter_ = 0;

    /** @private @const {!Object<number, function()>} */
    this.callbacks_ = {};

    /** @private @const {!Window} */
    this.win_ = win;

    /** @private @const {!Object<string, function()>} */
    this.methods_ = {};

    /** @private @const {function()} */
    this.listenToOnce_ = once(() => {
      listenTo(this.win_, e => this.onMessage_(e));
    });

    /** @private {boolean} */
    this.muted_ = false;

    /**
     * @return {!DocMetadataDef}
     * @private
     */
    this.getMetadataOnce_ = once(() => {
      const {canonicalUrl, sourceUrl} = tryParseJson(this.win_.name);
      return {canonicalUrl, sourceUrl};
    });
  }

  /** @return {!DocMetadataDef} */
  getMetadata() {
    return this.getMetadataOnce_();
  }

  /**
   * @param {string} name
   * @param {function()} callback
   */
  method(name, callback) {
    userAssert(validMethods.indexOf(name) > -1, `Invalid method ${name}.`);

    const wrappedCallback =
        (name == 'play' || name == 'pause') ?
          this.safePlayOrPause_(callback) :
          callback;

    this.methods_[name] = wrappedCallback;
    this.listenToOnce_();
  }

  /**
   * @param {!JsonObject} data
   * @private
   */
  onMessage_(data) {
    const id = data['id'];
    if (isFiniteNumber(id) && this.callbacks_[id]) {
      this.handleResponse_(id, data['args']);
      return;
    }

    if (data['event'] == 'method') {
      this.maybeExecute_(data['method']);
    }
  }

  /**
   * @param {number} id
   * @param {!JsonObject} args
   * @private
   */
  handleResponse_(id, args) {
    const callback = this.callbacks_[id];
    callback(args);
    delete this.callbacks_[id];
  }

  /**
   * @param {string} method
   * @private
   */
  maybeExecute_(method) {
    if (!(method in this.methods_)) {
      return;
    }
    this.methods_[method].call();
  }

  /**
   * @param {string} type
   * @param {*} obj
   */
  listenTo(type, obj) {
    switch (type.toLowerCase()) {
      case 'jwplayer':
        this.listenToJwPlayer_(obj);
        break;
      case 'videojs':
        this.listenToVideoJs_(obj);
        break;
      default:
        userAssert(false, `Invalid listener type ${type}.`);
    }
  }

  /**
   * @param {!JwplayerPartialInterfaceDef} player
   * @private
   */
  listenToJwPlayer_(player) {
    ['error', 'setupError'].forEach(e => {
      player.on(e, () => {
        userAssert.apply(null, [false].concat(arguments));
        this.postEvent('error');
      });
    });

    ['adSkipped', 'adComplete', 'adError'].forEach(e => {
      player.on(e, () => this.postEvent('ad_end'));
    });

    player.on('adStarted', () => this.postEvent('ad_start'));

    const redispatchAs = {
      'play': 'playing',
      'ready': 'canplay',
      'pause': 'pause',
    };

    Object.keys(redispatchAs).forEach(e => {
      player.on(e, () => this.postEvent(redispatchAs[e]));
    });

    player.on('volume', e => this.onVolumeChange_(e.volume));

    this.method('play', () => player.play());
    this.method('pause', () => player.pause());
    this.method('mute', () => player.setMute(true));
    this.method('unmute', () => player.setMute(false));
    this.method('showcontrols', () => player.setControls(true));
    this.method('hidecontrols', () => player.setControls(false));
    this.method('fullscreenenter', () => player.setFullscreen(true));
    this.method('fullscreenexit', () => player.setFullscreen(false));
  }

  /**
   * @param {!Element} element
   * @private
   */
  listenToVideoJs_(element) {
    userAssert(this.win_.videojs, 'Video.JS not imported.');

    // Retrieve lazily.
    const player = once(() =>
      this.win_.videojs.getPlayer(element));

    ['canplay', 'playing', 'pause', 'ended'].forEach(e => {
      listen(element, e, () => this.postEvent(e));
    });

    listen(element, 'volumechange', () =>
      this.onVolumeChange_(player().volume()));

    this.method('play', () => player().play());
    this.method('pause', () => player().pause());
    this.method('mute', () => player().muted(true));
    this.method('unmute', () => player().muted(false));
    this.method('showcontrols', () => player().controls(true));
    this.method('hidecontrols', () => player().controls(false));
    this.method('fullscreenenter', () => player().requestFullscreen());
    this.method('fullscreenexit', () => player().exitFullscreen());
  }

  /**
   * @param {number} newVolume
   * @private
   */
  onVolumeChange_(newVolume) {
    if (newVolume < 0.01) {
      this.muted_ = true;
      this.postEvent('muted');
      return;
    }
    if (this.muted_) {
      this.muted_ = false;
      this.postEvent('unmuted');
    }
  }

  /**
   * @param {function()} callback
   * @private
   */
  safePlayOrPause_(callback) {
    return () => {
      try {
        tryResolve(() => callback());
      } catch (e) {
        // OK to dismiss errors as they are expected.
      }
    };
  }

  /**
   * Posts a playback event.
   * @param {string} event
   */
  postEvent(event) {
    userAssert(validEvents.indexOf(event) > -1, `Invalid event ${event}`);
    this.postToParent_(dict({'event': event}));
  }

  /**
   * @param {!JsonObject} data
   * @param {function()=} opt_callback
   * @private
   */
  postToParent_(data, opt_callback) {
    const id = this.callCounter_++;
    const completeData = Object.assign({id}, data);

    if (!getMode(this.win_).test && this.win_.parent) {
      this.win_.parent./*OK*/postMessage(completeData, '*');
    }

    if (opt_callback) {
      this.callbacks_[id] = opt_callback;
    }
    return id;
  }

  /**
   * Gets the video's intersection with the document's viewport.
   * @param {function(!JsonObject)} callback
   */
  getIntersection(callback) {
    this.listenToOnce_();
    this.getIntersectionForTesting_(callback);
  }

  /**
   * Returns message id for testing. Private as message id is an implementation
   * detail that others should not rely on.
   * @param {function(!JsonObject)} callback
   * @private
   */
  getIntersectionForTesting_(callback) {
    return this.postToParent_(dict({'method': 'getIntersection'}), callback);
  }
}

/**
 * @param {!Window} win
 * @param {function(!JsonObject)} onMessage
 */
function listenTo(win, onMessage) {
  listen(win, 'message', e => {
    const message = tryParseJson(getData(e));
    if (!message) {
      // only process valid JSON.
      return;
    }
    onMessage(message);
  });
}

/**
 * Adopt window asynchronously.
 * This follows the same method as AMP.push (see runtime).
 * @param {!Window} global
 * @visibleForTesting
 */
export function adopt(global) {
  // Hacky way to make AMP errors (e.g. from listenFor) do *something*.
  global.reportError = console.error.bind(console);

  // Initialize one object per frame.
  const integration = new AmpVideoIntegration(global);

  // Create array if the script loaded before callback push
  // (otherwise already created).
  const callbacks = (global.AmpVideoIframe = global.AmpVideoIframe || []);

  // Rewrite push to execute callbacks are added after adoption.
  callbacks.push = callback => callback(integration);

  // Execute callbacks created before adoption.
  callbacks.forEach(callbacks.push);
}

if (!getMode(self).test) {
  adopt(self);
}
<|endoftext|>


<commit_before>/**
	Copyright (C) 2014  Matheus Borges Teixeira

	This is a part of Arkanos Organizer Suite (AOS) app.
	AOS is a web application for organizing personal goals.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

Ext.define('AOS.view.Forgot', {
		extend: 'Ext.Panel',
		alias: 'widget.principal',
		requires: [	'Ext.form.FieldSet',
					'Ext.field.Email',
					'Ext.Label'],
		config: {
			title: 'Forgot',
			items: [
				{
					xtype: 'titlebar',
					title: 'Reset Password',
					docked: 'top',
					items: [
						{
							xtype: 'button',
							text: 'Back',
							itemId: 'backButton',
							align: 'left',
							handler: function(){
								//TODO optmize
								var me = this.parent.parent.parent;
								me.fireEvent('switching','AOS.view.Login', { type: 'slide', direction: 'right' });
							}
						}
					]
				},
				{
					xtype: 'label',
					id: 'forgotErrorMessage',
					html: 'Login failed.',
					style: 'width:100%; text-align:center; color:#D44;',
					hidden: true,
					hideAnimation: 'fadeOut',
					showAnimation: 'fadeIn'
				},
				{
					xtype: 'fieldset',
					id: 'forgotInput',
					title: 'Please inform your email',
					items:[
						{
							xtype: 'emailfield',
							placeHolder: 'my@email.com',
							itemId: 'forgotEmail',
							name: 'forgotEmail',
							required: true
						}
					]
				},
				{
					xtype: 'button',
					ui: 'action',
					margin: '1%',
					text: 'Reset Password'
					//TODO api call to reset password
				}
			]
		}
	});
<commit_msg>Documenting Forgot.js<commit_after>/**
	Copyright (C) 2014  Matheus Borges Teixeira

	This is a part of Arkanos Organizer Suite (AOS) app.
	AOS is a web application for organizing personal goals.

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU Affero General Public License as
	published by the Free Software Foundation, either version 3 of the
	License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Affero General Public License for more details.

	You should have received a copy of the GNU Affero General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

/**
 * View for resetting password.
 */
Ext.define('AOS.view.Forgot', {
	extend: 'Ext.Panel',
	alias: 'widget.principal',
	requires: [
		'Ext.form.FieldSet',
		'Ext.field.Email',
		'Ext.Label'
	],
	config: {
		title: 'Forgot',
		items: [
			{
				xtype: 'titlebar',
				title: 'Reset Password',
				docked: 'top',
				items: [
					{
						xtype: 'button',
						text: 'Back',
						itemId: 'backButton',
						align: 'left',
						handler: function(){
							AOS.Helper.switchTo('AOS.view.Login', { type: 'slide', direction: 'right' });
						}
					}
				]
			},
			{
				xtype: 'label',
				id: 'forgotErrorMessage',
				html: 'Login failed.',
				style: 'width:100%; text-align:center; color:#D44;',
				hidden: true,
				hideAnimation: 'fadeOut',
				showAnimation: 'fadeIn'
			},
			{
				xtype: 'fieldset',
				id: 'forgotInput',
				title: 'Please inform your email',
				items:[
					{
						xtype: 'emailfield',
						placeHolder: 'my@email.com',
						itemId: 'forgotEmail',
						name: 'forgotEmail',
						required: true
					}
				]
			},
			{
				xtype: 'button',
				ui: 'action',
				margin: '1%',
				text: 'Reset Password'
				//TODO: API call to reset password
			}
		]
	}
});
<|endoftext|>


<commit_before>/* @flow */

import { Constants, bus, cache } from '../../core-server';
import ThreadRel from '../../models/threadrel';
import { textrel } from '../../models/models';
import log from 'winston';

bus.on('change', (changes, next) => {
	if (!changes.entities) {
		next();
		return;
	}

	for (const id in changes.entities) {
		const entity = changes.entities[id];
		let text, role, user;

		if (entity.type === Constants.TYPE_TEXT) {
			if (!entity.parents) return;
			const relationId = entity.creator + '_' + entity.parents[0];
			let promises;

			if (entity.body.match(/@\w+/g)) {
				const mentions = entity.body.match(/@\w+/g);

				log.info('mention: ', mentions);

				promises = mentions.map((u) => {
					return new Promise((resolve, reject) => {
						cache.getEntity(u.slice(1), (err, usr) => {
							if (err) {
								reject(err);
								return;
							}
							if (!usr) {
								resolve();
								return;
							}
							log.info('got user: ', usr);
							const textRel = {
								item: entity.id,
								user: usr.id,
								type: Constants.TYPE_TEXTREL,
								roles: [ Constants.ROLE_MENTIONED ]
							};
							const relation = new textrel(textRel);

							log.info('create relation on mention: ', relation, relation.id);
							resolve(relation);
						});
					});
				});

				mentions.forEach((usr) => {
					promises.push(new Promise((resolve, reject) => {
						cache.getEntity(relationId, (e, r) => {
							if (e) {
								reject(e);
								return;
							}
							if (r && r.roles.indexOf(Constants.ROLE_MENTIONED) > -1) {
								resolve();
								return;
							}
							const threadRel = {
								item: entity.parents[0],
								user: usr.slice(1),
								type: Constants.TYPE_THREADREL,
								roles: [ Constants.ROLE_MENTIONED ]
							};
							const threadRelation = new ThreadRel(threadRel);

							log.info('Thread Relation created:', threadRelation);
							resolve(threadRelation);
						});
					}));
				});

			} else {
				promises = [];
			}
			promises.push(new Promise((resolve, reject) => {
				cache.getEntity(relationId, (err, r) => {
					log.info('Got previous relation: ', err, r, relationId);
					if (err) {
						reject(err);
						return;
					}
					if (r && r.roles.indexOf(Constants.ROLE_FOLLOWER) > -1) {
						resolve();
						return;
					}
					text = entity;
					role = [ Constants.ROLE_FOLLOWER ];
					user = entity.creator;
					const threadRel = {
						item: text.parents[0],
						user,
						type: Constants.TYPE_THREADREL,
						roles: role
					};
					const relation = new ThreadRel(threadRel);

					log.info('create relation on text: ', relation, relation.id);
					resolve(relation);
				});
			}));

			Promise.all(promises).then(relations => {
				log.info('all promises resolved');
				relations.forEach(relation => {
					if (!relation) return;
					console.log("relation.id", relation.id);
					changes.entities[relation.id] = relation;
				});
				next();
			}).catch((err) => {
				log.info('cought error: ', err);
				next(err);
			});
		}
	}
}, Constants.APP_PRIORITIES.RELATIONS);
log.info('Relation module ready');
<commit_msg>Check if text is created then create relation<commit_after>/* @flow */

import { Constants, bus, cache } from '../../core-server';
import ThreadRel from '../../models/threadrel';
import { textrel } from '../../models/models';
import log from 'winston';

bus.on('change', (changes, next) => {
	if (!changes.entities) {
		next();
		return;
	}

	for (const id in changes.entities) {
		const entity = changes.entities[id];
		let text, role, user;

		if (entity.type === Constants.TYPE_TEXT) {
			if (entity.createTime !== entity.updateTime) {
				next();
				continue;
			}
			const relationId = entity.creator + '_' + entity.parents[0];
			let promises;

			if (entity.body.match(/@\w+/g)) {
				const mentions = entity.body.match(/@\w+/g);

				log.info('mention: ', mentions);

				promises = mentions.map((u) => {
					return new Promise((resolve, reject) => {
						cache.getEntity(u.slice(1), (err, usr) => {
							if (err) {
								reject(err);
								return;
							}
							if (!usr) {
								resolve();
								return;
							}
							log.info('got user: ', usr);
							const textRel = {
								item: entity.id,
								user: usr.id,
								type: Constants.TYPE_TEXTREL,
								roles: [ Constants.ROLE_MENTIONED ]
							};
							const relation = new textrel(textRel);

							log.info('create relation on mention: ', relation, relation.id);
							resolve(relation);
						});
					});
				});

				mentions.forEach((usr) => {
					promises.push(new Promise((resolve, reject) => {
						cache.getEntity(relationId, (e, r) => {
							log.info('Got previous relation for mention: ', e, r, relationId);
							if (e) {
								reject(e);
								return;
							}
							if (r && r.roles.indexOf(Constants.ROLE_MENTIONED) > -1) {
								log.info(relationId, 'this user is already mentioned. Return');
								resolve();
								return;
							}
							const threadRel = {
								item: entity.parents[0],
								user: usr.slice(1),
								type: Constants.TYPE_THREADREL,
								roles: [ Constants.ROLE_MENTIONED ]
							};
							const threadRelation = new ThreadRel(threadRel);

							log.info('Thread Relation created:', threadRelation);
							resolve(threadRelation);
						});
					}));
				});

			} else {
				promises = [];
			}
			promises.push(new Promise((resolve, reject) => {
				cache.getEntity(relationId, (err, r) => {
					log.info('Got previous relation for follower: ', err, r, relationId);
					if (err) {
						reject(err);
						return;
					}
					if (r && r.roles.indexOf(Constants.ROLE_FOLLOWER) > -1) {
						log.info(relationId, 'this user is already a follower. Return');
						resolve();
						return;
					}
					text = entity;
					role = [ Constants.ROLE_FOLLOWER ];
					user = entity.creator;
					const threadRel = {
						item: text.parents[0],
						user,
						type: Constants.TYPE_THREADREL,
						roles: role
					};
					const relation = new ThreadRel(threadRel);

					log.info('create relation on text: ', relation, relation.id);
					resolve(relation);
				});
			}));

			Promise.all(promises).then(relations => {
				log.info('all promises resolved');
				relations.forEach(relation => {
					if (!relation) return;
					log.info('relation.id: ', relation.id);
					changes.entities[relation.id] = relation;
				});
				next();
			}).catch((err) => {
				log.info('cought error: ', err);
				next(err);
			});
		}
	}
}, Constants.APP_PRIORITIES.RELATIONS);
log.info('Relation module ready');
<|endoftext|>


<commit_before>/**
 * @file 解码 HTML 字符实体
 * @author errorrik(errorrik@gmail.com)
 */

/**
 * 解码 HTML 字符实体
 *
 * @param {string} source 要解码的字符串
 * @return {string}
 */
function decodeHTMLEntity(source) {
    return source
        .replace(/</g, '<')
        .replace(/>/g, '>')
        .replace(/ /g, ' ')
        .replace(/"/g, '\"')
        .replace(/&#([0-9]+);/g, function (match, code) {
            return String.fromCharCode(+code);
        })
        .replace(/&#x([0-9a-f]+);/ig, function (match, code) {
            return String.fromCharCode(parseInt(code, 16));
        })
        .replace(/&/g, '&');
}

exports = module.exports = decodeHTMLEntity;
<commit_msg>fix #276<commit_after>/**
 * @file 解码 HTML 字符实体
 * @author errorrik(errorrik@gmail.com)
 */

var ENTITY_DECODE_MAP = {
    lt: '<',
    gt: '>',
    nbsp: ' ',
    quot: '\"',
    emsp: '\u2003',
    ensp: '\u2002',
    thinsp: '\u2009',
    copy: '\xa9',
    reg: '\xae',
    zwnj: '\u200c',
    zwj: '\u200d',
    amp: '&'
};

/**
 * 解码 HTML 字符实体
 *
 * @param {string} source 要解码的字符串
 * @return {string}
 */
function decodeHTMLEntity(source) {
    return source
        .replace(/&#([0-9]+);/g, function (match, code) {
            return String.fromCharCode(+code);
        })
        .replace(/&#x([0-9a-f]+);/ig, function (match, code) {
            return String.fromCharCode(parseInt(code, 16));
        })
        .replace(/&([a-z]+);/ig, function (match, code) {
            return ENTITY_DECODE_MAP[code];
        });
}

exports = module.exports = decodeHTMLEntity;
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

// ********************************************************************************************* //
// Module

define([
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/dom",
    "firebug/chrome/menu",
],
function (FBTrace, Obj, Dom, Menu) {

// ********************************************************************************************* //
// Constants

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

// Introduced in Firefox 8
Cu["import"]("resource:///modules/source-editor.jsm");

// ********************************************************************************************* //
// Source View

function ScriptView()
{
    this.editor = null;
}

ScriptView.prototype = Obj.extend(new Firebug.EventSource(),
{
    dispatchName: "ScriptView",
    initialized: false,

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Initialization

    initialize: function(parentNode)
    {
        this.onContextMenuListener = this.onContextMenu.bind(this);
        this.onBreakpointChangeListener = this.onBreakpointChange.bind(this);

        var config = {
            mode: SourceEditor.MODES.JAVASCRIPT,
            showLineNumbers: true,
            readOnly: true,
            showAnnotationRuler: true,
            showOverviewRuler: true,
            theme: "chrome://firebug/skin/orion-firebug.css",
        };

        this.editor = new SourceEditor();
        this.editor.init(parentNode, config, this.onEditorLoad.bind(this));

        // xxxHonza: use CSS?
        this.editor._iframe.style.width = "100%";
        this.editor._iframe.style.height = "100%";
    },

    onEditorLoad: function()
    {
        this.initialized = true;

        // Add editor listeners
        this.editor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
            this.onContextMenuListener);
        this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
            this.onBreakpointChangeListener);

        // Focus so, keyboard works as expected.
        this.editor.focus();

        if (this.defaultSource)
            this.showSource(this.defaultSource);
    },

    destroy: function()
    {
        this.editor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
            this.onContextMenuListener);
        this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
            this.onBreakpointChangeListener);

        if (this.initialized)
            this.editor.destroy();

        this.editor = null;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Public API

    showSource: function(source)
    {
        if (this.initialized)
            this.editor.setText(source);
        else
            this.defaultSource = source;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Context Menu

    onContextMenu: function(event)
    {
        var popup = document.getElementById("fbScriptViewPopup");
        Dom.eraseNode(popup);

        var browserWindow = Firebug.chrome.window;
        var commandDispatcher = browserWindow.document.commandDispatcher;

        var items = [];
        this.dispatch("onContextMenu", [items]);

        for (var i=0; i<items.length; i++)
            Menu.createMenuItem(popup, items[i]);

        if (!popup.childNodes.length)
            return;

        popup.openPopupAtScreen(event.screenX, event.screenY, true);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Search

    search: function(text, reverse)
    {
        var curDoc = this.searchCurrentDoc(!Firebug.searchGlobal, text, reverse);
        if (!curDoc && Firebug.searchGlobal)
        {
            return this.searchOtherDocs(text, reverse) ||
                this.searchCurrentDoc(true, text, reverse);
        }
        return curDoc;
    },

    searchOtherDocs: function(text, reverse)
    {
        var scanRE = Firebug.Search.getTestingRegex(text);

        var self = this;

        function scanDoc(compilationUnit)
        {
            var lines = null;

            // TODO The source lines arrive asynchronous in general
            compilationUnit.getSourceLines(-1, -1, function loadSource(unit, firstLineNumber,
                lastLineNumber, linesRead)
            {
                lines = linesRead;
            });

            if (!lines)
                return;

            // We don't care about reverse here as we are just looking for existence.
            // If we do have a result, we will handle the reverse logic on display.
            for (var i = 0; i < lines.length; i++)
            {
                if (scanRE.test(lines[i]))
                    return true;
            }
        }

        if (this.dispatch("onNavigateToNextDocument", [scanDoc, reverse]))
            return this.searchCurrentDoc(true, text, reverse) && "wraparound";
    },

    searchCurrentDoc: function(wrapSearch, text, reverse)
    {
        var options =
        {
            ignoreCase: !Firebug.Search.isCaseSensitive(text),
            backwards: reverse
        };

        if (this.currentSearch && text == this.currentSearch.text)
        {
            options.start = this.currentSearch.start;
            if (reverse)
                options.start -= text.length + 1;
        }
        else
        {
            this.currentSearch = {text: text, start: 0};
        }

        var offset = this.editor.find(text, options);
        FBTrace.sysout("search", {options: options, offset: offset});
        if (offset != -1)
        {
            this.editor.setSelection(offset, offset + text.length);
            this.currentSearch.start = offset + text.length;
            return true;
        }

        return false;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Breakpoints

    onBreakpointChange: function(event)
    {
        if (this.skipEditorBreakpointChange)
            return;

        event.added.forEach(function(bp) {
            this.dispatch("onBreakpointAdd", [bp]);
        }, this);

        event.removed.forEach(function(bp) {
            this.dispatch("onBreakpointRemove", [bp]);
        }, this);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Highlight Line

    scrollToLine: function(href, lineNo, highlighter)
    {
        FBTrace.sysout("scriptView.scrollToLine; line: " + lineNo);

        this.editor.setDebugLocation(lineNo - 1);
        this.editor.setCaretPosition(lineNo - 1);
    },

    removeDebugLocation: function()
    {
        this.editor.setDebugLocation(-1);
    }
});

// ********************************************************************************************* //
// Export

return ScriptView;

// ********************************************************************************************* //
});<commit_msg>Avoid exception<commit_after>/* See license.txt for terms of usage */

// ********************************************************************************************* //
// Module

define([
    "firebug/lib/trace",
    "firebug/lib/object",
    "firebug/lib/dom",
    "firebug/chrome/menu",
],
function (FBTrace, Obj, Dom, Menu) {

// ********************************************************************************************* //
// Constants

var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;

// Introduced in Firefox 8
Cu["import"]("resource:///modules/source-editor.jsm");

// ********************************************************************************************* //
// Source View

function ScriptView()
{
    this.editor = null;
}

ScriptView.prototype = Obj.extend(new Firebug.EventSource(),
{
    dispatchName: "ScriptView",
    initialized: false,

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Initialization

    initialize: function(parentNode)
    {
        this.onContextMenuListener = this.onContextMenu.bind(this);
        this.onBreakpointChangeListener = this.onBreakpointChange.bind(this);

        var config = {
            mode: SourceEditor.MODES.JAVASCRIPT,
            showLineNumbers: true,
            readOnly: true,
            showAnnotationRuler: true,
            showOverviewRuler: true,
            theme: "chrome://firebug/skin/orion-firebug.css",
        };

        this.editor = new SourceEditor();
        this.editor.init(parentNode, config, this.onEditorLoad.bind(this));

        // xxxHonza: use CSS?
        this.editor._iframe.style.width = "100%";
        this.editor._iframe.style.height = "100%";
    },

    onEditorLoad: function()
    {
        this.initialized = true;

        // Add editor listeners
        this.editor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
            this.onContextMenuListener);
        this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
            this.onBreakpointChangeListener);

        // Focus so, keyboard works as expected.
        this.editor.focus();

        if (this.defaultSource)
            this.showSource(this.defaultSource);
    },

    destroy: function()
    {
        this.editor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU,
            this.onContextMenuListener);
        this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
            this.onBreakpointChangeListener);

        if (this.initialized)
            this.editor.destroy();

        this.editor = null;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Public API

    showSource: function(source)
    {
        if (this.initialized)
            this.editor.setText(source);
        else
            this.defaultSource = source;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Context Menu

    onContextMenu: function(event)
    {
        var popup = document.getElementById("fbScriptViewPopup");
        Dom.eraseNode(popup);

        var browserWindow = Firebug.chrome.window;
        var commandDispatcher = browserWindow.document.commandDispatcher;

        var items = [];
        this.dispatch("onContextMenu", [items]);

        for (var i=0; i<items.length; i++)
            Menu.createMenuItem(popup, items[i]);

        if (!popup.childNodes.length)
            return;

        popup.openPopupAtScreen(event.screenX, event.screenY, true);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Search

    search: function(text, reverse)
    {
        var curDoc = this.searchCurrentDoc(!Firebug.searchGlobal, text, reverse);
        if (!curDoc && Firebug.searchGlobal)
        {
            return this.searchOtherDocs(text, reverse) ||
                this.searchCurrentDoc(true, text, reverse);
        }
        return curDoc;
    },

    searchOtherDocs: function(text, reverse)
    {
        var scanRE = Firebug.Search.getTestingRegex(text);

        var self = this;

        function scanDoc(compilationUnit)
        {
            var lines = null;

            // TODO The source lines arrive asynchronous in general
            compilationUnit.getSourceLines(-1, -1, function loadSource(unit, firstLineNumber,
                lastLineNumber, linesRead)
            {
                lines = linesRead;
            });

            if (!lines)
                return;

            // We don't care about reverse here as we are just looking for existence.
            // If we do have a result, we will handle the reverse logic on display.
            for (var i = 0; i < lines.length; i++)
            {
                if (scanRE.test(lines[i]))
                    return true;
            }
        }

        if (this.dispatch("onNavigateToNextDocument", [scanDoc, reverse]))
            return this.searchCurrentDoc(true, text, reverse) && "wraparound";
    },

    searchCurrentDoc: function(wrapSearch, text, reverse)
    {
        var options =
        {
            ignoreCase: !Firebug.Search.isCaseSensitive(text),
            backwards: reverse
        };

        if (this.currentSearch && text == this.currentSearch.text)
        {
            options.start = this.currentSearch.start;
            if (reverse)
                options.start -= text.length + 1;
        }
        else
        {
            this.currentSearch = {text: text, start: 0};
        }

        var offset = this.editor.find(text, options);
        FBTrace.sysout("search", {options: options, offset: offset});
        if (offset != -1)
        {
            this.editor.setSelection(offset, offset + text.length);
            this.currentSearch.start = offset + text.length;
            return true;
        }

        return false;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Breakpoints

    onBreakpointChange: function(event)
    {
        if (this.skipEditorBreakpointChange)
            return;

        event.added.forEach(function(bp) {
            this.dispatch("onBreakpointAdd", [bp]);
        }, this);

        event.removed.forEach(function(bp) {
            this.dispatch("onBreakpointRemove", [bp]);
        }, this);
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Highlight Line

    scrollToLine: function(href, lineNo, highlighter)
    {
        FBTrace.sysout("scriptView.scrollToLine; line: " + lineNo);

        //@hack xxxHonza: avoid exception
        if (!this.editor._model)
            return;

        this.editor.setDebugLocation(lineNo - 1);
        this.editor.setCaretPosition(lineNo - 1);
    },

    removeDebugLocation: function()
    {
        //@hack xxxHonza: avoid exception
        if (!this.editor._model)
            return;

        this.editor.setDebugLocation(-1);
    }
});

// ********************************************************************************************* //
// Export

return ScriptView;

// ********************************************************************************************* //
});<|endoftext|>


<commit_before>'use strict';
var host = "http://localhost:8080";
//var host = "http://spell.pituwa.lk";
angular.module('myApp.view1', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/view1', {
    templateUrl: 'view1/view1.html',
    controller: 'View1Ctrl'
  });
}]).controller('View1Ctrl', ['$scope', '$http', '$interval', function($scope, $http, $interval) {

    $interval(function () {
        $http.get(host + "/stats").then(function (res) {
            if (res.data.words - $scope.words != 0) {
                $scope.wordDiff = res.data.words - $scope.words;
            }
            $scope.words = res.data.words;
            $scope.sites = res.data.sites;
        });
    }, 30000);

    $scope.wordDiff = 0;
    $scope.sites = 0;
    $scope.words = 0;
    $scope.docElm = document.getElementById("pastedArea");
    $scope.checkResult = document.getElementById('checkResult');
    $scope.doc =  function () {
        return document.getElementById("pastedArea").innerText;
    };
    $scope.ndoc = "";
    $scope.result = {};
    $scope.handler = {};
    $scope.buttonName = "සිංහල අක්ෂර වින්යාස පරීක්ෂාව ";
    $scope.checkSpell = function () {
        var words = $scope.doc().split(" ");

        $http.post(host + "/bulk", words).then(function (res) {
            var result = res.data;
            $scope.docElm.innerHTML = "";
            var errors = 0;
            words.map(function (word) {
                result[word] = result[word].reverse();
                var correct = result[word][0] === word;
                var elm = null;
                if (!correct) {
                    errors++;
                    elm = document.createElement("span");
                    elm.innerText = word;
                    elm.style.backgroundColor = 'yellow';
                    elm.data = result[word];
                    elm.addEventListener("click", function (e) {
                        $scope.handleDisplay(elm, e, word);
                    });
                } else {
                    elm = document.createTextNode(word)
                }
                $scope.docElm.appendChild(elm);
                var space = document.createTextNode(" ");
                $scope.docElm.appendChild(space);
            });
            if (errors === 0) {
                $scope.checkResult.innerText = "සිය ලුවච නනිවැරදී";
            }
        });
    };

    $scope.handleDisplay = function (elm, me, word) {
        var ulx = document.getElementById("ulWordList");
        var words  = elm.data;

        while(ulx.getElementsByTagName("li").length > 0) {
            var lis = ulx.getElementsByTagName("li");
            ulx.removeChild(lis[0]);
        }

        words.forEach(function (v, k) {
            var li = document.createElement("li");
            li.innerHTML = v;
            ulx.appendChild(li);
        });

        var clsWord = document.getElementsByClassName("word")
        for (var i = 0; i < clsWord.length; i++) {
            clsWord[i].style.opacity = 0.4;
            clsWord[i].style.fontWeight = "normal";
        }

        elm.style.opacity = 1.0;
        elm.style.fontWeight = 800;
        var wordList = document.getElementById("wordList");
        var writePad = document.getElementById("writePad");
        wordList.style.top = me.pageY + "px";
        wordList.style.left = (me.pageX + 50) + "px";
    };

}]);

/*function handleDisplay(clk, elm, words, e) {
    console.log(e);
}

var zwidth = [];

function selectList(words, dst)
{

    if (words === undefined || words.length === 0) {
        $http.get("http://localhost:8080/lvar?lookup=" + v + "&size=" + v.length - 2).then(function (res) {
            words = res.data;

            var clsWord = document.getElementsByClassName("word")
            for (var i = 0; i < clsWord.length; i++) {
                clsWord[i].style.opacity = 0.4;
            }
            dst.style.opacity = 1.0;
            dst.style.fontWeight = 800;
            var wordList = document.getElementById("wordList");
            var writePad = document.getElementById("writePad");
            wordList.style.top = dst.style.top;
            var idx2 = dst.id.substring(2, 3);
            var sumx = 0;
            for (var i = 0; i < idx2; i++) sumx += zwidth[i];
            wordList.style.left = sumx + "px";
        });
    } else {*/<commit_msg>initial<commit_after>'use strict';
//var host = "http://localhost:8080";
var host = "http://spell.pituwa.lk";
angular.module('myApp.view1', ['ngRoute'])

.config(['$routeProvider', function($routeProvider) {
  $routeProvider.when('/view1', {
    templateUrl: 'view1/view1.html',
    controller: 'View1Ctrl'
  });
}]).controller('View1Ctrl', ['$scope', '$http', '$interval', function($scope, $http, $interval) {

    $interval(function () {
        $http.get(host + "/stats").then(function (res) {
            if (res.data.words - $scope.words != 0) {
                $scope.wordDiff = res.data.words - $scope.words;
            }
            $scope.words = res.data.words;
            $scope.sites = res.data.sites;
        });
    }, 30000);

    $scope.wordDiff = 0;
    $scope.sites = 0;
    $scope.words = 0;
    $scope.docElm = document.getElementById("pastedArea");
    $scope.checkResult = document.getElementById('checkResult');
    $scope.doc =  function () {
        return document.getElementById("pastedArea").innerText;
    };
    $scope.ndoc = "";
    $scope.result = {};
    $scope.handler = {};
    $scope.buttonName = "සිංහල අක්ෂර වින්යාස පරීක්ෂාව ";
    $scope.checkSpell = function () {
        var words = $scope.doc().split(" ");

        $http.post(host + "/bulk", words).then(function (res) {
            var result = res.data;
            $scope.docElm.innerHTML = "";
            var errors = 0;
            words.map(function (word) {
                result[word] = result[word].reverse();
                var correct = result[word][0] === word;
                var elm = null;
                if (!correct) {
                    errors++;
                    elm = document.createElement("span");
                    elm.innerText = word;
                    elm.style.backgroundColor = 'yellow';
                    elm.data = result[word];
                    elm.addEventListener("click", function (e) {
                        $scope.handleDisplay(elm, e, word);
                    });
                } else {
                    elm = document.createTextNode(word)
                }
                $scope.docElm.appendChild(elm);
                var space = document.createTextNode(" ");
                $scope.docElm.appendChild(space);
            });
            if (errors === 0) {
                $scope.checkResult.innerText = "සිය ලුවච නනිවැරදී";
            }
        });
    };

    $scope.handleDisplay = function (elm, me, word) {
        var ulx = document.getElementById("ulWordList");
        var words  = elm.data;

        while(ulx.getElementsByTagName("li").length > 0) {
            var lis = ulx.getElementsByTagName("li");
            ulx.removeChild(lis[0]);
        }

        words.forEach(function (v, k) {
            var li = document.createElement("li");
            li.innerHTML = v;
            ulx.appendChild(li);
        });

        var clsWord = document.getElementsByClassName("word")
        for (var i = 0; i < clsWord.length; i++) {
            clsWord[i].style.opacity = 0.4;
            clsWord[i].style.fontWeight = "normal";
        }

        elm.style.opacity = 1.0;
        elm.style.fontWeight = 800;
        var wordList = document.getElementById("wordList");
        var writePad = document.getElementById("writePad");
        wordList.style.top = me.pageY + "px";
        wordList.style.left = (me.pageX + 50) + "px";
    };

}]);

/*function handleDisplay(clk, elm, words, e) {
    console.log(e);
}

var zwidth = [];

function selectList(words, dst)
{

    if (words === undefined || words.length === 0) {
        $http.get("http://localhost:8080/lvar?lookup=" + v + "&size=" + v.length - 2).then(function (res) {
            words = res.data;

            var clsWord = document.getElementsByClassName("word")
            for (var i = 0; i < clsWord.length; i++) {
                clsWord[i].style.opacity = 0.4;
            }
            dst.style.opacity = 1.0;
            dst.style.fontWeight = 800;
            var wordList = document.getElementById("wordList");
            var writePad = document.getElementById("writePad");
            wordList.style.top = dst.style.top;
            var idx2 = dst.id.substring(2, 3);
            var sumx = 0;
            for (var i = 0; i < idx2; i++) sumx += zwidth[i];
            wordList.style.left = sumx + "px";
        });
    } else {*/<|endoftext|>