<commit_before>import React, {Component} from 'react';
import {Tooltip} from 'reactjs-components';

import {FormReducer} from '../../reducers/serviceForm/Container';
import AdvancedSection from '../../../../../../src/js/components/form/AdvancedSection';
import AdvancedSectionContent from '../../../../../../src/js/components/form/AdvancedSectionContent';
import AdvancedSectionLabel from '../../../../../../src/js/components/form/AdvancedSectionLabel';
import FieldError from '../../../../../../src/js/components/form/FieldError';
import FieldHelp from '../../../../../../src/js/components/form/FieldHelp';
import FieldInput from '../../../../../../src/js/components/form/FieldInput';
import FieldLabel from '../../../../../../src/js/components/form/FieldLabel';
import FieldTextarea from '../../../../../../src/js/components/form/FieldTextarea';
import FormGroup from '../../../../../../src/js/components/form/FormGroup';
import MetadataStore from '../../../../../../src/js/stores/MetadataStore';
import Icon from '../../../../../../src/js/components/Icon';
import {findNestedPropertyInObject} from '../../../../../../src/js/utils/Util';

const containerSettings = {
  privileged: {
    label: Grant Runtime Privileges,
    helpText: 'By default, containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container.'
  },
  forcePullImage: {
    label: Force Pull Image On Launch,
    helpText: 'Force Docker to pull the image before launching each task.'
  }
};

const getArtifactsLabel = () => {
  return (
    <FieldLabel>
      {'Artifact URI '}
      <Tooltip
        content="Provided URIs are passed to Mesos fetcher module and resolved in runtime."
        interactive={true}
        maxWidth={300}
        scrollContainer=".gm-scroll-view"
        wrapText={true}>
          <Icon color="grey" id="ring-question" size="mini" family="mini" />
      </Tooltip>
    </FieldLabel>
  );
};

class ContainerServiceFormSection extends Component {
  getArtifactsInputs(data = []) {
    const errors = this.props.errors.fetch || [];

    let content = data.map((item, index) => {
      let label;
      if (index === 0) {
        label = getArtifactsLabel();
      }

      return (

          <FormGroup
            className="column-10"
            showError={Boolean(errors[index])}>
            {label}
            <FieldInput
              name={`fetch.${index}.uri`}
              type="text"
              value={item.uri}/>
            <FieldError>{errors[index]}</FieldError>
          </FormGroup>
          <FormGroup className="flex flex-item-align-end column-2">

              Delete

          </FormGroup>

      );
    });

    if (data.length === 0) {
      content = (

          <FormGroup className="column-10">
            {getArtifactsLabel()}
          </FormGroup>

      );
    }

    return (

        {content}

    );
  }

  getGPUSInput(data) {
    if (data.container.type === 'DOCKER') {
      return [
        <Tooltip
          content="Docker Engine does not support GPU resources, please select Universal Container Runtime if you want to use GPU resources."
          interactive={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view"
          wrapText={true}>
          <FieldLabel>
            {'GPUs '}
            <Icon color="grey" id="lock" size="mini" family="mini" />
          </FieldLabel>
        </Tooltip>,
        <FieldInput
          name="gpus"
          type="number"
          disabled={true}
          value={data.gpus} />
      ];
    }

    return [
      <FieldLabel>GPUs</FieldLabel>,
      <FieldInput
        name="gpus"
        type="number"
        value={data.gpus} />
    ];
  }

  getAdvancedSettings(data = {}, errors = {}) {
    let {container = {}} = data;
    let typeErrors = errors.container && errors.container.type;
    let selections = Object.keys(containerSettings).map((settingName, index) => {
      let {helpText, label} = containerSettings[settingName];
      let checked = findNestedPropertyInObject(
        container,
        `docker.${settingName}`
      );

      return (
        <FieldLabel key={index}>
          <FieldInput
            checked={Boolean(checked)}
            name={`container.docker.${settingName}`}
            type="checkbox"
            value={settingName} />
          {label}
          <FieldHelp>{helpText}</FieldHelp>
        </FieldLabel>
      );
    });

    return (
      <AdvancedSectionContent>
        <FormGroup showError={Boolean(typeErrors)}>
          {selections}
          <FieldError>{typeErrors}</FieldError>
        </FormGroup>

          <FormGroup className="column-4" showError={Boolean(errors.gpus)}>
            {this.getGPUSInput(data)}
            <FieldError>{errors.gpus}</FieldError>
          </FormGroup>
          <FormGroup className="column-4" showError={Boolean(errors.disk)}>
            <FieldLabel>Disk (MiB)</FieldLabel>
            <FieldInput
              name="disk"
              type="number"
              value={data.disk} />
            <FieldError>{errors.disk}</FieldError>
          </FormGroup>

        {this.getArtifactsInputs(data.fetch)}
        <FieldError>{errors.fetch}</FieldError>

            + Add Artifact

      </AdvancedSectionContent>
    );
  }

  getCMDLabel() {
    let tooltipContent = (

        {'The command value will be wrapped by the underlying Mesos executor via /bin/sh -c ${cmd}. '}

          More information
        .

    );

    return (
      <FieldLabel>
        {'Command '}
        <Tooltip
          content={tooltipContent}
          interactive={true}
          wrapText={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view">
            <Icon color="grey" id="ring-question" size="mini" family="mini" />
        </Tooltip>
      </FieldLabel>
    );
  }

  getImageLabel() {
    let tooltipContent = (

        {'Enter a Docker image or browse '}

          Docker Hub

        {' to find more. You can also enter an image from your private registry. '}

          More information
        .

    );

    return (
      <FieldLabel>
        {'Container Image '}
        <Tooltip
          content={tooltipContent}
          interactive={true}
          wrapText={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view">
            <Icon color="grey" id="ring-question" size="mini" family="mini" />
        </Tooltip>
      </FieldLabel>
    );
  }

  render() {
    let {data, errors} = this.props;

    let {container = {}} = data;
    let image = findNestedPropertyInObject(container, 'docker.image');
    let imageErrors = findNestedPropertyInObject(
      errors,
      'container.docker.image'
    );

    return (

          Container

Configure your container below. Enter a container image or command you want to run.

          <FormGroup className="column-6" showError={Boolean(imageErrors)}>
            {this.getImageLabel()}
            <FieldInput
              name="container.docker.image"
              value={image} />
            <FieldHelp>
              Enter a Docker image you want to run, e.g. nginx.
            </FieldHelp>
            <FieldError>{imageErrors}</FieldError>
          </FormGroup>

          <FormGroup
            className="column-3"
             required={true}
             showError={Boolean(errors.cpus)}>
            <FieldLabel>
              CPUs
            </FieldLabel>
            <FieldInput
              name="cpus"
              type="number"
              step="0.01"
              value={data.cpus} />
            <FieldError>{errors.cpus}</FieldError>
          </FormGroup>
          <FormGroup
            className="column-3"
            required={true}
            showError={Boolean(errors.mem)}>
            <FieldLabel>
              Memory (MiB)
            </FieldLabel>
            <FieldInput
              name="mem"
              type="number"
              value={data.mem} />
            <FieldError>{errors.mem}</FieldError>
          </FormGroup>

        <FormGroup showError={Boolean(errors.cmd)}>
          {this.getCMDLabel()}
          <FieldTextarea name="cmd" value={data.cmd} />
          <FieldHelp>
            A shell command for your container to execute.
          </FieldHelp>
          <FieldError>{errors.cmd}</FieldError>
        </FormGroup>

        <AdvancedSection>
          <AdvancedSectionLabel>
            Advanced Container Settings
          </AdvancedSectionLabel>
          {this.getAdvancedSettings(data, errors)}
        </AdvancedSection>

    );
  }
}

ContainerServiceFormSection.defaultProps = {
  data: {},
  errors: {},
  onAddItem() {},
  onRemoveItem() {}
};

ContainerServiceFormSection.propTypes = {
  data: React.PropTypes.object,
  errors: React.PropTypes.object,
  onAddItem: React.PropTypes.func,
  onRemoveItem: React.PropTypes.func
};

ContainerServiceFormSection.configReducers = {
  container: FormReducer
};

module.exports = ContainerServiceFormSection;
<commit_msg>Updated copy. Thanks Phil<commit_after>import React, {Component} from 'react';
import {Tooltip} from 'reactjs-components';

import {FormReducer} from '../../reducers/serviceForm/Container';
import AdvancedSection from '../../../../../../src/js/components/form/AdvancedSection';
import AdvancedSectionContent from '../../../../../../src/js/components/form/AdvancedSectionContent';
import AdvancedSectionLabel from '../../../../../../src/js/components/form/AdvancedSectionLabel';
import FieldError from '../../../../../../src/js/components/form/FieldError';
import FieldHelp from '../../../../../../src/js/components/form/FieldHelp';
import FieldInput from '../../../../../../src/js/components/form/FieldInput';
import FieldLabel from '../../../../../../src/js/components/form/FieldLabel';
import FieldTextarea from '../../../../../../src/js/components/form/FieldTextarea';
import FormGroup from '../../../../../../src/js/components/form/FormGroup';
import MetadataStore from '../../../../../../src/js/stores/MetadataStore';
import Icon from '../../../../../../src/js/components/Icon';
import {findNestedPropertyInObject} from '../../../../../../src/js/utils/Util';

const containerSettings = {
  privileged: {
    label: Grant Runtime Privileges,
    helpText: 'By default, containers are “unprivileged” and cannot, for example, run a Docker daemon inside a Docker container.'
  },
  forcePullImage: {
    label: Force Pull Image On Launch,
    helpText: 'Force Docker to pull the image before launching each task.'
  }
};

const getArtifactsLabel = () => {
  return (
    <FieldLabel>
      {'Artifact URI '}
      <Tooltip
        content="Provided URIs are passed to Mesos fetcher module and resolved at runtime."
        interactive={true}
        maxWidth={300}
        scrollContainer=".gm-scroll-view"
        wrapText={true}>
          <Icon color="grey" id="ring-question" size="mini" family="mini" />
      </Tooltip>
    </FieldLabel>
  );
};

class ContainerServiceFormSection extends Component {
  getArtifactsInputs(data = []) {
    const errors = this.props.errors.fetch || [];

    let content = data.map((item, index) => {
      let label;
      if (index === 0) {
        label = getArtifactsLabel();
      }

      return (

          <FormGroup
            className="column-10"
            showError={Boolean(errors[index])}>
            {label}
            <FieldInput
              name={`fetch.${index}.uri`}
              type="text"
              value={item.uri}/>
            <FieldError>{errors[index]}</FieldError>
          </FormGroup>
          <FormGroup className="flex flex-item-align-end column-2">

              Delete

          </FormGroup>

      );
    });

    if (data.length === 0) {
      content = (

          <FormGroup className="column-10">
            {getArtifactsLabel()}
          </FormGroup>

      );
    }

    return (

        {content}

    );
  }

  getGPUSInput(data) {
    if (data.container.type === 'DOCKER') {
      return [
        <Tooltip
          content="Docker Engine does not support GPU resources, please select Universal Container Runtime if you want to use GPU resources."
          interactive={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view"
          wrapText={true}>
          <FieldLabel>
            {'GPUs '}
            <Icon color="grey" id="lock" size="mini" family="mini" />
          </FieldLabel>
        </Tooltip>,
        <FieldInput
          name="gpus"
          type="number"
          disabled={true}
          value={data.gpus} />
      ];
    }

    return [
      <FieldLabel>GPUs</FieldLabel>,
      <FieldInput
        name="gpus"
        type="number"
        value={data.gpus} />
    ];
  }

  getAdvancedSettings(data = {}, errors = {}) {
    let {container = {}} = data;
    let typeErrors = errors.container && errors.container.type;
    let selections = Object.keys(containerSettings).map((settingName, index) => {
      let {helpText, label} = containerSettings[settingName];
      let checked = findNestedPropertyInObject(
        container,
        `docker.${settingName}`
      );

      return (
        <FieldLabel key={index}>
          <FieldInput
            checked={Boolean(checked)}
            name={`container.docker.${settingName}`}
            type="checkbox"
            value={settingName} />
          {label}
          <FieldHelp>{helpText}</FieldHelp>
        </FieldLabel>
      );
    });

    return (
      <AdvancedSectionContent>
        <FormGroup showError={Boolean(typeErrors)}>
          {selections}
          <FieldError>{typeErrors}</FieldError>
        </FormGroup>

          <FormGroup className="column-4" showError={Boolean(errors.gpus)}>
            {this.getGPUSInput(data)}
            <FieldError>{errors.gpus}</FieldError>
          </FormGroup>
          <FormGroup className="column-4" showError={Boolean(errors.disk)}>
            <FieldLabel>Disk (MiB)</FieldLabel>
            <FieldInput
              name="disk"
              type="number"
              value={data.disk} />
            <FieldError>{errors.disk}</FieldError>
          </FormGroup>

        {this.getArtifactsInputs(data.fetch)}
        <FieldError>{errors.fetch}</FieldError>

            + Add Artifact

      </AdvancedSectionContent>
    );
  }

  getCMDLabel() {
    let tooltipContent = (

        {'The command value will be wrapped by the underlying Mesos executor via /bin/sh -c ${cmd}. '}

          More information
        .

    );

    return (
      <FieldLabel>
        {'Command '}
        <Tooltip
          content={tooltipContent}
          interactive={true}
          wrapText={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view">
            <Icon color="grey" id="ring-question" size="mini" family="mini" />
        </Tooltip>
      </FieldLabel>
    );
  }

  getImageLabel() {
    let tooltipContent = (

        {'Enter a Docker image or browse '}

          Docker Hub

        {' to find more. You can also enter an image from your private registry. '}

          More information
        .

    );

    return (
      <FieldLabel>
        {'Container Image '}
        <Tooltip
          content={tooltipContent}
          interactive={true}
          wrapText={true}
          maxWidth={300}
          scrollContainer=".gm-scroll-view">
            <Icon color="grey" id="ring-question" size="mini" family="mini" />
        </Tooltip>
      </FieldLabel>
    );
  }

  render() {
    let {data, errors} = this.props;

    let {container = {}} = data;
    let image = findNestedPropertyInObject(container, 'docker.image');
    let imageErrors = findNestedPropertyInObject(
      errors,
      'container.docker.image'
    );

    return (

          Container

Configure your container below. Enter a container image or command you want to run.

          <FormGroup className="column-6" showError={Boolean(imageErrors)}>
            {this.getImageLabel()}
            <FieldInput
              name="container.docker.image"
              value={image} />
            <FieldHelp>
              Enter a Docker image you want to run, e.g. nginx.
            </FieldHelp>
            <FieldError>{imageErrors}</FieldError>
          </FormGroup>

          <FormGroup
            className="column-3"
             required={true}
             showError={Boolean(errors.cpus)}>
            <FieldLabel>
              CPUs
            </FieldLabel>
            <FieldInput
              name="cpus"
              type="number"
              step="0.01"
              value={data.cpus} />
            <FieldError>{errors.cpus}</FieldError>
          </FormGroup>
          <FormGroup
            className="column-3"
            required={true}
            showError={Boolean(errors.mem)}>
            <FieldLabel>
              Memory (MiB)
            </FieldLabel>
            <FieldInput
              name="mem"
              type="number"
              value={data.mem} />
            <FieldError>{errors.mem}</FieldError>
          </FormGroup>

        <FormGroup showError={Boolean(errors.cmd)}>
          {this.getCMDLabel()}
          <FieldTextarea name="cmd" value={data.cmd} />
          <FieldHelp>
            A shell command for your container to execute.
          </FieldHelp>
          <FieldError>{errors.cmd}</FieldError>
        </FormGroup>

        <AdvancedSection>
          <AdvancedSectionLabel>
            Advanced Container Settings
          </AdvancedSectionLabel>
          {this.getAdvancedSettings(data, errors)}
        </AdvancedSection>

    );
  }
}

ContainerServiceFormSection.defaultProps = {
  data: {},
  errors: {},
  onAddItem() {},
  onRemoveItem() {}
};

ContainerServiceFormSection.propTypes = {
  data: React.PropTypes.object,
  errors: React.PropTypes.object,
  onAddItem: React.PropTypes.func,
  onRemoveItem: React.PropTypes.func
};

ContainerServiceFormSection.configReducers = {
  container: FormReducer
};

module.exports = ContainerServiceFormSection;
<|endoftext|>


<commit_before>// Constants
const regex = {
    error: new RegExp('^[a-zA-Z]*Error:'),
};

// DOM elements
const elements = {
    wrapper: document.querySelector('#wrapper'),
    prompt: document.querySelector('#prompt'),
    container: document.querySelector('#container'),
    browser: document.querySelector('#browser'),
    config: document.querySelector('#config'),
    log: document.querySelector('#log'),
};

// Show prompt and hide log container
elements.prompt.hidden = false;
elements.container.hidden = true;

/**
 * Escape HTML.
 */
function escapeHTML(text) {
    const template = document.createElement('span');
    template.innerText = text;
    return template.innerHTML;
}

/**
 * Create an element from HTML.
 */
function createElementFromHTML(html) {
    const template = document.createElement('template');
    template.innerHTML = html.trim();
    return template.content.firstChild;
}

/**
 * Format a record (message) value.
 */
function formatRecordValue(value) {
    // Handle null
    if (value === null) {
        return `<span class="null">${escapeHTML(`${value}`)}</span>`;
    }

    // Handle boolean
    if (value.constructor === Boolean) {
        return `<span class="boolean">${escapeHTML(value)}</span>`;
    }

    // Handle number
    if (value.constructor === Number) {
        return `<span class="number">${escapeHTML(value)}</span>`;
    }

    // Handle string, converted types (e.g. ArrayBuffer, Blob, ...)
    // and errors (exceptions).
    if (value.constructor === String) {
        if (value.startsWith('[') && value.endsWith(']')) {
            return `<span class="converted">${escapeHTML(value)}</span>`;
        }
        if (regex.error.test(value)) {
            return `<span class="error">${escapeHTML(value)}</span>`;
        }
        return `<span class="string">${escapeHTML(value)}</span>`;
    }

    // Handle object
    if (value.constructor === Object) {
        const entries = Object.entries(value);
        return `

                Object(${entries.length})

                    ${entries.map(([key, value]) => {
                        return `<li><span class="type">${escapeHTML(key)}:</span> ${formatRecordValue(value)}</li>`;
                    }).join('\n')}

            `;
    }

    // Handle array
    if (value instanceof Array) {
        return `

                Array(${value.length})

                    ${value.map((item, index) => {
                        return `<li><span class="type">${index}:</span> ${formatRecordValue(item)}</li>`;
                    }).join('\n')}

            `;
    }

    // Unknown
    return `[${value.constructor}]`;
}

/**
 * Show the log in the UI.
 * @param data A log report in JSON notation.
 */
function showLog(data) {
    // Decode as JSON
    let container;
    try {
        container = JSON.parse(data);
    } catch (error) {
        return console.error('Could not parse pasted text to object:', error);
    }

    // Required keys to be available
    if (!(container.config instanceof Object) ||
        container.browser.constructor !== String ||
        !(container.log instanceof Array)) {
        return console.error('Not a valid container object');
    }

    // Hide prompt and show log container
    elements.prompt.hidden = true;
    elements.container.hidden = false;

    // Display meta data
    elements.browser.textContent = container.browser;
    elements.config.textContent = JSON.stringify(container.config, null, 2);

    // Display log records
    elements.log.innerHTML = '';
    let startTimestampMs;
    for (let [timestampMs, type, tag, ...values] of container.log) {
        // Determine start timestamp so we can display the offset in seconds
        if (startTimestampMs === undefined) {
            startTimestampMs = timestampMs;
        }

        // Get CSS style from tag (if any)
        if (tag.startsWith('%c')) {
            const style = escapeHTML(values.shift());
            tag = `<span style="${style}">${escapeHTML(tag.substring(2))}</span>`;
        } else {
            tag = escapeHTML(tag);
        }

        // Add element to log container
        elements.log.appendChild(createElementFromHTML(`

                    ${((timestampMs - startTimestampMs) / 1000).toFixed(3)}
                    ${tag}
                    ${values.map((value) => formatRecordValue(value)).join('\n')}

`));
    }
}

/**
 * Listen for *paste* events.
 */
document.addEventListener('paste', (event) => {
    // If no clipboard data is available, do nothing.
    let text;
    try {
        text = event.clipboardData.getData('text');
    } catch (error) {
        return console.error('Could not retrieve pasted data as text:', error);
    }

    // Show log
    showLog(text);
});

/**
 * Listen for *drag* events.
 */
document.addEventListener('dragover', (event) => {
    event.preventDefault();
});
document.addEventListener('dragenter', () => {
    elements.wrapper.className = 'drag-over';
});
document.addEventListener('drop', (event) => {
    event.preventDefault();
    elements.wrapper.className = '';

    // Read first file (if any)
    const files = event.dataTransfer.files;
    if (files.length === 0) {
        console.error('No files in drop event');
        return;
    }
    const reader = new FileReader();
    reader.addEventListener('load', (event) => {
        showLog(event.target.result);
    });
    reader.readAsText(files[0]);
});
<commit_msg>Troubleshooting log viewer: Show full date in title text (#967)<commit_after>// Constants
const regex = {
    error: new RegExp('^[a-zA-Z]*Error:'),
};

// DOM elements
const elements = {
    wrapper: document.querySelector('#wrapper'),
    prompt: document.querySelector('#prompt'),
    container: document.querySelector('#container'),
    browser: document.querySelector('#browser'),
    config: document.querySelector('#config'),
    log: document.querySelector('#log'),
};

// Show prompt and hide log container
elements.prompt.hidden = false;
elements.container.hidden = true;

/**
 * Escape HTML.
 */
function escapeHTML(text) {
    const template = document.createElement('span');
    template.innerText = text;
    return template.innerHTML;
}

/**
 * Create an element from HTML.
 */
function createElementFromHTML(html) {
    const template = document.createElement('template');
    template.innerHTML = html.trim();
    return template.content.firstChild;
}

/**
 * Format a record (message) value.
 */
function formatRecordValue(value) {
    // Handle null
    if (value === null) {
        return `<span class="null">${escapeHTML(`${value}`)}</span>`;
    }

    // Handle boolean
    if (value.constructor === Boolean) {
        return `<span class="boolean">${escapeHTML(value)}</span>`;
    }

    // Handle number
    if (value.constructor === Number) {
        return `<span class="number">${escapeHTML(value)}</span>`;
    }

    // Handle string, converted types (e.g. ArrayBuffer, Blob, ...)
    // and errors (exceptions).
    if (value.constructor === String) {
        if (value.startsWith('[') && value.endsWith(']')) {
            return `<span class="converted">${escapeHTML(value)}</span>`;
        }
        if (regex.error.test(value)) {
            return `<span class="error">${escapeHTML(value)}</span>`;
        }
        return `<span class="string">${escapeHTML(value)}</span>`;
    }

    // Handle object
    if (value.constructor === Object) {
        const entries = Object.entries(value);
        return `

                Object(${entries.length})

                    ${entries.map(([key, value]) => {
                        return `<li><span class="type">${escapeHTML(key)}:</span> ${formatRecordValue(value)}</li>`;
                    }).join('\n')}

            `;
    }

    // Handle array
    if (value instanceof Array) {
        return `

                Array(${value.length})

                    ${value.map((item, index) => {
                        return `<li><span class="type">${index}:</span> ${formatRecordValue(item)}</li>`;
                    }).join('\n')}

            `;
    }

    // Unknown
    return `[${value.constructor}]`;
}

/**
 * Show the log in the UI.
 * @param data A log report in JSON notation.
 */
function showLog(data) {
    // Decode as JSON
    let container;
    try {
        container = JSON.parse(data);
    } catch (error) {
        return console.error('Could not parse pasted text to object:', error);
    }

    // Required keys to be available
    if (!(container.config instanceof Object) ||
        container.browser.constructor !== String ||
        !(container.log instanceof Array)) {
        return console.error('Not a valid container object');
    }

    // Hide prompt and show log container
    elements.prompt.hidden = true;
    elements.container.hidden = false;

    // Display meta data
    elements.browser.textContent = container.browser;
    elements.config.textContent = JSON.stringify(container.config, null, 2);

    // Display log records
    elements.log.innerHTML = '';
    let startTimestampMs;
    for (let [timestampMs, type, tag, ...values] of container.log) {
        // Determine start timestamp so we can display the offset in seconds
        if (startTimestampMs === undefined) {
            startTimestampMs = timestampMs;
        }

        // Get CSS style from tag (if any)
        if (tag.startsWith('%c')) {
            const style = escapeHTML(values.shift());
            tag = `<span style="${style}">${escapeHTML(tag.substring(2))}</span>`;
        } else {
            tag = escapeHTML(tag);
        }

        // Add element to log container
        elements.log.appendChild(createElementFromHTML(`

                    ${((timestampMs - startTimestampMs) / 1000).toFixed(3)}
                    ${tag}
                    ${values.map((value) => formatRecordValue(value)).join('\n')}

`));
    }
}

/**
 * Listen for *paste* events.
 */
document.addEventListener('paste', (event) => {
    // If no clipboard data is available, do nothing.
    let text;
    try {
        text = event.clipboardData.getData('text');
    } catch (error) {
        return console.error('Could not retrieve pasted data as text:', error);
    }

    // Show log
    showLog(text);
});

/**
 * Listen for *drag* events.
 */
document.addEventListener('dragover', (event) => {
    event.preventDefault();
});
document.addEventListener('dragenter', () => {
    elements.wrapper.className = 'drag-over';
});
document.addEventListener('drop', (event) => {
    event.preventDefault();
    elements.wrapper.className = '';

    // Read first file (if any)
    const files = event.dataTransfer.files;
    if (files.length === 0) {
        console.error('No files in drop event');
        return;
    }
    const reader = new FileReader();
    reader.addEventListener('load', (event) => {
        showLog(event.target.result);
    });
    reader.readAsText(files[0]);
});
<|endoftext|>


<commit_before>import posix from 'posix';
import Project from '../lib/project';

async function run() {
  const project = new Project();
  await* [
    project.linkAll(),
    project.compile()
  ];

  const currentNpmCommand = JSON.parse(process.env.npm_config_argv).original[0];
  if (currentNpmCommand === 'publish') {
    const output = await project.directory.execSh('npm publish dist');
    process.stdout.write(`${output}\n`);
    process.kill(posix.getppid());
  }
}

run().catch(e => {
  process.stderr.write(e.stack + '\n');
  process.exit(1);
});
<commit_msg>Only compile into dist at publish time<commit_after>import posix from 'posix';
import Project from '../lib/project';

async function run() {
  const project = new Project();
  const currentNpmCommand = JSON.parse(process.env.npm_config_argv).original[0];
  if (currentNpmCommand === 'install') {
    await project.linkAll();
  } else if (currentNpmCommand === 'publish') {
    await project.compile();
    const output = await project.directory.execSh('npm publish dist');
    process.stdout.write(`${output}\n`);
    process.kill(posix.getppid());
    await project.directory.rm('dist');
  }
}

run().catch(e => {
  process.stderr.write(e.stack + '\n');
  process.exit(1);
});
<|endoftext|>


<commit_before>"use strict";

var nextId = 0;

/*
$at_class bcdfghjklmnpqrstvwxyz=C aeiou=V
$at_special y:VyV=C y:VyC=V y:CyC=V
*/

var atlUtils = {
    replaceAt: function(str, index, ch) {
        return str.substr(0, index) + ch + str.substr(index+ch.length);
    }
}

var loader = {
    id: "autotokens",
    name: "Automagical Tokenization",

    load: function(tokens, lists, options) {
        // Make sure defaults are in order.
        if(this.defaults.classes === null) {
            this.setupDefaults();
        }

        var classes = this.defaults.classes;
        var specials = this.defaults.specials;
        var endings = this.defaults.endings;
        var flags = this.defaults.flags;

        var optSyllables = false;
        if(options.hasOwnProperty('at_syllables')) {
            optSyllables = (options.at_syllables[0].toLowerCase() === 'true');
        }

        if(options.hasOwnProperty('at_classes')) {
            var newClasses = {};
            var atClasses = options.at_classes;

            for (var cl in classes) {
                if(classes.hasOwnProperty(cl)) {
                    newClasses[cl] = classes[cl];
                }
            }

            for(var i = 0; i < atClasses.length; ++i) {
                var assignment = atClasses[i].split('=');

                if(assignment.length < 2) {
                    throw new Error('at classes need to be formatted "aeiou=V" or "Ø=V" for single letters');
                }

                var letters = assignment[0];
                var letterClass = assignment[1];

                for(var j = 0; j < letters.length; ++j) {
                    newClasses[letters[j]] = letterClass;
                }
            }

            classes = newClasses;
        }

        // Add custom endings
        if(options.hasOwnProperty('at_endings')) {
            endings = options.at_endings.concat(endings);
        }

        if(options.hasOwnProperty('at_flags')) {
            var newFlags = {};
            var flagKeys = Object.keys(flags);

            for(var i = 0; i < flagKeys; ++i) {
                newFlags[flagKeys[i]] = flags[flagKeys[i]];
            }

            for(var i = 0; i < options.at_flags.length; ++i) {
                newFlags[options.at_flags[i]] = true;
            }

            flags = newFlags;
        }

        for(var i = 0; i < tokens.length; ++i) {
            if(i >= options.list.length) {
                break;
            }

            var listId = options.list[i];
            var list = null, group = null;
            var token = tokens[i].toLowerCase();

            // Ensure the list exists, and select it.
            if(!lists.hasOwnProperty(listId)) {
                lists[listId] = [];
            }
            list = lists[listId];

            // Tokenize
            var r = null, headers;

            if(optSyllables) {
                r = this.getSyllables(token, classes, specials, flags);
                this.applyEndings(r, endings, flags);

                headers = [r.classes.join(',')];
            } else {
                r = this.getFragments(token, classes, specials, flags);
                this.applyEndings(r, endings, flags);
                this.applyNumbers(r, flags);

                headers = r.classes;
            }

            // Construct group id
            var groupId = r.classes.join(',');

            // Try find group
            for(var j = 0; j < list.length; ++j) {
                if(list[j].id == groupId) {
                    group = list[j];
                    break;
                }
            }

            // If unsuccessful, make the group.
            if(group === null) {
                group = {
                    id: groupId,
                    headers: headers,
                    lines: []
                }

                list.push(group);
            }

            // Add the tokens to the group.
            group.lines.push(r.fragments);
        }
    },

    getSyllables: function(name, classes, specials, flags) {
        var cs = this.getClasses(name, classes, specials);

        var r = {
            fragments: [],
            classes: []
        };

        var prev = 'V';
        var currFragment = "";
        var currClass = "";

        for(var i = 0; i < name.length; ++i) {
            var ch = name.charAt(i);
            var cl = cs[i];

            if(cl !== 'V' && prev === 'V') {
                if(currFragment.length > 0) {
                    r.fragments.push(currFragment);
                    r.classes.push(currClass);
                }

                currFragment = "";
                currClass = "";
            }

            currFragment += ch;
            currClass += cl;
            prev = cl;
        }

        if(currFragment.length > 0) {
            r.fragments.push(currFragment);
            r.classes.push(currClass);
        }

        return r;
    },

    getFragments: function(name, classes, specials, flags) {
        var cs = this.getClasses(name, classes, specials);

        var r = {
            fragments: [],
            classes: []
        };

        var prev = 'V';
        var currFragment = "";
        var currClass = "";

        for(var i = 0; i < name.length; ++i) {
            var ch = name.charAt(i);
            var cl = cs[i];

            if(flags.nvc && prev !== null && prev !== 'V') {
                if(cl !== 'V') {
                    prev = cl;
                } else {
                    prev = 'C';
                }
            }

            if(cl !== prev) {
                if(currFragment.length > 0) {
                    r.fragments.push(currFragment);
                    r.classes.push(currClass);
                }

                currFragment = ch;
                currClass = cl;

                prev = cl;
            } else {
                currFragment += ch;
                currClass += cl;

                prev = cl;
            }
        }

        if(currFragment.length > 0) {
            r.fragments.push(currFragment);
            r.classes.push(currClass);
        }

        return r;
    },

    applyEndings: function(r, endings) {
        for(var i = 0; i < endings.length; ++i) {
            var ending = endings[i];
            var valid = null;
            var ef = "";
            var ec = "";

            for(var j = r.fragments.length - 1; j > 0; --j) {
                ef = r.fragments[j] + ef;
                ec = r.classes[j] + ec;

                if(ec.length > ending.length) {
                    break;
                }

                if(ec.length === ending.length) {
                    if(valid !== null && valid.class.length > ending.length) {
                        break;
                    }

                    var match = true;

                    for(var k = 0; k < ending.length; ++k) {
                        var ech = ending[k];

                        if(ech === ech.toUpperCase()) {
                            if(ef.charAt(k) === ef.charAt(k + 1)) {
                                ec = atlUtils.replaceAt(ec, k, 'D');
                            }

                            if(ec[k] !== ech) {
                                match = false;
                                break;
                            }
                        } else {
                            if(ef[k] !== ech) {
                                match = false;
                                break;
                            }
                        }
                    }

                    if(match) {
                        valid = {
                            index: j,
                            fragment: ef,
                            class: ec
                        };
                    }
                }
            }

            if(valid !== null) {
                r.fragments.splice(valid.index);
                r.classes.splice(valid.index);
                r.fragments.push(valid.fragment);
                r.classes.push(valid.class);
            }
        }
    },

    applyNumbers: function(r, flags) {
        var ev = false;
        var sv = false;

        // Start
        if(r.classes.length > 0) {
            sv = (r.classes[0].charAt(0) === 'V');

            r.classes[0] = 'S' + r.classes[0];

            if(flags.lrs) {
                var len =  r.classes.join('').length;
                r.classes[0] = r.classes[0] + "." + len
            }
        } else {
            throw new Error("Can't apply numbers to 0 classes");
        }

        // Ending
        if(r.classes.length > 1) {
            var ind = r.classes.length - 1;
            ev = (r.classes[ind].charAt(0) === 'V');

            r.classes[ind] = 'E' + r.classes[ind];
        }

        // The rest
        var lc = 0;
        var cindex = 1;
        if(!sv) {
            lc = 0;
        }

        var len = r.classes.length;
        for(var i = 1; i < len - 1; ++i) {
            var cl = r.classes[i];
            var isV = (cl.charAt(0) === 'V');

            if(!flags.npn) {
                if(ev && i == len - 2) { // SC V *C* EV
                    cl = cl + 'L'
                } else if(!ev && i >= len - 3) { // SC V *C* *E* C
                    cl = cl + 'L'
                } else { // SC *V* *C* *V* C V EC
                    if(isV) {
                        cl = cl + cindex;
                        cindex++;
                    } else {
                        cl = cl + cindex;
                    }
                }
            }

            r.classes[i] = cl;
        }

        // Length restriction
        if(flags.lrs) {
            r.classes[0] = r.classes[0] + '.' + cindex;
        }

        if(flags.lre) {
            var last = r.classes.length - 1;
            r.classes[last] = r.classes[last] + '.' + cindex;
        }

        return r;
    },

    getClasses: function(name, classes, specials) {
        var results = [];

        for(var i = 0; i < name.length; ++i) {
            results.push(this.getClass(name, i, classes, specials));
        }

        return results;
    },

    getClass: function(name, pos, classes, specials) {
        var ch = name.charAt(pos);

        if(!classes.hasOwnProperty(ch)) {
            classes[ch] = 'O';
        }

        if(specials.hasOwnProperty(ch)) {
            var sp = specials[ch];

            if(pos == 0) {
                if(name.length === 0) {
                    return classes[ch];
                } else {
                    if(name[pos + 1] === ch) {
                        var key = ch + ch;

                        if(sp.hasOwnProperty(key)) {
                            return sp[key];
                        } else {
                            return classes[ch];
                        }
                    } else {
                        var next = this.getClass(name, pos + 1, classes, specials);
                        var key = ch + next;

                        if(sp.hasOwnProperty(key)) {
                            return sp[key];
                        }
                    }
                }
            } else if (pos == name.length - 1) {
                var prev = this.getClass(name, pos - 1, classes, specials);
                var key = prev + ch;

                if(sp.hasOwnProperty(key)) {
                    return sp[key];
                }
            } else {
                if(name[pos + 1] === ch) {
                    var key = ch + ch;

                    if(sp.hasOwnProperty(key)) {
                        return sp[key];
                    } else {
                        return classes[ch];
                    }
                } else {
                    var prev = this.getClass(name, pos - 1, classes, specials);
                    var next = this.getClass(name, pos + 1, classes, specials);

                    var key = prev + ch + next;

                    if(sp.hasOwnProperty(key)) {
                        return sp[key];
                    }
                }
            }
        }

        return classes[ch];
    },

    setupDefaults: function() {
        var consonants = 'bcdfghjklmnpqrstvwxyz';
        var vowels = 'aeiou';
        this.defaults.classes = {};

        for(var i = 0; i < consonants.length; ++i) {
            this.defaults.classes[consonants[i]] = 'C';
        }

        for(var i = 0; i < vowels.length; ++i) {
            this.defaults.classes[vowels[i]] = 'V';
        }
    },

    defaults: {
       specials: {
           "y": {
               "VyV": 'C',
               "VyC": 'V',
               "CyV": 'C',
               "CyC": 'V',
               "Vy": 'V',
               "Cy": 'V',
               "yy": 'V',
               "VyO": 'V',
               "CyO": 'V',
           }
       },
       classes: null,
       endings: [
           "CV", "CCV", "CCCV", "VC", "VCC", "VCCC", "CVV", "CVVV"
       ],
       flags: {
           lre: false,
           lrs: false,
           npn: false,
           nvc: false
       }
   }
}

if(typeof(module) !== 'undefined' && typeof(module.exports) !== 'undefined') {
    module.exports = loader;
} else {
    loaders[loader.id] = loader;
}
<commit_msg>[Loader] Remove autotokens loader from master, as it is experimental<commit_after><|endoftext|>


<commit_before>// Initialize your app
var myApp = new Framework7();

// Export selectors engine
var $$ = Dom7;

// Add view
var mainView = myApp.addView('.view-main', {
    // Because we use fixed-through navbar we can enable dynamic navbar
    dynamicNavbar: true
});

// Callbacks to run specific code for specific pages, for example for About page:
myApp.onPageInit('about', function (page) {
    // run createContentPage func after link was clicked
    $$('.create-page').on('click', function () {
        createContentPage();
    });
});

// Generate dynamic page
var dynamicPageIndex = 0;
function createContentPage() {
	mainView.router.loadContent(
        '' +
        '
' +
        '
' +
        '
Back
' +
        '
Dynamic Page ' + (++dynamicPageIndex) + '
' +
        '
' +
        '
' +
        '
' +
        '  ' +
        '
' +
        '    ' +
        '
' +
        '
' +
        '
' +
        '
Here is a dynamic page created on ' + new Date() + ' !
' +
        '
Go back or go to Services.
' +
        '
' +
        '
' +
        '
' +
        '
' +
        '
'
    );
	return;
}<commit_msg>Remove unused JS<commit_after>// Initialize your app
var myApp = new Framework7();

// Export selectors engine
var $$ = Dom7;

// Add view
var mainView = myApp.addView('.view-main', {
    // Because we use fixed-through navbar we can enable dynamic navbar
    dynamicNavbar: true
});
<|endoftext|>


<commit_before>var handlebars = require("handlebars");
var request = require("request");

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position){
      position = position || 0;
      return this.substr(position, searchString.length) === searchString;
  };
}

if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.lastIndexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.split(search).join(replacement);
};

var tags;

var generateSlug = function (title) {
    var slug = '';
    var allowed = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -';
    for (var i = 0; i < title.length; i++) {
        var ch = title[i];
        if (allowed.indexOf(ch) >= 0) {
            slug += ch;
        }
    }
    slug = slug.toLowerCase().replaceAll(' ', '-');
    return slug;
};

// Get tagged projects from the public API
var getTaggedProjects = function (done) {
    var tagged = {};
    request('http://playcanvas.com/api/apps?limit=32&tags=tutorial', function (err, response, body) {
        if (err) {
            console.error(err);
            return;
        }

        var samples = JSON.parse(body).result;

        for (var i = 0; i < samples.length; i++) {
            var appUrl = samples[i].url;
            var editorUrl = 'https://playcanvas.com/editor/project/'+samples[i].project_id;
            var projectUrl = 'https://playcanvas.com/project/'+samples[i].project_id+'/overview';
            var name = samples[i].name;
            var tags = samples[i].tags || [];
            tags.splice(tags.indexOf('tutorial'),1); // remove tutorial
            var slug = generateSlug(name);

            // var path = 'en/tutorials/'+slug;
            var url = '/tutorials/'+slug;
            var path = 'en'+url;

            tagged[path] = {
                title: name,
                url: url,
                path: path,
                tags: tags,
                slug: slug,
                appUrl: appUrl,
                editorUrl: editorUrl,
                projectUrl: projectUrl
            };
        }

        done(tagged);
    });
};

var getTags = function (done) {
    request('https://playcanvas.com/api/tags', function (err, response, body) {
        var json = JSON.parse(body).result;
        var tags = json.filter(function (tag) {return tag.kind === 'app'});
        done(tags);
    });
}

module.exports = function makePlugin(dir) {
    // compile the template used to generate pages for tagged projects
    handlebars.registerPartial("template-tagged-project", fs.readFileSync("templates/partials/tutorial-tagged-project.tmpl.html", "utf-8"));
    taggedProjectTemplate = handlebars.compile(handlebars.partials["template-tagged-project"]);

    return function (opts) {
        return function (files, metalsmith, done) {
            var langs = ['en', 'ru', 'ja', 'zh'];

            var index = {}; // index object of tutorials
            var tags = [];
            var tagSet = {};

            for (var i = 0; i < langs.length; i++) {
                var lang = langs[i];
                index[lang] = {};
                // get all tutorials and insert into index
                for (var filepath in files) {
                    if (filepath.indexOf('.DS_Store') >= 0) continue;

                    var file = files[filepath];

                    if (filepath.startsWith(lang+'/'+dir)) {
                        if (filepath.endsWith('tutorials/index.html')) continue; // skip main index page

                        // separate tags
                        var tags = file.tags || '';
                        file.tags = tags.split(',').filter(function (tag) {return !!tag;}).map(function (tag) {return tag.trim()}) || [];
                        // file.tags.forEach(function (tag) {tagSet[tag]=true;});

                        index[lang][filepath] = {
                            title: file.title,
                            url: filepath.replace(lang, '').replace('index.html',''),
                            tags: file.tags
                        }
                    }
                }
            }

            getTags(function (tags) {
                getTaggedProjects(function (tagged) {
                    // add tagged projects into main index
                    for (var key in tagged) {

                        for (var i = 0; i < langs.length; i++) {
                            var lang = langs[i];

                            // insert tagged project into index
                            index[lang][key] = tagged[key];

                            // create a new output file for each tagged project
                            var file = {
                                title: tagged[key].title,
                                template: 'tutorial-page.tmpl.html',
                                tags: tagged[key].tags,
                                mode: '0644',
                                path: lang + tagged[key].url,
                                locale: lang,
                                en: true,
                                contents: new Buffer(taggedProjectTemplate(tagged[key]))
                            }
                            // add tags
                            // file.tags.forEach(function (tag) {tagSet[tag]=true;});

                            files[file.path+'/index.html'] = file;
                        }
                    }

                    var context = {
                        pages: {},
                        tags: tags
                    };

                    for (var lang in index) {
                        context.pages[lang] = [];
                        for (var key in index[lang]) {
                            context.pages[lang].push(index[lang][key]);
                        }

                        context.pages[lang].sort(function (a,b) {
                            if (a.title && b.title) {
                                var _a = a.title.toLowerCase();
                                var _b = b.title.toLowerCase();
                                if (_a < _b) return -1;
                                if (_a > _b) return 1;
                            }
                            return 0;
                        });

                    }

                    // tutorial main page filters
                    // render a new template using the template's template(!) and the content from the file tree
                    var contentsTemplatePartialName = 'template-tutorial-contents';
                    handlebars.registerPartial(contentsTemplatePartialName, fs.readFileSync("templates/partials/tutorial-contents.tmpl.html", "utf-8"));
                    var contentsTemplate = handlebars.compile(handlebars.partials[contentsTemplatePartialName])
                    var contents = contentsTemplate(context);

                    // registry the generated contents template as the partial used in the tutorial pages
                    handlebars.registerPartial('tutorials-navigation', contents);

                    // render a new template used to template the tag selection on a tutorial page
                    var contentsLinksTemplatePartialName = 'template-tutorial-contents-links';
                    handlebars.registerPartial(contentsLinksTemplatePartialName, fs.readFileSync("templates/partials/tutorial-contents-links.tmpl.html", "utf-8"));
                    var contentsTemplate = handlebars.compile(handlebars.partials[contentsLinksTemplatePartialName])
                    var contents = contentsTemplate(context);
                    handlebars.registerPartial('tutorials-navigation-links', contents);

                    // tutorial navigation
                    // render a new template using the template's template(!) and the content from the file tree
                    var listTemplatePartialName = 'template-tutorial-list';
                    handlebars.registerPartial(listTemplatePartialName, fs.readFileSync("templates/partials/tutorial-list.tmpl.html", "utf-8"));
                    var listTemplate = handlebars.compile(handlebars.partials[listTemplatePartialName])
                    var contents = listTemplate(context);

                    // generate pages for tagged projects

                    // registry the generated list template as the partial used in the tutorial pages
                    handlebars.registerPartial('tutorials-list', contents);;

                    done();
                });

            });
        };
    };
};
<commit_msg>filter out some tags: skype, tutorial, challenge<commit_after>var handlebars = require("handlebars");
var request = require("request");

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position){
      position = position || 0;
      return this.substr(position, searchString.length) === searchString;
  };
}

if (!String.prototype.endsWith) {
  String.prototype.endsWith = function(searchString, position) {
      var subjectString = this.toString();
      if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
        position = subjectString.length;
      }
      position -= searchString.length;
      var lastIndex = subjectString.lastIndexOf(searchString, position);
      return lastIndex !== -1 && lastIndex === position;
  };
}

String.prototype.replaceAll = function(search, replacement) {
    var target = this;
    return target.split(search).join(replacement);
};

var tags;

var generateSlug = function (title) {
    var slug = '';
    var allowed = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ -';
    for (var i = 0; i < title.length; i++) {
        var ch = title[i];
        if (allowed.indexOf(ch) >= 0) {
            slug += ch;
        }
    }
    slug = slug.toLowerCase().replaceAll(' ', '-');
    return slug;
};

// Get tagged projects from the public API
var getTaggedProjects = function (done) {
    var tagged = {};
    request('http://playcanvas.com/api/apps?limit=32&tags=tutorial', function (err, response, body) {
        if (err) {
            console.error(err);
            return;
        }

        var samples = JSON.parse(body).result;

        for (var i = 0; i < samples.length; i++) {
            var appUrl = samples[i].url;
            var editorUrl = 'https://playcanvas.com/editor/project/'+samples[i].project_id;
            var projectUrl = 'https://playcanvas.com/project/'+samples[i].project_id+'/overview';
            var name = samples[i].name;
            var tags = samples[i].tags || [];
            var slug = generateSlug(name);

            // var path = 'en/tutorials/'+slug;
            var url = '/tutorials/'+slug;
            var path = 'en'+url;

            tagged[path] = {
                title: name,
                url: url,
                path: path,
                tags: tags,
                slug: slug,
                appUrl: appUrl,
                editorUrl: editorUrl,
                projectUrl: projectUrl
            };
        }

        done(tagged);
    });
};

var getTags = function (done) {
    request('https://playcanvas.com/api/tags', function (err, response, body) {
        var json = JSON.parse(body).result;
        var tags = json.filter(function (tag) {
            if (['skype', 'tutorial', 'challenge'].indexOf(tag.id) >= 0) return false;
            return tag.kind === 'app'
        });
        done(tags);
    });
}

module.exports = function makePlugin(dir) {
    // compile the template used to generate pages for tagged projects
    handlebars.registerPartial("template-tagged-project", fs.readFileSync("templates/partials/tutorial-tagged-project.tmpl.html", "utf-8"));
    taggedProjectTemplate = handlebars.compile(handlebars.partials["template-tagged-project"]);

    return function (opts) {
        return function (files, metalsmith, done) {
            var langs = ['en', 'ru', 'ja', 'zh'];

            var index = {}; // index object of tutorials
            var tags = [];
            var tagSet = {};

            for (var i = 0; i < langs.length; i++) {
                var lang = langs[i];
                index[lang] = {};
                // get all tutorials and insert into index
                for (var filepath in files) {
                    if (filepath.indexOf('.DS_Store') >= 0) continue;

                    var file = files[filepath];

                    if (filepath.startsWith(lang+'/'+dir)) {
                        if (filepath.endsWith('tutorials/index.html')) continue; // skip main index page

                        // separate tags
                        var tags = file.tags || '';
                        file.tags = tags.split(',').filter(function (tag) {return !!tag;}).map(function (tag) {return tag.trim()}) || [];
                        // file.tags.forEach(function (tag) {tagSet[tag]=true;});

                        index[lang][filepath] = {
                            title: file.title,
                            url: filepath.replace(lang, '').replace('index.html',''),
                            tags: file.tags
                        }
                    }
                }
            }

            getTags(function (tags) {
                getTaggedProjects(function (tagged) {
                    // add tagged projects into main index
                    for (var key in tagged) {

                        for (var i = 0; i < langs.length; i++) {
                            var lang = langs[i];

                            // insert tagged project into index
                            index[lang][key] = tagged[key];

                            // create a new output file for each tagged project
                            var file = {
                                title: tagged[key].title,
                                template: 'tutorial-page.tmpl.html',
                                tags: tagged[key].tags,
                                mode: '0644',
                                path: lang + tagged[key].url,
                                locale: lang,
                                en: true,
                                contents: new Buffer(taggedProjectTemplate(tagged[key]))
                            }
                            // add tags
                            // file.tags.forEach(function (tag) {tagSet[tag]=true;});

                            files[file.path+'/index.html'] = file;
                        }
                    }

                    var context = {
                        pages: {},
                        tags: tags
                    };

                    for (var lang in index) {
                        context.pages[lang] = [];
                        for (var key in index[lang]) {
                            context.pages[lang].push(index[lang][key]);
                        }

                        context.pages[lang].sort(function (a,b) {
                            if (a.title && b.title) {
                                var _a = a.title.toLowerCase();
                                var _b = b.title.toLowerCase();
                                if (_a < _b) return -1;
                                if (_a > _b) return 1;
                            }
                            return 0;
                        });

                    }

                    // tutorial main page filters
                    // render a new template using the template's template(!) and the content from the file tree
                    var contentsTemplatePartialName = 'template-tutorial-contents';
                    handlebars.registerPartial(contentsTemplatePartialName, fs.readFileSync("templates/partials/tutorial-contents.tmpl.html", "utf-8"));
                    var contentsTemplate = handlebars.compile(handlebars.partials[contentsTemplatePartialName])
                    var contents = contentsTemplate(context);

                    // registry the generated contents template as the partial used in the tutorial pages
                    handlebars.registerPartial('tutorials-navigation', contents);

                    // render a new template used to template the tag selection on a tutorial page
                    var contentsLinksTemplatePartialName = 'template-tutorial-contents-links';
                    handlebars.registerPartial(contentsLinksTemplatePartialName, fs.readFileSync("templates/partials/tutorial-contents-links.tmpl.html", "utf-8"));
                    var contentsTemplate = handlebars.compile(handlebars.partials[contentsLinksTemplatePartialName])
                    var contents = contentsTemplate(context);
                    handlebars.registerPartial('tutorials-navigation-links', contents);

                    // tutorial navigation
                    // render a new template using the template's template(!) and the content from the file tree
                    var listTemplatePartialName = 'template-tutorial-list';
                    handlebars.registerPartial(listTemplatePartialName, fs.readFileSync("templates/partials/tutorial-list.tmpl.html", "utf-8"));
                    var listTemplate = handlebars.compile(handlebars.partials[listTemplatePartialName])
                    var contents = listTemplate(context);

                    // generate pages for tagged projects

                    // registry the generated list template as the partial used in the tutorial pages
                    handlebars.registerPartial('tutorials-list', contents);;

                    done();
                });

            });
        };
    };
};
<|endoftext|>


<commit_before>$(document).ready(function() {
    //    随机头像
    var base_img_url="https://blog.mana.love/img/avatar_";
    var avatar=$('.avatar_a');
    var str='';
    avatar.empty().append(str);
    console.log(str);
});
<commit_msg>avatar<commit_after>$(document).ready(function() {
    //    随机头像
    var base_img_url = "https://blog.mana.love/img/avatar_";
    var avatar = $('.avatar_a');
    var str = '';
    if ($(".avatar_a>img").length == 0) {
        avatar.empty().append(str);
    } else {
        $(".avatar_a>img").attr('src', base_img_url + parseInt(19 * Math.random()) + '.png');
    }
    console.log(str);
});
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var keylime = require('keylime'),
      mach = require('mach'),
      _ = require('lodash'),
      p = require('path'),
      Promise = require('bluebird'),

      Directory = require('./pimm/support/directory'),
      Path = require('./pimm/support/path'),
      Controller = require('./pimm/controller'),
      MethodMapper = require('./pimm/support/method_mapper'),

      Pimm = keylime('Pimm');

  Pimm
    .attr('dir', '.' + p.sep)
    .attr('port', 3000)
    .attr('middleware', [])

    .attr('_stack', mach.stack)
    .attr('_default_middleware', [mach.logger, mach.params])
    .attr('_config', {
      environment: process.env.NODE_ENV
    })

    /**
     * Interface to a generic configuration storage. Pimm does not use
     * this config interface for determining behavior, but you can use
     * it for storing global configuration values.
     *
     *    // main application.js file
     *    app
     *      .config('redis', {host: 'localhost'})
     *      .config('debug', true);
     *
     *    // some other module.js
     *    var app = require('./application');
     *
     *    app.config('redis');   // => gets the redis conf
     *    app.config();          // => gets all config
     */
    .method('config', function config() {
      var args = [].slice.call(arguments);

      switch (_.size(args)) {
        case 2:
          this._config[args[0]] = args[1];
          return this;
        case 1:
          return this._config[args[0]];
        default:
          return this._config;
      }
    })

    /**
     * Adds middleware to the stack (chainable);
     *
     *    app
     *      .use(someExternalMiddleware())
     *      .use(function(request) {
     *        if (request['x-auth-token']) {
     *          // check user
     *        }
     *      });
     */
    .method('use', function use(middleware) {
      if (!_.isFunction(middleware)) {
        throw new Error('You must supply a function argument to the \'use\' method.');
      }
      this.middleware.push(middleware);
      return this;
    })

    /**
     * Starts the application.
     *
     *    app.start().then(function() {
     *      console.log('App started!');
     *    });
     */
    .method('start', function start() {
      this._loadMiddleware();
      return this._loadControllers()
      .then(function() {

        mach.serve(this._stack, this.port);
        return true;
      });
    })

    .method('_loadMiddleware', function loadMiddleware() {
      _.each(_.flatten([this._default_middleware, this.middleware]), function(middleware) {
        this._stack.use(middleware);
      }, this);
    })

    .method('_loadControllers', function loadControllers() {
      var controllerPath = new Path({value: this.dir}).resolve('controllers'),
          controllerDir = new Directory({path: controllerPath});

      return Promise.bind(this)
      .then(function() {
        return controllerDir.load();
      })
      .then(function() {
        _.forIn(controllerDir.files, function(Controller, path) {
          var controller = new Controller(),
              partialPath = new Path({value: path}).withoutExtension(),
              subRoute = _.first(_.at(partialPath, 0)) === '.' ? _.rest(partialPath).join('') : partialPath,
              mapper = new MethodMapper({methods: controller._getMethods()}).map();
          this._stack.map(subRoute, function(app) {
            app.run(mapper.stack);
          });
        }, this);
      });
    });

  module.exports = Pimm;
  module.exports.Controller = Controller;
}());
<commit_msg>add Pimm#routes<commit_after>(function() {
  'use strict';

  var keylime = require('keylime'),
      mach = require('mach'),
      _ = require('lodash'),
      p = require('path'),
      Promise = require('bluebird'),

      Directory = require('./pimm/support/directory'),
      Path = require('./pimm/support/path'),
      Controller = require('./pimm/controller'),
      MethodMapper = require('./pimm/support/method_mapper'),
      Router = require('./pimm/router'),

      Pimm = keylime('Pimm');

  Pimm
    .attr('dir', '.' + p.sep)
    .attr('port', 3000)
    .attr('middleware', [])

    .attr('_stack', mach.stack)
    .attr('_default_middleware', [mach.logger, mach.params])
    .attr('_config', {
      environment: process.env.NODE_ENV
    })
    .attr('_router', Router)

    /**
     * Interface to a generic configuration storage. Pimm does not use
     * this config interface for determining behavior, but you can use
     * it for storing global configuration values.
     *
     *    // main application.js file
     *    app
     *      .config('redis', {host: 'localhost'})
     *      .config('debug', true);
     *
     *    // some other module.js
     *    var app = require('./application');
     *
     *    app.config('redis');   // => gets the redis conf
     *    app.config();          // => gets all config
     */
    .method('config', function config() {
      var args = [].slice.call(arguments);

      switch (_.size(args)) {
        case 2:
          this._config[args[0]] = args[1];
          return this;
        case 1:
          return this._config[args[0]];
        default:
          return this._config;
      }
    })

    /**
     * Adds middleware to the stack (chainable);
     *
     *    app
     *      .use(someExternalMiddleware())
     *      .use(function(request) {
     *        if (request['x-auth-token']) {
     *          // check user
     *        }
     *      });
     */
    .method('use', function use(middleware) {
      if (!_.isFunction(middleware)) {
        throw new Error('You must supply a function argument to the \'use\' method.');
      }
      this.middleware.push(middleware);
      return this;
    })

    /**
     * Add routes to the Pimm application. Use the `this` keyword
     * inside the function to add routes.
     *
     * (See router.js to see what routing methods are available)
     *
     *    app.routes(function() {
     *      this.resources('posts');
     *
     *      this.namespace('admin', function() {
     *        this.resources('users');
     *      });
     *    });
     */
    .method('routes', function routes(func) {
      if (_.isFunction(func)) {
        func.call(this._router);
      }
      return this;
    })

    /**
     * Starts the application.
     *
     *    app.start().then(function() {
     *      console.log('App started!');
     *    });
     */
    .method('start', function start() {
      this._loadMiddleware();
      return this._loadControllers()
      .then(function() {

        mach.serve(this._stack, this.port);
        return true;
      });
    })

    .method('_loadMiddleware', function loadMiddleware() {
      _.each(_.flatten([this._default_middleware, this.middleware]), function(middleware) {
        this._stack.use(middleware);
      }, this);
    })

    .method('_loadControllers', function loadControllers() {
      var controllerPath = new Path({value: this.dir}).resolve('controllers'),
          controllerDir = new Directory({path: controllerPath});

      return Promise.bind(this)
      .then(function() {
        return controllerDir.load();
      })
      .then(function() {
        _.forIn(controllerDir.files, function(Controller, path) {
          var controller = new Controller(),
              partialPath = new Path({value: path}).withoutExtension(),
              subRoute = _.first(_.at(partialPath, 0)) === '.' ? _.rest(partialPath).join('') : partialPath,
              mapper = new MethodMapper({methods: controller._getMethods()}).map();
          this._stack.map(subRoute, function(app) {
            app.run(mapper.stack);
          });
        }, this);
      });
    });

  module.exports = Pimm;
  module.exports.Controller = Controller;
}());
<|endoftext|>


<commit_before>
export const KEYS = {
  ESC: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  ENTER: 13,
  TAB: 9
};

export function getMatch(subString, array, key) {
  return array
    .filter(function(string) {
      if (key) { string = string[key]; }
      return string.toLowerCase().indexOf(subString.toLowerCase()) > -1 &&
        subString.length < string.length;
    })
    .sortBy('length');
}

export function setCursor(node, pos) {
  if (node) {
    if (node.createTextRange) {
      var textRange = node.createTextRange();
      textRange.collapse(true);
      textRange.moveEnd(pos);
      textRange.moveStart(pos);
      textRange.select();
      return true;
    } else if (node.setSelectionRange) {
      node.setSelectionRange(pos,pos);
      return true;
    }
  }
  return false;
}
<commit_msg>fixed an issue causing exceptions to be thrown<commit_after>
export const KEYS = {
  ESC: 27,
  LEFT: 37,
  UP: 38,
  RIGHT: 39,
  DOWN: 40,
  ENTER: 13,
  TAB: 9
};

export function getMatch(subString, array, key) {
  return array.filter(function(string) {
      if (key) {
        string = string[key];
      }
      if (string === undefined || string === null) {
        return false;
      }
      return string.toLowerCase().indexOf(subString.toLowerCase()) > -1 && subString.length < string.length;
    }).sortBy('length');
}

export function setCursor(node, pos) {
  if (node) {
    if (node.createTextRange) {
      var textRange = node.createTextRange();
      textRange.collapse(true);
      textRange.moveEnd(pos);
      textRange.moveStart(pos);
      textRange.select();
      return true;
    } else if (node.setSelectionRange) {
      node.setSelectionRange(pos,pos);
      return true;
    }
  }
  return false;
}
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        root.interact = factory();
    }
}(this, function () {
    // http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
    var isTouch = !!('ontouchstart' in window) // works on most browsers
      || !!('onmsgesturechange' in window); // works on ie10

    var interactions = [],
        minMoveDistance = 5,
        interact = {},
        maximumMovesToPersist = 1000, // Should be plenty..
        propertiesToCopy = 'target,pageX,pageY,clientX,clientY,offsetX,offsetY,screenX,screenY,shiftKey,x,y'.split(','); // Stuff that will be on every interaction.

    // document.body.style.webkitTouchCallout = 'none';
    // document.body.style.KhtmlUserSelect = 'none';

    function getActualTarget() {
        // For some reason touch browsers never change the event target during a touch.
        // This is, lets face it, fucking stupid.
        return document.elementFromPoint(this.pageX - window.scrollX, this.pageY - window.scrollY);
    }

    function getMoveDistance(x1,y1,x2,y2){
        var adj = Math.abs(x1 - x2),
            opp = Math.abs(y1 - y2);

        return Math.sqrt(Math.pow(adj,2) + Math.pow(opp,2));
    }

    function destroyInteraction(interaction){
        for(var i = 0; i < interactions.length; i++){
            if(interactions[i].identifier === interaction.identifier){
                interactions.splice(i,1);
            }
        }
    }

    function getInteraction(identifier){
        for(var i = 0; i < interactions.length; i++){
            if(interactions[i].identifier === identifier){
                return interactions[i];
            }
        }
    }

    function copyInteractionInfo(target, interactionInfo){
        // Some touch objects are immutable, so we need to copy their properties over.
        for(var i = 0;  i < propertiesToCopy.length; i++){
            target[propertiesToCopy[i]] = interactionInfo[propertiesToCopy[i]];
        }

        return target;
    }

    function Interaction(event, interactionInfo){
        // If there is no event (eg: desktop) just make the identifier undefined
        if(!event){
            event = {};
        }
        // If there is no extra info about the interaction (eg: desktop) just use the event itself
        if(!interactionInfo){
            interactionInfo = event;
        }

        // If there is another interaction with the same ID, something went wrong.
        // KILL IT WITH FIRE!
        var oldInteraction = getInteraction(interactionInfo.identifier);
        oldInteraction && oldInteraction.destroy();

        this.identifier = interactionInfo.identifier;

        this.moves = [];

        interactions.push(this);
    }

    Interaction.prototype = {
        constructor: Interaction,
        getActualTarget: getActualTarget,
        destroy: function(){
            trigger('destroy', this.target, this, this);
            destroyInteraction(this);
        },
        start: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            // store the info of where the interaction started
            this.lastStart = copyInteractionInfo({}, interactionInfo);

            copyInteractionInfo(this, interactionInfo);

            trigger('start', event.target, event, this);
            return this;
        },
        move: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            var currentTouch = copyInteractionInfo({}, interactionInfo);
            currentTouch.time = new Date();

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            // Memory saver, culls any moves that are over the maximum to keep.
            this.moves = this.moves.slice(-maximumMovesToPersist);

            this.moves.push(currentTouch);

            var lastMove = this.moves[this.moves.length-2];
            lastMove && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastMove.pageY, currentTouch.pageX - lastMove.pageX) * 180 / Math.PI);
            this.angle = currentTouch.angle || 0;

            trigger('move', event.target, event, this);
            return this;
        },
        drag: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            var currentTouch = copyInteractionInfo({}, interactionInfo);
            currentTouch.time = new Date();
            currentTouch.isDrag = true;

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            if(!this.moves){
                this.moves = [];
            }

            // Memory saver, culls any moves that are over the maximum to keep.
            this.moves = this.moves.slice(-maximumMovesToPersist);

            this.moves.push(currentTouch);

            if(!this.dragStarted && getMoveDistance(this.lastStart.pageX, this.lastStart.pageY, currentTouch.pageX, currentTouch.pageY) > minMoveDistance){
                this.dragStarted = true;
            }

            var lastDrag = this.moves[this.moves.length-2];
            lastDrag && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastDrag.pageY, currentTouch.pageX - lastDrag.pageX) * 180 / Math.PI);
            this.angle = currentTouch.angle || 0;

            if(this.dragStarted){
                trigger('drag', event.target, event, this);
            }
            return this;
        },
        end: function(event, interactionInfo, activate){
            // If you don't know what this is for by now, you're an idiot...
            if(!interactionInfo){
                interactionInfo = event;
            }

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            // If the interaction didnt move further than the minMoveDistance,
            // and we are not explicitly telling the interaction it is not an activate (eg, mouse click)
            // or we are forcing an activation (on 'click' for example)
            // Trigger an activate.

            if(activate === true || (activate !== false && getMoveDistance(this.pageX, this.lastStart.pageY, this.pageX, this.end.pageY) < minMoveDistance)){
                trigger('activate', event.target, event, this);
            }

            trigger('end', event.target, event, this);

            return this;
        },
        getMoveDistance: function(){
            if(this.moves.length > 1){
                var current = this.moves[this.moves.length-1],
                    previous = this.moves[this.moves.length-2];

                return getMoveDistance(current.pageX, current.pageY, previous.pageX, previous.pageY);
            }
        },
        getSpeed: function(){
            if(this.moves.length > 1){
                var current = this.moves[this.moves.length-1],
                    previous = this.moves[this.moves.length-2];

                return this.getMoveDistance() / (current.time - previous.time);
            }
            return 0;
        },
        getCurrentAngle: function(blend){
            var currentPosition,
                lastAngle,
                i = this.moves.length-1,
                angle,
                firstAngle,
                angles = [],
                blendSteps = 20/(this.getSpeed()*2+1),
                stepsUsed = 0;

            if(this.moves && this.moves.length){

                currentPosition = this.moves[i];
                angle = firstAngle = currentPosition.angle;

                if(blend && this.moves.length > 1){
                    while(--i > 0 && this.moves.length - i < blendSteps){
                        lastAngle = this.moves[i].angle;
                        if(Math.abs(lastAngle - firstAngle) > 180){
                            angle -= lastAngle
                        }else{
                            angle += lastAngle
                        }
                        stepsUsed++;
                    }
                    angle = angle/stepsUsed;
                }
            }
            return angle;
        },
        getAllInteractions: function(){
            return interactions.slice();
        }
    };

    function on(types, target, callback){
        var type;

        types = types.split(' ');

        for(var i = 0; i < types.length; i++){
            type = types[i];
            if(!target){
                callback = target;
                target = document;
            }

            if(!target._interactions){
                target._interactions = {};
            }
            if(!target._interactions[type]){
                target._interactions[type] = [];
            }
            target._interactions[type].push(callback);
        }

        return this;
    }

    // basic but functional
    function off(type, target, callback){
        var index = target._interactions[type].indexOf(callback);

        index >=0 && target._interactions[type].splice(index, 1);

        return this;
    }

    function trigger(type, target, event, interaction, eventInfo){
        var currentTarget = target,
            shouldPreventDefault = false,
            shouldStopPropagation = false;

        interaction.originalEvent = event;
        interaction.preventDefault = function(){
            shouldPreventDefault = true;
        }
        interaction.stopPropagation = function(){
            shouldStopPropagation = true;
        }

        while(currentTarget){
            currentTarget._interactions &&
            currentTarget._interactions[type] &&
            currentTarget._interactions[type].forEach(function(callback){
                callback(interaction);
            });
            currentTarget = currentTarget.parentNode;
        }
        if(shouldPreventDefault){
            event.preventDefault();
        }

        if(shouldStopPropagation){
            event.stopPropagation();
        }
    }

    function start(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            new Interaction(event, event.changedTouches[i]).start(event, touch);
        }
    }
    function drag(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            getInteraction(touch.identifier).drag(event, touch);
        }
    }
    function end(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            getInteraction(touch.identifier).end(event, touch).destroy();
        }
    }

    if(isTouch){
        addEvent(document, 'touchstart', start);
        addEvent(document, 'touchmove', drag);
        addEvent(document, 'touchend', end);
    }else{
        // there will only be one interaction on desktop. Make it now.
        new Interaction(event);

        var mouseIsDown = false;
        addEvent(document, 'mousedown', function(event){
            mouseIsDown = true;
            getInteraction().start(event);
        });
        addEvent(document, 'mousemove', function(event){
            if(!interactions.length){
                new Interaction(event);
            }
            if(mouseIsDown){
                getInteraction().drag(event);
            }else{
                getInteraction().move(event);
            }
        });
        addEvent(document, 'mouseup', function(event){
            mouseIsDown = false;
            getInteraction().end(event, null, false);
        });
        addEvent(document, 'click', function(event){
            getInteraction().end(event, null, true);
        });
    }

    function addEvent(element, type, callback) {
        if(element.addEventListener){
            element.addEventListener(type, callback);
        }
        else if(document.attachEvent){
            element.attachEvent("on"+ type, callback);
        }
    }

    interact.on = on;

    return interact;
})();<commit_msg>Exposed off<commit_after>(function (root, factory) {
    if (typeof exports === 'object') {
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        define(factory);
    } else {
        root.interact = factory();
    }
}(this, function () {
    // http://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript
    var isTouch = !!('ontouchstart' in window) // works on most browsers
      || !!('onmsgesturechange' in window); // works on ie10

    var interactions = [],
        minMoveDistance = 5,
        interact = {},
        maximumMovesToPersist = 1000, // Should be plenty..
        propertiesToCopy = 'target,pageX,pageY,clientX,clientY,offsetX,offsetY,screenX,screenY,shiftKey,x,y'.split(','); // Stuff that will be on every interaction.

    // document.body.style.webkitTouchCallout = 'none';
    // document.body.style.KhtmlUserSelect = 'none';

    function getActualTarget() {
        // For some reason touch browsers never change the event target during a touch.
        // This is, lets face it, fucking stupid.
        return document.elementFromPoint(this.pageX - window.scrollX, this.pageY - window.scrollY);
    }

    function getMoveDistance(x1,y1,x2,y2){
        var adj = Math.abs(x1 - x2),
            opp = Math.abs(y1 - y2);

        return Math.sqrt(Math.pow(adj,2) + Math.pow(opp,2));
    }

    function destroyInteraction(interaction){
        for(var i = 0; i < interactions.length; i++){
            if(interactions[i].identifier === interaction.identifier){
                interactions.splice(i,1);
            }
        }
    }

    function getInteraction(identifier){
        for(var i = 0; i < interactions.length; i++){
            if(interactions[i].identifier === identifier){
                return interactions[i];
            }
        }
    }

    function copyInteractionInfo(target, interactionInfo){
        // Some touch objects are immutable, so we need to copy their properties over.
        for(var i = 0;  i < propertiesToCopy.length; i++){
            target[propertiesToCopy[i]] = interactionInfo[propertiesToCopy[i]];
        }

        return target;
    }

    function Interaction(event, interactionInfo){
        // If there is no event (eg: desktop) just make the identifier undefined
        if(!event){
            event = {};
        }
        // If there is no extra info about the interaction (eg: desktop) just use the event itself
        if(!interactionInfo){
            interactionInfo = event;
        }

        // If there is another interaction with the same ID, something went wrong.
        // KILL IT WITH FIRE!
        var oldInteraction = getInteraction(interactionInfo.identifier);
        oldInteraction && oldInteraction.destroy();

        this.identifier = interactionInfo.identifier;

        this.moves = [];

        interactions.push(this);
    }

    Interaction.prototype = {
        constructor: Interaction,
        getActualTarget: getActualTarget,
        destroy: function(){
            trigger('destroy', this.target, this, this);
            destroyInteraction(this);
        },
        start: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            // store the info of where the interaction started
            this.lastStart = copyInteractionInfo({}, interactionInfo);

            copyInteractionInfo(this, interactionInfo);

            trigger('start', event.target, event, this);
            return this;
        },
        move: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            var currentTouch = copyInteractionInfo({}, interactionInfo);
            currentTouch.time = new Date();

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            // Memory saver, culls any moves that are over the maximum to keep.
            this.moves = this.moves.slice(-maximumMovesToPersist);

            this.moves.push(currentTouch);

            var lastMove = this.moves[this.moves.length-2];
            lastMove && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastMove.pageY, currentTouch.pageX - lastMove.pageX) * 180 / Math.PI);
            this.angle = currentTouch.angle || 0;

            trigger('move', event.target, event, this);
            return this;
        },
        drag: function(event, interactionInfo){
            // If there is no extra info about the interaction (eg: desktop) just use the event itself
            if(!interactionInfo){
                interactionInfo = event;
            }

            var currentTouch = copyInteractionInfo({}, interactionInfo);
            currentTouch.time = new Date();
            currentTouch.isDrag = true;

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            if(!this.moves){
                this.moves = [];
            }

            // Memory saver, culls any moves that are over the maximum to keep.
            this.moves = this.moves.slice(-maximumMovesToPersist);

            this.moves.push(currentTouch);

            if(!this.dragStarted && getMoveDistance(this.lastStart.pageX, this.lastStart.pageY, currentTouch.pageX, currentTouch.pageY) > minMoveDistance){
                this.dragStarted = true;
            }

            var lastDrag = this.moves[this.moves.length-2];
            lastDrag && (currentTouch.angle = Math.atan2(currentTouch.pageY - lastDrag.pageY, currentTouch.pageX - lastDrag.pageX) * 180 / Math.PI);
            this.angle = currentTouch.angle || 0;

            if(this.dragStarted){
                trigger('drag', event.target, event, this);
            }
            return this;
        },
        end: function(event, interactionInfo, activate){
            // If you don't know what this is for by now, you're an idiot...
            if(!interactionInfo){
                interactionInfo = event;
            }

            // Update the interaction
            copyInteractionInfo(this, interactionInfo);

            // If the interaction didnt move further than the minMoveDistance,
            // and we are not explicitly telling the interaction it is not an activate (eg, mouse click)
            // or we are forcing an activation (on 'click' for example)
            // Trigger an activate.

            if(activate === true || (activate !== false && getMoveDistance(this.pageX, this.lastStart.pageY, this.pageX, this.end.pageY) < minMoveDistance)){
                trigger('activate', event.target, event, this);
            }

            trigger('end', event.target, event, this);

            return this;
        },
        getMoveDistance: function(){
            if(this.moves.length > 1){
                var current = this.moves[this.moves.length-1],
                    previous = this.moves[this.moves.length-2];

                return getMoveDistance(current.pageX, current.pageY, previous.pageX, previous.pageY);
            }
        },
        getSpeed: function(){
            if(this.moves.length > 1){
                var current = this.moves[this.moves.length-1],
                    previous = this.moves[this.moves.length-2];

                return this.getMoveDistance() / (current.time - previous.time);
            }
            return 0;
        },
        getCurrentAngle: function(blend){
            var currentPosition,
                lastAngle,
                i = this.moves.length-1,
                angle,
                firstAngle,
                angles = [],
                blendSteps = 20/(this.getSpeed()*2+1),
                stepsUsed = 0;

            if(this.moves && this.moves.length){

                currentPosition = this.moves[i];
                angle = firstAngle = currentPosition.angle;

                if(blend && this.moves.length > 1){
                    while(--i > 0 && this.moves.length - i < blendSteps){
                        lastAngle = this.moves[i].angle;
                        if(Math.abs(lastAngle - firstAngle) > 180){
                            angle -= lastAngle
                        }else{
                            angle += lastAngle
                        }
                        stepsUsed++;
                    }
                    angle = angle/stepsUsed;
                }
            }
            return angle;
        },
        getAllInteractions: function(){
            return interactions.slice();
        }
    };

    function on(types, target, callback){
        var type;

        types = types.split(' ');

        for(var i = 0; i < types.length; i++){
            type = types[i];
            if(!target){
                callback = target;
                target = document;
            }

            if(!target._interactions){
                target._interactions = {};
            }
            if(!target._interactions[type]){
                target._interactions[type] = [];
            }
            target._interactions[type].push(callback);
        }

        return this;
    }

    // basic but functional
    function off(type, target, callback){
        var index = target._interactions[type].indexOf(callback);

        index >=0 && target._interactions[type].splice(index, 1);

        return this;
    }

    function trigger(type, target, event, interaction, eventInfo){
        var currentTarget = target,
            shouldPreventDefault = false,
            shouldStopPropagation = false;

        interaction.originalEvent = event;
        interaction.preventDefault = function(){
            shouldPreventDefault = true;
        }
        interaction.stopPropagation = function(){
            shouldStopPropagation = true;
        }

        while(currentTarget){
            currentTarget._interactions &&
            currentTarget._interactions[type] &&
            currentTarget._interactions[type].forEach(function(callback){
                callback(interaction);
            });
            currentTarget = currentTarget.parentNode;
        }
        if(shouldPreventDefault){
            event.preventDefault();
        }

        if(shouldStopPropagation){
            event.stopPropagation();
        }
    }

    function start(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            new Interaction(event, event.changedTouches[i]).start(event, touch);
        }
    }
    function drag(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            getInteraction(touch.identifier).drag(event, touch);
        }
    }
    function end(event){
        var touch;

        for(var i = 0; i < event.changedTouches.length; i++){
            touch = event.changedTouches[i];
            getInteraction(touch.identifier).end(event, touch).destroy();
        }
    }

    if(isTouch){
        addEvent(document, 'touchstart', start);
        addEvent(document, 'touchmove', drag);
        addEvent(document, 'touchend', end);
    }else{
        // there will only be one interaction on desktop. Make it now.
        new Interaction(event);

        var mouseIsDown = false;
        addEvent(document, 'mousedown', function(event){
            mouseIsDown = true;
            getInteraction().start(event);
        });
        addEvent(document, 'mousemove', function(event){
            if(!interactions.length){
                new Interaction(event);
            }
            if(mouseIsDown){
                getInteraction().drag(event);
            }else{
                getInteraction().move(event);
            }
        });
        addEvent(document, 'mouseup', function(event){
            mouseIsDown = false;
            getInteraction().end(event, null, false);
        });
        addEvent(document, 'click', function(event){
            getInteraction().end(event, null, true);
        });
    }

    function addEvent(element, type, callback) {
        if(element.addEventListener){
            element.addEventListener(type, callback);
        }
        else if(document.attachEvent){
            element.attachEvent("on"+ type, callback);
        }
    }

    interact.on = on;
    interact.off = off;

    return interact;
})();<|endoftext|>