<commit_before>const {DatabaseConnector} = require('nylas-core')

function cleanSubject(subject) {
  if (subject === null) {
    return ""
  }
  const regex = new RegExp(/^((re|fw|fwd|aw|wg|undeliverable|undelivered):\s*)+/ig)
  const cleanedSubject = subject.replace(regex, () => "")
  return cleanedSubject
}

function getThreadFromReferences({db, references}) {
  const {Message} = db
  const messageId = references.split()[references.length - 1]
  return Message.find({where: {messageId: messageId}})
  .then((message) => {
    return message.getThread()
  })
}

function matchThread({db, message}) {
  const {Thread} = db
  if (message.headers.references) {
    return getThreadFromReferences()
    .then((thread) => {
      if (thread) {
        return thread
      }
      return Thread.create({
        subject: message.subject,
        cleanedSubject: cleanSubject(message.subject),
      })
    })
  }

  return Thread.create({
    subject: message.subject,
    cleanedSubject: cleanSubject(message.subject),
  })
}

function addMessageToThread({db, accountId, message}) {
  const {Thread} = db
  if (message.threadId) {
    return Thread.find({where: {threadId: message.threadId}})
  }
  return matchThread({db, accountId, message})
  .then((thread) => (thread))
}

function processMessage({message, accountId}) {
  return DatabaseConnector.forAccount(accountId)
  .then((db) => addMessageToThread({db, accountId, message}))
  .then((thread) => {
    thread.addMessage(message)
    message.setThread(thread)
    return message
  })
}

// TODO: Incorporate this more elaborate threading algorithm
/*
function fetchCorrespondingThread({db, accountId, message}) {
  const cleanedSubject = cleanSubject(message.subject)
  return getThreads({db, message, cleanedSubject})
  .then((threads) => {
    return findCorrespondingThread({message, threads})
  })
}

function getThreads({db, message, cleanedSubject}) {
  const {Thread} = db
  return Thread.findAll({
    where: {
      threadId: message.headers.threadId,
      cleanedSubject: cleanedSubject,
    },
    order: [
      ['id', 'DESC']
    ],
  })
}

function findCorrespondingThread({message, threads}) {
  for (const thread of threads) {
    for (const match of thread.messages) {
      // Ignore BCC
      const {matchEmails, messageEmails} = removeBccParticipants({message, match})

      // A thread is probably related if it has more than two common participants
      const intersectingParticipants = getIntersectingParticipants({messageEmails, matchEmails})
      if (intersectingParticipants.length >= 2) {
        if (thread.messages.length >= MAX_THREAD_LENGTH)
          break
        return match.thread
      }

      // Handle case for self-sent emails
      if (!message.from || !message.to)
        return
      if (isSentToSelf({message, match})) {
        if (thread.messages.length >= MAX_THREAD_LENGTH)
          break
        return match.thread
      }
    }
  }
}

function removeBccParticipants({message, match}) {
  const matchBcc = match.bcc ? match.bcc : []
  const messageBcc = message.bcc ? message.bcc : []
  let matchEmails = match.participants.filter((participant) => {
    return matchBcc.find(bcc => bcc === participant)
  })
  matchEmails.map((email) => {
    return email[1]
  })
  let messageEmails = message.participants.filter((participant) => {
    return messageBcc.find(bcc => bcc === participant)
  })
  messageEmails.map((email) => {
    return email[1]
  })
  return {messageEmails, matchEmails}
}

function getIntersectingParticipants({messageEmails, matchEmails}) {
  const matchParticipants = new Set(matchEmails)
  const messageParticipants = new Set(messageEmails)
  const intersectingParticipants = new Set([...matchParticipants]
    .filter(participant => messageParticipants.has(participant)))
  return intersectingParticipants
}

function isSentToSelf({message, match}) {
  const matchFrom = match.from.map((participant) => {
    return participant[1]
  })
  const matchTo = match.to.map((participant) => {
    return participant[1]
  })
  const messageFrom = message.from.map((participant) => {
    return participant[1]
  })
  const messageTo = message.to.map((participant) => {
    return participant[1]
  })

  return (messageTo.length === 1 &&
    messageFrom === messageTo &&
    matchFrom === matchTo &&
    messageTo === matchFrom)
}
*/

module.exports = {
  order: 1,
  processMessage,
}
<commit_msg>Add check for Gmail thread ID<commit_after>const {DatabaseConnector} = require('nylas-core')

function cleanSubject(subject) {
  if (subject === null) {
    return ""
  }
  const regex = new RegExp(/^((re|fw|fwd|aw|wg|undeliverable|undelivered):\s*)+/ig)
  const cleanedSubject = subject.replace(regex, () => "")
  return cleanedSubject
}

function getThreadFromReferences({db, references}) {
  const {Message} = db
  const messageId = references.split()[references.length - 1]
  return Message.find({where: {messageId: messageId}})
  .then((message) => {
    return message.getThread()
  })
}

function matchThread({db, message}) {
  const {Thread} = db
  if (message.headers.references) {
    return getThreadFromReferences()
    .then((thread) => {
      if (thread) {
        return thread
      }
      return Thread.create({
        subject: message.subject,
        cleanedSubject: cleanSubject(message.subject),
      })
    })
  }

  return Thread.create({
    subject: message.subject,
    cleanedSubject: cleanSubject(message.subject),
  })
}

function addMessageToThread({db, accountId, message}) {
  const {Thread} = db
  // Check for Gmail's own thread ID
  if (message.headers['X-GM-THRID']) {
    return Thread.find({where: {threadId: message.headers['X-GM-THRID']})
  }
  return matchThread({db, accountId, message})
  .then((thread) => (thread))
}

function processMessage({message, accountId}) {
  return DatabaseConnector.forAccount(accountId)
  .then((db) => addMessageToThread({db, accountId, message}))
  .then((thread) => {
    thread.addMessage(message)
    message.setThread(thread)
    return message
  })
}

// TODO: Incorporate this more elaborate threading algorithm
/*
function fetchCorrespondingThread({db, accountId, message}) {
  const cleanedSubject = cleanSubject(message.subject)
  return getThreads({db, message, cleanedSubject})
  .then((threads) => {
    return findCorrespondingThread({message, threads})
  })
}

function getThreads({db, message, cleanedSubject}) {
  const {Thread} = db
  return Thread.findAll({
    where: {
      threadId: message.headers.threadId,
      cleanedSubject: cleanedSubject,
    },
    order: [
      ['id', 'DESC']
    ],
  })
}

function findCorrespondingThread({message, threads}) {
  for (const thread of threads) {
    for (const match of thread.messages) {
      // Ignore BCC
      const {matchEmails, messageEmails} = removeBccParticipants({message, match})

      // A thread is probably related if it has more than two common participants
      const intersectingParticipants = getIntersectingParticipants({messageEmails, matchEmails})
      if (intersectingParticipants.length >= 2) {
        if (thread.messages.length >= MAX_THREAD_LENGTH)
          break
        return match.thread
      }

      // Handle case for self-sent emails
      if (!message.from || !message.to)
        return
      if (isSentToSelf({message, match})) {
        if (thread.messages.length >= MAX_THREAD_LENGTH)
          break
        return match.thread
      }
    }
  }
}

function removeBccParticipants({message, match}) {
  const matchBcc = match.bcc ? match.bcc : []
  const messageBcc = message.bcc ? message.bcc : []
  let matchEmails = match.participants.filter((participant) => {
    return matchBcc.find(bcc => bcc === participant)
  })
  matchEmails.map((email) => {
    return email[1]
  })
  let messageEmails = message.participants.filter((participant) => {
    return messageBcc.find(bcc => bcc === participant)
  })
  messageEmails.map((email) => {
    return email[1]
  })
  return {messageEmails, matchEmails}
}

function getIntersectingParticipants({messageEmails, matchEmails}) {
  const matchParticipants = new Set(matchEmails)
  const messageParticipants = new Set(messageEmails)
  const intersectingParticipants = new Set([...matchParticipants]
    .filter(participant => messageParticipants.has(participant)))
  return intersectingParticipants
}

function isSentToSelf({message, match}) {
  const matchFrom = match.from.map((participant) => {
    return participant[1]
  })
  const matchTo = match.to.map((participant) => {
    return participant[1]
  })
  const messageFrom = message.from.map((participant) => {
    return participant[1]
  })
  const messageTo = message.to.map((participant) => {
    return participant[1]
  })

  return (messageTo.length === 1 &&
    messageFrom === messageTo &&
    matchFrom === matchTo &&
    messageTo === matchFrom)
}
*/

module.exports = {
  order: 1,
  processMessage,
}
<|endoftext|>


<commit_before>/*
 * This file is part of ORY Editor.
 *
 * ORY Editor is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ORY Editor is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with ORY Editor.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license LGPL-3.0
 * @copyright 2016-2018 Aeneas Rekkas
 * @author Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 */

/* eslint-disable no-alert, prefer-reflect, default-case, react/display-name */
import LinkIcon from '@material-ui/icons/Link'
import React, { Component } from 'react'
import TextField from '@material-ui/core/TextField'
import { ToolbarButton } from '../../helpers'
import Plugin from '../Plugin'
import Link from './node'
import Dialog from '@material-ui/core/Dialog'
import Button from '@material-ui/core/Button'
import { Data } from 'slate'
import type { Props } from '../props'
import ThemeProvider from 'ory-editor-ui/lib/ThemeProvider'

export const A = 'LINK/LINK'

class LinkButton extends Component {
  state = {
    open: false,
    href: '',
    title: '',
    hadLinks: false
  }

  props: Props

  input: Component<*, *, *>

  onRef = (component: Component<*, *, *>) => {
    if (!component && true) {
      return null
    }

    const e = component.querySelector('input')
    if (e) {
      e.focus()
    }
  }

  onClick = e => {
    const { editorState, onChange } = this.props
    e.preventDefault()

    const hasLinks = editorState.inlines.some(
      (inline: any) => inline.type === A
    )

    if (hasLinks) {
      const newState = editorState
        .transform()
        .unwrapInline(A)
        .apply()
      onChange(newState)
    } else if (editorState.isExpanded) {
      this.setState({
        open: true,
        wasExpanded: editorState.isExpanded,
        href: '',
        title: '',
        hadLinks: hasLinks
      })
    } else {
      this.setState({
        open: true,
        wasExpanded: editorState.isExpanded,
        href: '',
        title: '',
        hadLinks: hasLinks
      })
    }
  }

  handleClose = () => {
    this.setState({ open: false })

    const newState = this.props.editorState
      .transform()
      .focus()
      .apply()
    window.setTimeout(() => this.props.onChange(newState), 1)
  }

  handleSubmit = () => {
    this.setState({ open: false })

    if (!this.state.href) {
      this.handleClose()
      return
    }

    if (this.state.wasExpanded) {
      const newState = this.props.editorState
        .transform()
        .focus()
        .apply()
        .transform()
        .wrapInline({
          type: A,
          data: { href: this.state.href }
        })
        .collapseToEnd()
        .apply()

      window.setTimeout(() => this.props.onChange(newState), 1)
      window.setTimeout(() => this.props.focus(), 100)
      return
    }

    if (!this.state.title) {
      this.handleClose()
      return
    }

    const newState = this.props.editorState
      .transform()
      .insertText(this.state.title)
      .extend(-this.state.title.length)
      .wrapInline({
        type: A,
        data: { href: this.state.href }
      })
      .collapseToEnd()
      .focus()
      .apply()

    this.props.onChange(newState)
    window.setTimeout(() => this.props.focus(), 100)
  }

  onHrefChange = e => {
    this.setState({ href: e.target.value })
  }

  onTitleChange = e => {
    this.setState({ title: e.target.value })
  }

  render() {
    const actions = [
      <Button
        key="0"
        variant='flat'
        label="Cancel"
        primary
        onClick={this.handleClose}
      />,
      <Button
        key="1"
        variant='flat'
        label="Submit"
        primary
        onClick={this.handleSubmit}
      />
    ]
    const { editorState } = this.props

    const hasLinks = editorState.inlines.some(
      (inline: any) => inline.type === A
    )
    return (
      <ThemeProvider>

          <ToolbarButton
            onClick={this.onClick}
            isActive={hasLinks}
            icon={<LinkIcon />}
          />

            <Dialog
              className="ory-prevent-blur"
              title="Create a link"
              modal={false}
              open={this.state.open}
              actions={[actions]}
            >
              {this.state.wasExpanded ? null : (

                  <TextField
                    placeholder="Link title"
                    onChange={this.onTitleChange}
                    value={this.state.title}
                  />

              )}

                <TextField
                  placeholder="http://example.com/my/link.html"
                  onChange={this.onHrefChange}
                  value={this.state.href}
                />

            </Dialog>

      </ThemeProvider>
    )
  }
}

export default class LinkPlugin extends Plugin {
  name = 'link'

  nodes = { [A]: Link }

  hoverButtons = [LinkButton]
  toolbarButtons = [LinkButton]

  deserialize = (el, next) => {
    switch (el.tagName.toLowerCase()) {
      case 'a':
        return {
          kind: 'inline',
          type: A,
          nodes: next(el.childNodes),
          data: Data.create({
            href: (el.attrs.find(({ name }) => name === 'href') || {
              value: ''
            }).value
          })
        }
    }
  }

  serialize = (
    object: { type: string, kind: string, data: any },
    children: any[]
  ) => {
    if (object.kind !== 'inline') {
      return
    }
    switch (object.type) {
      case A:
        return {children}
    }
  }
}
<commit_msg>plugin/slate: Resolve broken link window (#552)<commit_after>/*
 * This file is part of ORY Editor.
 *
 * ORY Editor is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ORY Editor is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with ORY Editor.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @license LGPL-3.0
 * @copyright 2016-2018 Aeneas Rekkas
 * @author Aeneas Rekkas <aeneas+oss@aeneas.io>
 *
 */

/* eslint-disable no-alert, prefer-reflect, default-case, react/display-name */
import LinkIcon from '@material-ui/icons/Link'
import React, { Component } from 'react'
import TextField from '@material-ui/core/TextField'
import { ToolbarButton } from '../../helpers'
import Plugin from '../Plugin'
import Link from './node'
import Dialog from '@material-ui/core/Dialog'
import DialogTitle from '@material-ui/core/DialogTitle'
import DialogContent from '@material-ui/core/DialogContent'
import DialogActions from '@material-ui/core/DialogActions'
import Button from '@material-ui/core/Button'
import { Data } from 'slate'
import type { Props } from '../props'
import ThemeProvider from 'ory-editor-ui/lib/ThemeProvider'

export const A = 'LINK/LINK'

class LinkButton extends Component {
  state = {
    open: false,
    href: '',
    title: '',
    hadLinks: false
  }

  props: Props

  input: Component<*, *, *>

  onRef = (component: Component<*, *, *>) => {
    if (!component && true) {
      return null
    }

    const e = component.querySelector('input')
    if (e) {
      e.focus()
    }
  }

  onClick = e => {
    const { editorState, onChange } = this.props
    e.preventDefault()

    const hasLinks = editorState.inlines.some(
      (inline: any) => inline.type === A
    )

    if (hasLinks) {
      const newState = editorState
        .transform()
        .unwrapInline(A)
        .apply()
      onChange(newState)
    } else if (editorState.isExpanded) {
      this.setState({
        open: true,
        wasExpanded: editorState.isExpanded,
        href: '',
        title: '',
        hadLinks: hasLinks
      })
    } else {
      this.setState({
        open: true,
        wasExpanded: editorState.isExpanded,
        href: '',
        title: '',
        hadLinks: hasLinks
      })
    }
  }

  handleClose = () => {
    this.setState({ open: false })

    const newState = this.props.editorState
      .transform()
      .focus()
      .apply()
    window.setTimeout(() => this.props.onChange(newState), 1)
  }

  handleSubmit = () => {
    this.setState({ open: false })

    if (!this.state.href) {
      this.handleClose()
      return
    }

    if (this.state.wasExpanded) {
      const newState = this.props.editorState
        .transform()
        .focus()
        .apply()
        .transform()
        .wrapInline({
          type: A,
          data: { href: this.state.href }
        })
        .collapseToEnd()
        .apply()

      window.setTimeout(() => this.props.onChange(newState), 1)
      window.setTimeout(() => this.props.focus(), 100)
      return
    }

    if (!this.state.title) {
      this.handleClose()
      return
    }

    const newState = this.props.editorState
      .transform()
      .insertText(this.state.title)
      .extend(-this.state.title.length)
      .wrapInline({
        type: A,
        data: { href: this.state.href }
      })
      .collapseToEnd()
      .focus()
      .apply()

    this.props.onChange(newState)
    window.setTimeout(() => this.props.focus(), 100)
  }

  onHrefChange = e => {
    this.setState({ href: e.target.value })
  }

  onTitleChange = e => {
    this.setState({ title: e.target.value })
  }

  render() {
    const actions = <React.Fragment>
      <Button
        variant='flat'
        label="Cancel"
        primary
        onClick={this.handleClose}
      >
        Cancel
      </Button>
      <Button
        variant='flat'
        label="Ok"
        primary
        onClick={this.handleSubmit}
      >
        Ok
      </Button>
    </React.Fragment>
    const { editorState } = this.props

    const hasLinks = editorState.inlines.some(
      (inline: any) => inline.type === A
    )
    return (
      <ThemeProvider>

          <ToolbarButton
            onClick={this.onClick}
            isActive={hasLinks}
            icon={<LinkIcon />}
          />

            <Dialog
              className="ory-prevent-blur"
              title="Create a link"
              // modal={false}
              open={this.state.open}
              actions={[actions]}
            >
              <DialogTitle id="confirmation-dialog-title">Create a link</DialogTitle>
              <DialogContent>
                {this.state.wasExpanded ? null : (

                    <TextField
                      placeholder="Link title"
                      onChange={this.onTitleChange}
                      value={this.state.title}
                    />

                )}

                  <TextField
                    placeholder="http://example.com/my/link.html"
                    onChange={this.onHrefChange}
                    value={this.state.href}
                  />

              </DialogContent>
              <DialogActions>
                {actions}
              </DialogActions>
            </Dialog>

      </ThemeProvider>
    )
  }
}

export default class LinkPlugin extends Plugin {
  name = 'link'

  nodes = { [A]: Link }

  hoverButtons = [LinkButton]
  toolbarButtons = [LinkButton]

  deserialize = (el, next) => {
    switch (el.tagName.toLowerCase()) {
      case 'a':
        return {
          kind: 'inline',
          type: A,
          nodes: next(el.childNodes),
          data: Data.create({
            href: (el.attrs.find(({ name }) => name === 'href') || {
              value: ''
            }).value
          })
        }
    }
  }

  serialize = (
    object: { type: string, kind: string, data: any },
    children: any[]
  ) => {
    if (object.kind !== 'inline') {
      return
    }
    switch (object.type) {
      case A:
        return {children}
    }
  }
}
<|endoftext|>


<commit_before>"use strict";

const ReactDOM = require("react-dom/server");
const Helmet = require("react-helmet");
const Router = require("react-router");
const defaultTemplate = require("./defaultTemplate.js");

const serverContext = require("./serverContext.js");
const match = Router.match;

module.exports = function serverRender(cfg) {
    const cookies = cfg.cookies;
    const request = cfg.request;
    const wildcatConfig = cfg.wildcatConfig;

    return new Promise(function serverRenderPromise(resolve, reject) {
        match(cfg, function serverRenderMatch(error, redirectLocation, renderProps) {
            let result = {};

            if (error) {
                return reject(error);
            }

            if (redirectLocation) {
                result = {
                    redirect: true,
                    redirectLocation,
                    status: 301
                };
            } else if (!renderProps) {
                result = {
                    error: "Not found",
                    status: 404
                };
            } else {
                const initialData = {};

                return Promise.all(
                    renderProps.components
                        .filter(function renderPropsFilter(component) {
                            return component.prefetch;
                        })
                        .map(function renderPropsMap(component) {
                            const prefetch = component.prefetch;
                            const key = prefetch.getKey();

                            return prefetch.run(renderProps).then(
                                function renderPropsPrefetchResult(props) {
                                    initialData[key] = prefetch[key] = props;
                                }
                            );
                        })
                )
                    .then(function serverRenderPromiseResult() {
                        // FIXME: Remove this workaround when this issue is resolved:
                        // https://github.com/callemall/material-ui/pull/2007
                        global.navigator = global.navigator || {
                            userAgent: request.header["user-agent"]
                        };

                        const reactMarkup = ReactDOM.renderToString(
                            serverContext(request, cookies, renderProps)
                        );

                        const head = Object.assign({
                            link: "",
                            meta: "",
                            title: ""
                        }, Helmet.rewind());

                        const htmlTemplate = wildcatConfig.serverSettings.htmlTemplate || defaultTemplate;

                        const html = htmlTemplate({
                            data: initialData,
                            head: head,
                            html: reactMarkup,
                            wildcatConfig
                        });

                        result = Object.assign({}, result, {
                            html: html,
                            status: 200
                        });

                        return resolve(result);
                    });
            }

            return resolve(result);
        });
    });
};
<commit_msg>fix(react-wildcat-handoff): catch and reject rendering errors<commit_after>"use strict";

const ReactDOM = require("react-dom/server");
const Helmet = require("react-helmet");
const Router = require("react-router");
const defaultTemplate = require("./defaultTemplate.js");

const serverContext = require("./serverContext.js");
const match = Router.match;

module.exports = function serverRender(cfg) {
    const cookies = cfg.cookies;
    const request = cfg.request;
    const wildcatConfig = cfg.wildcatConfig;

    return new Promise(function serverRenderPromise(resolve, reject) {
        match(cfg, function serverRenderMatch(error, redirectLocation, renderProps) {
            let result = {};

            if (error) {
                return reject(error);
            }

            if (redirectLocation) {
                result = {
                    redirect: true,
                    redirectLocation,
                    status: 301
                };
            } else if (!renderProps) {
                result = {
                    error: "Not found",
                    status: 404
                };
            } else {
                const initialData = {};

                return Promise.all(
                    renderProps.components
                        .filter(function renderPropsFilter(component) {
                            return component.prefetch;
                        })
                        .map(function renderPropsMap(component) {
                            const prefetch = component.prefetch;
                            const key = prefetch.getKey();

                            return prefetch.run(renderProps).then(
                                function renderPropsPrefetchResult(props) {
                                    initialData[key] = prefetch[key] = props;
                                }
                            );
                        })
                )
                    .then(function serverRenderPromiseResult() {
                        // FIXME: Remove this workaround when this issue is resolved:
                        // https://github.com/callemall/material-ui/pull/2007
                        global.navigator = global.navigator || {
                            userAgent: request.header["user-agent"]
                        };

                        const reactMarkup = ReactDOM.renderToString(
                            serverContext(request, cookies, renderProps)
                        );

                        const head = Object.assign({
                            link: "",
                            meta: "",
                            title: ""
                        }, Helmet.rewind());

                        const htmlTemplate = wildcatConfig.serverSettings.htmlTemplate || defaultTemplate;

                        const html = htmlTemplate({
                            data: initialData,
                            head: head,
                            html: reactMarkup,
                            wildcatConfig
                        });

                        result = Object.assign({}, result, {
                            html: html,
                            status: 200
                        });

                        return resolve(result);
                    })
                    .catch(function serverRenderError(err) {
                        return reject(err);
                    });
            }

            return resolve(result);
        });
    });
};
<|endoftext|>


<commit_before>'use strict';
var domutils = require('domutils');
var fs = require('fs');
var he = require('he');
var URI = require('urijs');
var _ = require('lodash');

var sectionDB = require('../databases/sectionsDB');
var pointer = require('../pointer');
var EllucianBaseParser = require('./ellucianBaseParser').EllucianBaseParser;
var ellucianRequisitesParser = require('./ellucianRequisitesParser');
var ellucianRequisitesParser2 = require('./ellucianRequisitesParser2');

//700+ college sites use this poor interface for their registration
//good thing tho, is that it is easily scrapeable and does not require login to access seats avalible

function EllucianSectionParser() {
	EllucianBaseParser.prototype.constructor.apply(this, arguments);

	this.name = 'EllucianSectionParser';

	this.requiredAttrs = [
		"seatsCapacity",
		"seatsRemaining"
	];

	//minCredits and maxCredits are optional
}

//prototype constructor
EllucianSectionParser.prototype = Object.create(EllucianBaseParser.prototype);
EllucianSectionParser.prototype.constructor = EllucianSectionParser;

EllucianSectionParser.prototype.supportsPage = function (url) {
	return url.indexOf('bwckschd.p_disp_detail_sched') > -1;
};

EllucianSectionParser.prototype.getDatabase = function (pageData) {
	return sectionDB;
};

EllucianSectionParser.prototype.parseElement = function (pageData, element) {
	if (element.type != 'tag') {
		return;
	}

	if (element.name == 'table' && element.attribs.class == 'datadisplaytable' && element.parent.name == 'td' && _(element.attribs.summary).includes("seating")) {
		var tableData = this.parseTable(element);

		if (!tableData || tableData._rowCount === 0 || !tableData.capacity || !tableData.actual || !tableData.remaining) {
			elog('ERROR: invalid table in section parser', tableData, pageData.dbData.url);
			return;
		}

		//dont need to store all 3, if can determine the 3rd from the other 2 (yay math)
		var seatsCapacity = parseInt(tableData.capacity[0]);
		var seatsActual = parseInt(tableData.actual[0]);
		var seatsRemaining = parseInt(tableData.remaining[0]);

		if (seatsActual + seatsRemaining != seatsCapacity) {
			console.log('warning, actual + remaining != capacity', seatsCapacity, seatsActual, seatsRemaining, pageData.dbData.url);
		}

		pageData.setData('seatsCapacity', seatsCapacity);
		pageData.setData('seatsRemaining', seatsRemaining);

		if (tableData._rowCount > 1) {

			var waitCapacity = parseInt(tableData.capacity[1]);
			var waitActual = parseInt(tableData.actual[1]);
			var waitRemaining = parseInt(tableData.remaining[1]);

			if (waitActual + waitRemaining != waitCapacity) {
				console.log('warning, wait actual + remaining != capacity', waitCapacity, waitActual, waitRemaining, pageData.dbData.url);
			}

			pageData.setData('waitCapacity', waitCapacity);
			pageData.setData('waitRemaining', waitRemaining);
		}

		//third row is cross list seats, rarely listed and not doing anyting with that now
		// https://ssb.ccsu.edu/pls/ssb_cPROD/bwckschd.p_disp_detail_sched?term_in=201610&crn_in=12532

		//find co and pre reqs and restrictions
		var prereqs = ellucianRequisitesParser.parseRequirementSection(pageData, element.parent.children, 'prerequisites');
		if (prereqs) {
			pageData.setParentData('prereqs', prereqs);
		}

		var coreqs = ellucianRequisitesParser.parseRequirementSection(pageData, element.parent.children, 'corequisites');
		if (coreqs) {
			pageData.setParentData('coreqs', coreqs);
		}

		//find co and pre reqs and restrictions
		var prereqs2 = ellucianRequisitesParser2.parseRequirementSection(pageData, element.children, 'prerequisites');
		if (!_.isEqual(prereqs, prereqs2)) {
			console.log("WARNING: prereqs parsed by the new parser are not equal", JSON.stringify(prereqs, null, 4), JSON.stringify(prereqs2, null, 4), element.children)
		}

		var coreqs2 = ellucianRequisitesParser2.parseRequirementSection(pageData, element.children, 'corequisites');
		if (!_.isEqual(coreqs, coreqs2)) {
			console.log("WARNING: coreqs parsed by the new parser are not equal", JSON.stringify(coreqs, null, 4), JSON.stringify(coreqs2, null, 4), element.children)
		}

		//grab credits
		var text = domutils.getText(element.parent).toLowerCase()
		var creditsParsed = this.parseCredits(text);

		if (creditsParsed) {
			pageData.setParentData('maxCredits', creditsParsed.maxCredits);
			pageData.setParentData('minCredits', creditsParsed.minCredits);
		}
		else {
			console.log('warning, nothing matchied credits', pageData.dbData.url, text);
		}

		// HONORS NOTES
		// swathmore and sju have "honors" in title
		// https://myswat.swarthmore.edu/pls/bwckschd.p_disp_detail_sched?term_in=201602&crn_in=25340
		// https://myswat.swarthmore.edu/pls/bwckctlg.p_disp_course_detail?cat_term_in=201602&subj_code_in=MATH&crse_numb_in=016H
		// https://ssb.sju.edu/pls/PRODSSB/bwckctlg.p_disp_course_detail?cat_term_in=201610&subj_code_in=CHM&crse_numb_in=126

		// clemson has Honors in attributes
		// HOWEVER it also has a "includes honors sections" in the dsecription
		// https://sisssb.clemson.edu/sisbnprd/bwckschd.p_disp_detail_sched?term_in=201608&crn_in=87931

		// gatech has honors in title
		// neu has it in attributes, as different sections in same class
		// https://wl11gp.neu.edu/udcprod8/bwckctlg.p_disp_listcrse?term_in=201710&subj_in=CS&crse_in=1800&schd_in=LEC
		//
		// TLDR: class.honors = sectionHtml.includes('honors')

		// grab honors (honours is canadian spelling)
		if (_(text).includes('honors') || _(text).includes('honours')) {
			pageData.setParentData('honors', true)
		}
		else {
			pageData.setParentData('honors', false)
		}
	}
};

//this allows subclassing, http://bites.goodeggs.com/posts/export-this/ (Mongoose section)
EllucianSectionParser.prototype.EllucianSectionParser = EllucianSectionParser;
module.exports = new EllucianSectionParser();

if (require.main === module) {
	module.exports.tests();
}
<commit_msg>fixed a bug with testing the new prereq parser.<commit_after>'use strict';
var domutils = require('domutils');
var fs = require('fs');
var he = require('he');
var URI = require('urijs');
var _ = require('lodash');

var sectionDB = require('../databases/sectionsDB');
var pointer = require('../pointer');
var EllucianBaseParser = require('./ellucianBaseParser').EllucianBaseParser;
var ellucianRequisitesParser = require('./ellucianRequisitesParser');
var ellucianRequisitesParser2 = require('./ellucianRequisitesParser2');

//700+ college sites use this poor interface for their registration
//good thing tho, is that it is easily scrapeable and does not require login to access seats avalible

function EllucianSectionParser() {
	EllucianBaseParser.prototype.constructor.apply(this, arguments);

	this.name = 'EllucianSectionParser';

	this.requiredAttrs = [
		"seatsCapacity",
		"seatsRemaining"
	];

	//minCredits and maxCredits are optional
}

//prototype constructor
EllucianSectionParser.prototype = Object.create(EllucianBaseParser.prototype);
EllucianSectionParser.prototype.constructor = EllucianSectionParser;

EllucianSectionParser.prototype.supportsPage = function (url) {
	return url.indexOf('bwckschd.p_disp_detail_sched') > -1;
};

EllucianSectionParser.prototype.getDatabase = function (pageData) {
	return sectionDB;
};

EllucianSectionParser.prototype.parseElement = function (pageData, element) {
	if (element.type != 'tag') {
		return;
	}

	if (element.name == 'table' && element.attribs.class == 'datadisplaytable' && element.parent.name == 'td' && _(element.attribs.summary).includes("seating")) {
		var tableData = this.parseTable(element);

		if (!tableData || tableData._rowCount === 0 || !tableData.capacity || !tableData.actual || !tableData.remaining) {
			elog('ERROR: invalid table in section parser', tableData, pageData.dbData.url);
			return;
		}

		//dont need to store all 3, if can determine the 3rd from the other 2 (yay math)
		var seatsCapacity = parseInt(tableData.capacity[0]);
		var seatsActual = parseInt(tableData.actual[0]);
		var seatsRemaining = parseInt(tableData.remaining[0]);

		if (seatsActual + seatsRemaining != seatsCapacity) {
			console.log('warning, actual + remaining != capacity', seatsCapacity, seatsActual, seatsRemaining, pageData.dbData.url);
		}

		pageData.setData('seatsCapacity', seatsCapacity);
		pageData.setData('seatsRemaining', seatsRemaining);

		if (tableData._rowCount > 1) {

			var waitCapacity = parseInt(tableData.capacity[1]);
			var waitActual = parseInt(tableData.actual[1]);
			var waitRemaining = parseInt(tableData.remaining[1]);

			if (waitActual + waitRemaining != waitCapacity) {
				console.log('warning, wait actual + remaining != capacity', waitCapacity, waitActual, waitRemaining, pageData.dbData.url);
			}

			pageData.setData('waitCapacity', waitCapacity);
			pageData.setData('waitRemaining', waitRemaining);
		}

		//third row is cross list seats, rarely listed and not doing anyting with that now
		// https://ssb.ccsu.edu/pls/ssb_cPROD/bwckschd.p_disp_detail_sched?term_in=201610&crn_in=12532

		//find co and pre reqs and restrictions
		var prereqs = ellucianRequisitesParser.parseRequirementSection(pageData, element.parent.children, 'prerequisites');
		if (prereqs) {
			pageData.setParentData('prereqs', prereqs);
		}

		var coreqs = ellucianRequisitesParser.parseRequirementSection(pageData, element.parent.children, 'corequisites');
		if (coreqs) {
			pageData.setParentData('coreqs', coreqs);
		}

		//find co and pre reqs and restrictions
		var prereqs2 = ellucianRequisitesParser2.parseRequirementSection(pageData, element.parent.children, 'prerequisites');
		if (!_.isEqual(prereqs, prereqs2)) {
			console.log("WARNING: prereqs parsed by the new parser are not equal", JSON.stringify(prereqs, null, 4), JSON.stringify(prereqs2, null, 4), element.parent.children)
		}

		var coreqs2 = ellucianRequisitesParser2.parseRequirementSection(pageData, element.parent.children, 'corequisites');
		if (!_.isEqual(coreqs, coreqs2)) {
			console.log("WARNING: coreqs parsed by the new parser are not equal", JSON.stringify(coreqs, null, 4), JSON.stringify(coreqs2, null, 4), element.parent.children)
		}

		//grab credits
		var text = domutils.getText(element.parent).toLowerCase()
		var creditsParsed = this.parseCredits(text);

		if (creditsParsed) {
			pageData.setParentData('maxCredits', creditsParsed.maxCredits);
			pageData.setParentData('minCredits', creditsParsed.minCredits);
		}
		else {
			console.log('warning, nothing matchied credits', pageData.dbData.url, text);
		}

		// HONORS NOTES
		// swathmore and sju have "honors" in title
		// https://myswat.swarthmore.edu/pls/bwckschd.p_disp_detail_sched?term_in=201602&crn_in=25340
		// https://myswat.swarthmore.edu/pls/bwckctlg.p_disp_course_detail?cat_term_in=201602&subj_code_in=MATH&crse_numb_in=016H
		// https://ssb.sju.edu/pls/PRODSSB/bwckctlg.p_disp_course_detail?cat_term_in=201610&subj_code_in=CHM&crse_numb_in=126

		// clemson has Honors in attributes
		// HOWEVER it also has a "includes honors sections" in the dsecription
		// https://sisssb.clemson.edu/sisbnprd/bwckschd.p_disp_detail_sched?term_in=201608&crn_in=87931

		// gatech has honors in title
		// neu has it in attributes, as different sections in same class
		// https://wl11gp.neu.edu/udcprod8/bwckctlg.p_disp_listcrse?term_in=201710&subj_in=CS&crse_in=1800&schd_in=LEC
		//
		// TLDR: class.honors = sectionHtml.includes('honors')

		// grab honors (honours is canadian spelling)
		if (_(text).includes('honors') || _(text).includes('honours')) {
			pageData.setParentData('honors', true)
		}
		else {
			pageData.setParentData('honors', false)
		}
	}
};

//this allows subclassing, http://bites.goodeggs.com/posts/export-this/ (Mongoose section)
EllucianSectionParser.prototype.EllucianSectionParser = EllucianSectionParser;
module.exports = new EllucianSectionParser();

if (require.main === module) {
	module.exports.tests();
}
<|endoftext|>


<commit_before>module.exports = function(users, experiments, schema) {
    const parse = require('co-body');
    const status = require('http-status');
    const _ = require('lodash');
    const nodemailer = require('nodemailer');

    return {
        updateProjectFavorites,
        updateUserSettings,
        createAccount,
        getUserRegistrationFromUuid
    };

    function* updateProjectFavorites(next) {
        let attrs = yield parse(this);
        this.body = yield users.updateProjectFavorites(this.reqctx.user.id, this.params.project_id, attrs);
        this.status = status.OK;
        yield next;
    }

    function* updateUserSettings(next) {
        let args = yield parse(this);
        let errors = yield validateUserSettingsArgs(args, this.reqctx.user.id);
        if (errors != null) {
            this.status = status.BAD_REQUEST;
            this.body = errors;
        } else {
            let settingArgs = {
                default_project: args.default_project,
                default_experiment: args.default_experiment
            };
            let rv = yield users.updateUserSettings(this.reqctx.user.id, settingArgs);
            if (rv.error) {
                this.status = status.BAD_REQUEST;
                this.body = rv;
            } else {
                this.body = rv.val;
            }
        }
        yield next;
    }

    function* createAccount(next) {
        let accountArgs = yield parse(this);
        schema.prepare(schema.userAccountSchema, accountArgs);
        let errors = yield validateCreateAccount(accountArgs);
        if (errors !== null) {
            this.status = status.BAD_REQUEST;
            this.body = errors;
        } else {
            let rv = yield users.createUnverifiedAccount(accountArgs);
            if (rv.error) {
                this.status = status.BAD_REQUEST;
                this.body = rv;
            } else {
                let evl = emailValidationLink(rv.val);
                if (evl.error) {
                    this.status = status.BAD_REQUEST;
                    this.body = evl;
                } else {
                    this.body = evl.val;
                }
            }
        }
        yield next;
    }

    function* getUserRegistrationFromUuid(next) {
        let result = yield users.getUserRegistrationFromUuid(this.params.validation_id);
        if (result.error) {
            this.status = status.BAD_REQUEST;
            this.body = result;
        } else {
            this.status = status.OK;
            this.body = result.val;
        }
        yield next;
    }

    function* validateUserSettingsArgs(args, userId) {
        if (!args.default_project || !_.isString(args.default_project)) {
            return {error: `Bad argument for default_project`};
        }

        let isValidProject = yield users.userHasProjectAccess(userId, args.default_project);
        if (!isValidProject) {
            return {error: `No such project: ${args.default_project}`};
        }

        if (args.default_experiment && !_.isString(args.default_experiment)) {
            return {error: `Bad argument for default_experiment`};
        }

        if (args.default_experiment) {
            let experimentInProject = yield experiments.experimentExistsInProject(args.default_project, args.default_experiment);
            if (!experimentInProject) {
                return {error: `No such experiment ${args.default_experiment} in project ${args.default_project}`};
            }
        } else {
            args.default_experiment = '';
        }

        return null;
    }

    function* validateCreateAccount(accountArgs) {
        return yield schema.validate(schema.userAccountSchema, accountArgs);
    }

    function emailValidationLink(userData) {
        var mailURL = `smtps://${process.env.MC_VERIFY_EMAIL}:${process.env.MC_VERIFY_PASS}@${process.env.MC_SMTP_HOST}`;
        console.log(mailURL);
        var transporter = nodemailer.createTransport(mailURL);

        var sendTo = userData.id;
        var validationLink = `${process.env.MC_VERIFY_LINK}/${userData.validate_uuid}`;
        let emailMsg =
            `Thank you for registering for an account with Materials Commons. To complete the registration
            process please click on the given link or copy and paste in the url given below.`;
        var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
        var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following link link: ${validationLink}`;

        var mailOptions = {
            from: process.env.MC_VERIFY_EMAIL,
            to: sendTo,
            subject: 'MaterialCommons - account verification',
            text: plainTextBody,
            html: htmlBody
        };

        console.log("emailValidation: " + sendTo);

        // send mail with defined transport object
        transporter.sendMail(mailOptions, function(error, info){
            if(error){
                console.log('Send error: ' + error);
                return {error: error};
            }
            console.log('Message sent: ' + info.response);
        });

        return {val: userData}
    }

};
<commit_msg>Add support for SMTP as the transport for nodemailer (so connect to localhost port 25).<commit_after>module.exports = function(users, experiments, schema) {
    const parse = require('co-body');
    const status = require('http-status');
    const _ = require('lodash');
    const nodemailer = require('nodemailer');
    const smtpTransport = require('nodemailer-smtp-transport');

    return {
        updateProjectFavorites,
        updateUserSettings,
        createAccount,
        getUserRegistrationFromUuid
    };

    function* updateProjectFavorites(next) {
        let attrs = yield parse(this);
        this.body = yield users.updateProjectFavorites(this.reqctx.user.id, this.params.project_id, attrs);
        this.status = status.OK;
        yield next;
    }

    function* updateUserSettings(next) {
        let args = yield parse(this);
        let errors = yield validateUserSettingsArgs(args, this.reqctx.user.id);
        if (errors != null) {
            this.status = status.BAD_REQUEST;
            this.body = errors;
        } else {
            let settingArgs = {
                default_project: args.default_project,
                default_experiment: args.default_experiment
            };
            let rv = yield users.updateUserSettings(this.reqctx.user.id, settingArgs);
            if (rv.error) {
                this.status = status.BAD_REQUEST;
                this.body = rv;
            } else {
                this.body = rv.val;
            }
        }
        yield next;
    }

    function* createAccount(next) {
        let accountArgs = yield parse(this);
        schema.prepare(schema.userAccountSchema, accountArgs);
        let errors = yield validateCreateAccount(accountArgs);
        if (errors !== null) {
            this.status = status.BAD_REQUEST;
            this.body = errors;
        } else {
            let rv = yield users.createUnverifiedAccount(accountArgs);
            if (rv.error) {
                this.status = status.BAD_REQUEST;
                this.body = rv;
            } else {
                let evl = emailValidationLink(rv.val);
                if (evl.error) {
                    this.status = status.BAD_REQUEST;
                    this.body = evl;
                } else {
                    this.body = evl.val;
                }
            }
        }
        yield next;
    }

    function* getUserRegistrationFromUuid(next) {
        let result = yield users.getUserRegistrationFromUuid(this.params.validation_id);
        if (result.error) {
            this.status = status.BAD_REQUEST;
            this.body = result;
        } else {
            this.status = status.OK;
            this.body = result.val;
        }
        yield next;
    }

    function* validateUserSettingsArgs(args, userId) {
        if (!args.default_project || !_.isString(args.default_project)) {
            return {error: `Bad argument for default_project`};
        }

        let isValidProject = yield users.userHasProjectAccess(userId, args.default_project);
        if (!isValidProject) {
            return {error: `No such project: ${args.default_project}`};
        }

        if (args.default_experiment && !_.isString(args.default_experiment)) {
            return {error: `Bad argument for default_experiment`};
        }

        if (args.default_experiment) {
            let experimentInProject = yield experiments.experimentExistsInProject(args.default_project, args.default_experiment);
            if (!experimentInProject) {
                return {error: `No such experiment ${args.default_experiment} in project ${args.default_project}`};
            }
        } else {
            args.default_experiment = '';
        }

        return null;
    }

    function* validateCreateAccount(accountArgs) {
        return yield schema.validate(schema.userAccountSchema, accountArgs);
    }

    function emailValidationLink(userData) {
        var transporter = nodemailer.createTransport(mailTransportConfig());
        var sendTo = userData.id;
        var validationLink = `${process.env.MC_VERIFY_LINK}/${userData.validate_uuid}`;
        let emailMsg =
            `Thank you for registering for an account with Materials Commons. To complete the registration
            process please click on the given link or copy and paste in the url given below.`;
        var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
        var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following link link: ${validationLink}`;

        var mailOptions = {
            from: process.env.MC_VERIFY_EMAIL,
            to: sendTo,
            subject: 'MaterialCommons - account verification',
            text: plainTextBody,
            html: htmlBody
        };

        console.log("emailValidation: " + sendTo);

        // send mail with defined transport object
        transporter.sendMail(mailOptions, function(error, info){
            if(error){
                console.log('Send error: ' + error);
                return {error: error};
            }
            console.log('Message sent: ' + info.response);
        });

        return {val: userData}
    }

    function mailTransportConfig() {
        if (process.env.MC_SMTP_HOST === 'localhost') {
            console.log('Email Server - localhost:25');
            return smtpTransport();
        } else {
            let mailURL = `smtps://${process.env.MC_VERIFY_EMAIL}:${process.env.MC_VERIFY_PASS}@${process.env.MC_SMTP_HOST}`;
            console.log(`Email server - ${mailURL}`);
            return mailURL;
        }
    }

};
<|endoftext|>


<commit_before>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2016 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Class for a button in the flyout.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.provide('Blockly.FlyoutButton');

goog.require('goog.dom');
goog.require('goog.math.Coordinate');

/**
 * Class for a button in the flyout.
 * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this
 *     button.
 * @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace.
 * @param {!Element} xml The XML specifying the label/button.
 * @param {boolean} isLabel Whether this button should be styled as a label.
 * @constructor
 */
Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) {
  // Labels behave the same as buttons, but are styled differently.

  /**
   * @type {!Blockly.WorkspaceSvg}
   * @private
   */
  this.workspace_ = workspace;

  /**
   * @type {!Blockly.Workspace}
   * @private
   */
  this.targetWorkspace_ = targetWorkspace;

  /**
   * @type {string}
   * @private
   */
  this.text_ = xml.getAttribute('text');

  /**
   * @type {!goog.math.Coordinate}
   * @private
   */
  this.position_ = new goog.math.Coordinate(0, 0);

  /**
   * Whether this button should be styled as a label.
   * @type {boolean}
   * @private
   */
  this.isLabel_ = isLabel;

  /**
   * Function to call when this button is clicked.
   * @type {function(!Blockly.FlyoutButton)}
   * @private
   */
  this.callback_ = null;

  var callbackKey = xml.getAttribute('callbackKey');
  if (this.isLabel_ && callbackKey) {
    console.warn('Labels should not have callbacks. Label text: ' + this.text_);
  } else if (!this.isLabel_ &&
      !(callbackKey && targetWorkspace.getButtonCallback(callbackKey))) {
    console.warn('Buttons should have callbacks. Button text: ' + this.text_);
  } else {
    this.callback_ = targetWorkspace.getButtonCallback(callbackKey);
  }

  /**
   * If specified, a CSS class to add to this button.
   * @type {?string}
   * @private
   */
  this.cssClass_ = xml.getAttribute('web-class') || null;
};

/**
 * The margin around the text in the button.
 */
Blockly.FlyoutButton.MARGIN = 40;

/**
 * The width of the button's rect.
 * @type {number}
 */
Blockly.FlyoutButton.prototype.width = 0;

/**
 * The height of the button's rect.
 * @type {number}
 */
Blockly.FlyoutButton.prototype.height = 40; // Can't be computed like the width

/**
 * Opaque data that can be passed to Blockly.unbindEvent_.
 * @type {Array.<!Array>}
 * @private
 */
Blockly.FlyoutButton.prototype.onMouseUpWrapper_ = null;

/**
 * Create the button elements.
 * @return {!Element} The button's SVG group.
 */
Blockly.FlyoutButton.prototype.createDom = function() {
  var cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
  if (this.cssClass_) {
    cssClass += ' ' + this.cssClass_;
  }

  this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': cssClass},
      this.workspace_.getCanvas());

  if (!this.isLabel_) {
    // Shadow rectangle (light source does not mirror in RTL).
    var shadow = Blockly.utils.createSvgElement('rect',
        {'class': 'blocklyFlyoutButtonShadow',
         'rx': 4, 'ry': 4, 'x': 1, 'y': 1},
         this.svgGroup_);
  }
  // Background rectangle.
  var rect = Blockly.utils.createSvgElement('rect',
      {'class': this.isLabel_ ?
        'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground',
        'rx': 4, 'ry': 4},
      this.svgGroup_);

  var svgText = Blockly.utils.createSvgElement('text',
      {'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText',
          'x': 0, 'y': 0, 'text-anchor': 'middle'},
      this.svgGroup_);
  svgText.textContent = this.text_;

  this.width = svgText.getComputedTextLength() +
      2 * Blockly.FlyoutButton.MARGIN;

  if (!this.isLabel_) {
    shadow.setAttribute('width', this.width);
    shadow.setAttribute('height', this.height);
  }

  rect.setAttribute('width', this.width);
  rect.setAttribute('height', this.height);

  // Add underline to flyout labels
  if (this.isLabel_) {
    // var lineWidth = this.workspace_.getMetrics().contentWidth;
    var lineWidth = 300;
    var underline = Blockly.utils.createSvgElement('line',
        {'x1':0, 'y1':this.height, 'x2':lineWidth, 'y2':this.height, 'stroke-width':1, 'stroke':'#ddd'},
        this.svgGroup_);
  }

  svgText.setAttribute('text-anchor', 'middle');
  svgText.setAttribute('alignment-baseline', 'central');
  svgText.setAttribute('x', this.width / 2);
  svgText.setAttribute('y', this.height / 2);

  this.updateTransform_();

  this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup',
      this, this.onMouseUp_);
  return this.svgGroup_;
};

/**
 * Correctly position the flyout button and make it visible.
 */
Blockly.FlyoutButton.prototype.show = function() {
  this.updateTransform_();
  this.svgGroup_.setAttribute('display', 'block');
};

/**
 * Update svg attributes to match internal state.
 * @private
 */
Blockly.FlyoutButton.prototype.updateTransform_ = function() {
  this.svgGroup_.setAttribute('transform',
      'translate(' + this.position_.x + ',' + this.position_.y + ')');
};

/**
 * Move the button to the given x, y coordinates.
 * @param {number} x The new x coordinate.
 * @param {number} y The new y coordinate.
 */
Blockly.FlyoutButton.prototype.moveTo = function(x, y) {
  this.position_.x = x;
  this.position_.y = y;
  this.updateTransform_();
};

/**
 * Get the button's target workspace.
 * @return {!Blockly.WorkspaceSvg} The target workspace of the flyout where this
 *     button resides.
 */
Blockly.FlyoutButton.prototype.getTargetWorkspace = function() {
  return this.targetWorkspace_;
};

/**
 * Dispose of this button.
 */
Blockly.FlyoutButton.prototype.dispose = function() {
  if (this.onMouseUpWrapper_) {
    Blockly.unbindEvent_(this.onMouseUpWrapper_);
  }
  if (this.svgGroup_) {
    goog.dom.removeNode(this.svgGroup_);
    this.svgGroup_ = null;
  }
  this.workspace_ = null;
  this.targetWorkspace_ = null;
};

/**
 * Do something when the button is clicked.
 * @param {!Event} e Mouse up event.
 * @private
 */
Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
  var gesture = this.targetWorkspace_.getGesture(e);
  if (gesture) {
    gesture.cancel();
  }

  // Call the callback registered to this button.
  if (this.callback_) {
    this.callback_(this);
  }
};
<commit_msg>Remove underline<commit_after>/**
 * @license
 * Visual Blocks Editor
 *
 * Copyright 2016 Google Inc.
 * https://developers.google.com/blockly/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Class for a button in the flyout.
 * @author fenichel@google.com (Rachel Fenichel)
 */
'use strict';

goog.provide('Blockly.FlyoutButton');

goog.require('goog.dom');
goog.require('goog.math.Coordinate');

/**
 * Class for a button in the flyout.
 * @param {!Blockly.WorkspaceSvg} workspace The workspace in which to place this
 *     button.
 * @param {!Blockly.WorkspaceSvg} targetWorkspace The flyout's target workspace.
 * @param {!Element} xml The XML specifying the label/button.
 * @param {boolean} isLabel Whether this button should be styled as a label.
 * @constructor
 */
Blockly.FlyoutButton = function(workspace, targetWorkspace, xml, isLabel) {
  // Labels behave the same as buttons, but are styled differently.

  /**
   * @type {!Blockly.WorkspaceSvg}
   * @private
   */
  this.workspace_ = workspace;

  /**
   * @type {!Blockly.Workspace}
   * @private
   */
  this.targetWorkspace_ = targetWorkspace;

  /**
   * @type {string}
   * @private
   */
  this.text_ = xml.getAttribute('text');

  /**
   * @type {!goog.math.Coordinate}
   * @private
   */
  this.position_ = new goog.math.Coordinate(0, 0);

  /**
   * Whether this button should be styled as a label.
   * @type {boolean}
   * @private
   */
  this.isLabel_ = isLabel;

  /**
   * Function to call when this button is clicked.
   * @type {function(!Blockly.FlyoutButton)}
   * @private
   */
  this.callback_ = null;

  var callbackKey = xml.getAttribute('callbackKey');
  if (this.isLabel_ && callbackKey) {
    console.warn('Labels should not have callbacks. Label text: ' + this.text_);
  } else if (!this.isLabel_ &&
      !(callbackKey && targetWorkspace.getButtonCallback(callbackKey))) {
    console.warn('Buttons should have callbacks. Button text: ' + this.text_);
  } else {
    this.callback_ = targetWorkspace.getButtonCallback(callbackKey);
  }

  /**
   * If specified, a CSS class to add to this button.
   * @type {?string}
   * @private
   */
  this.cssClass_ = xml.getAttribute('web-class') || null;
};

/**
 * The margin around the text in the button.
 */
Blockly.FlyoutButton.MARGIN = 40;

/**
 * The width of the button's rect.
 * @type {number}
 */
Blockly.FlyoutButton.prototype.width = 0;

/**
 * The height of the button's rect.
 * @type {number}
 */
Blockly.FlyoutButton.prototype.height = 40; // Can't be computed like the width

/**
 * Opaque data that can be passed to Blockly.unbindEvent_.
 * @type {Array.<!Array>}
 * @private
 */
Blockly.FlyoutButton.prototype.onMouseUpWrapper_ = null;

/**
 * Create the button elements.
 * @return {!Element} The button's SVG group.
 */
Blockly.FlyoutButton.prototype.createDom = function() {
  var cssClass = this.isLabel_ ? 'blocklyFlyoutLabel' : 'blocklyFlyoutButton';
  if (this.cssClass_) {
    cssClass += ' ' + this.cssClass_;
  }

  this.svgGroup_ = Blockly.utils.createSvgElement('g', {'class': cssClass},
      this.workspace_.getCanvas());

  if (!this.isLabel_) {
    // Shadow rectangle (light source does not mirror in RTL).
    var shadow = Blockly.utils.createSvgElement('rect',
        {'class': 'blocklyFlyoutButtonShadow',
         'rx': 4, 'ry': 4, 'x': 1, 'y': 1},
         this.svgGroup_);
  }
  // Background rectangle.
  var rect = Blockly.utils.createSvgElement('rect',
      {'class': this.isLabel_ ?
        'blocklyFlyoutLabelBackground' : 'blocklyFlyoutButtonBackground',
        'rx': 4, 'ry': 4},
      this.svgGroup_);

  var svgText = Blockly.utils.createSvgElement('text',
      {'class': this.isLabel_ ? 'blocklyFlyoutLabelText' : 'blocklyText',
          'x': 0, 'y': 0, 'text-anchor': 'middle'},
      this.svgGroup_);
  svgText.textContent = this.text_;

  this.width = svgText.getComputedTextLength() +
      2 * Blockly.FlyoutButton.MARGIN;

  if (!this.isLabel_) {
    shadow.setAttribute('width', this.width);
    shadow.setAttribute('height', this.height);
  }

  rect.setAttribute('width', this.width);
  rect.setAttribute('height', this.height);

  svgText.setAttribute('text-anchor', 'middle');
  svgText.setAttribute('alignment-baseline', 'central');
  svgText.setAttribute('x', this.width / 2);
  svgText.setAttribute('y', this.height / 2);

  this.updateTransform_();

  this.mouseUpWrapper_ = Blockly.bindEventWithChecks_(this.svgGroup_, 'mouseup',
      this, this.onMouseUp_);
  return this.svgGroup_;
};

/**
 * Correctly position the flyout button and make it visible.
 */
Blockly.FlyoutButton.prototype.show = function() {
  this.updateTransform_();
  this.svgGroup_.setAttribute('display', 'block');
};

/**
 * Update svg attributes to match internal state.
 * @private
 */
Blockly.FlyoutButton.prototype.updateTransform_ = function() {
  this.svgGroup_.setAttribute('transform',
      'translate(' + this.position_.x + ',' + this.position_.y + ')');
};

/**
 * Move the button to the given x, y coordinates.
 * @param {number} x The new x coordinate.
 * @param {number} y The new y coordinate.
 */
Blockly.FlyoutButton.prototype.moveTo = function(x, y) {
  this.position_.x = x;
  this.position_.y = y;
  this.updateTransform_();
};

/**
 * Get the button's target workspace.
 * @return {!Blockly.WorkspaceSvg} The target workspace of the flyout where this
 *     button resides.
 */
Blockly.FlyoutButton.prototype.getTargetWorkspace = function() {
  return this.targetWorkspace_;
};

/**
 * Dispose of this button.
 */
Blockly.FlyoutButton.prototype.dispose = function() {
  if (this.onMouseUpWrapper_) {
    Blockly.unbindEvent_(this.onMouseUpWrapper_);
  }
  if (this.svgGroup_) {
    goog.dom.removeNode(this.svgGroup_);
    this.svgGroup_ = null;
  }
  this.workspace_ = null;
  this.targetWorkspace_ = null;
};

/**
 * Do something when the button is clicked.
 * @param {!Event} e Mouse up event.
 * @private
 */
Blockly.FlyoutButton.prototype.onMouseUp_ = function(e) {
  var gesture = this.targetWorkspace_.getGesture(e);
  if (gesture) {
    gesture.cancel();
  }

  // Call the callback registered to this button.
  if (this.callback_) {
    this.callback_(this);
  }
};
<|endoftext|>


<commit_before>/**
 * @class Oskari.framework.bundle.admin-layerrights.Flyout
 *
 * Renders the layer rights management flyout.
 */
Oskari.clazz.define('Oskari.framework.bundle.admin-layerrights.Flyout',

/**
 * @method create called automatically on construction
 * @static
 * @param {Oskari.framework.bundle.admin-layerrights.AdminLayerRightsBundleInstance}
 *        instance reference to component that created the tile
 */
function(instance) {
    this.instance = instance;
    this.container = null;
    this.state = null;
    this.template = null;
}, {
    /**
     * @method getName
     * @return {String} the name for the component
     */
    getName : function() {
        return 'Oskari.framework.bundle.admin-layerrights.Flyout';
    },

    /**
     * @method setEl
     * @param {Object} el
     *      reference to the container in browser
     * @param {Number} width
     *      container size(?) - not used
     * @param {Number} height
     *      container size(?) - not used
     *
     * Interface method implementation
     */
    setEl : function(el, width, height) {
        this.container = el[0];
        if (!jQuery(this.container).hasClass('admin-layerrights')) {
            jQuery(this.container).addClass('admin-layerrights');
        }
    },

    /**
     * @method startPlugin
     *
     * Interface method implementation, assigns the HTML templates
     * that will be used to create the UI
     */
    startPlugin : function() {
        this.template = jQuery(
            '
\n' +
            '   <form method="post" id="admin-layerrights-form">' +
            '       ' +
            '          <select class="admin-layerrights-role"></select>\n' +
            '       ' + /*
            '       Theme' +
            '       <select id="admin-layerrights-theme"></select>\n' +
            '       Data provider' +
            '       <select id="admin-layerrights-dataprovider"></select>\n' +*/
            '
' +
            '
' +
            '

' +
            '   </form>' +
            '
\n');
    },

    /**
     * @method stopPlugin
     *
     * Interface method implementation, does nothing atm
     */
    stopPlugin : function() {
    },

    /**
     * @method getTitle
     * @return {String} localized text for the title of the flyout
     */
    getTitle : function() {
        return this.instance.getLocalization('title');
    },

    /**
     * @method getDescription
     * @return {String} localized text for the description of the
     * flyout
     */
    getDescription : function() {
        return this.instance.getLocalization('desc');
    },

    /**
     * @method getOptions
     * Interface method implementation, does nothing atm
     */
    getOptions : function() {

    },

    /**
     * @method setState
     * @param {Object} state
     */
    setState : function(state) {
        this.state = state;
    },

    /**
     * @method getState
     * @return {Object} state
     */
    getState : function() {
        if(!this.state) {
            return {};
        }
        return this.state;
    },

    doSave : function() {
        // save data
        // get stuff from slickgrid, pass it as json to actionroute
        alert("Savety save.");
    },

    /**
     * @method setContent
     * Creates the UI for a fresh start
     */
    setContent : function(content) {
        // TODO add filters (provider/theme etc.)
        var me = this;
        var sandbox = me.instance.getSandbox();

        var flyout = jQuery(this.container);
        flyout.empty();
        var container = this.template.clone();

        var button = Oskari.clazz.create('Oskari.userinterface.component.Button');
        button.setTitle(me.instance.getLocalization('save'));

        button.setHandler(me.doSave);
        // Not sure if we want savew on enter
        //field.bindEnterKey(doSave);

        var controls = container.find('div.controls');
        controls.append(button.getButton());

        var roleSelectLabel = container.find('label > span');
        roleSelectLabel.html(this.instance.getLocalization('selectRole'));
        container.append(content);
        flyout.append(container);
        var roleSelect = flyout.find('select.admin-layerrights-role');
        roleSelect.change(function(eventObject) {
            console.log("change");
            me.updatePermissionsTable(roleSelect.find("option:selected").val(), "ROLE");
        });
        // We're only supporting ROLE ATM, USER support might be added later
        me.getExternalIdsAjaxRequest("ROLE", 0);

    },

    updatePermissionsTable : function(activeRole, externalType) {
        alert("Update permissions table with role: " + activeRole);
        jQuery.getJSON(ajaxUrl, {
            cmd: "ajax.jsp",
            lang: Oskari.getLang(),
            timestamp: new Date().getTime(),
            externalId: activeRole,
            resourceType: "WMS_LAYER", // default karttataso, hardcoded for now (TODO move to backend)
            externalType: externalType
        }, function(result) {
            console.log(result);
        });
    },

    getExternalIdsAjaxRequest : function(externalType, selectedId) {
        var me = this;

        //ajaxRequestGoing = true;
        // TODO add error handling
        jQuery.getJSON(ajaxUrl, {
            action_route: "GetAllRoles",
            getExternalIds: externalType
        }, function (result) {
            me.makeExternalIdsSelect(result, externalType, selectedId);
        });
    },

    // result, (c)0/user/role, (b)selected id
    makeExternalIdsSelect : function(result, externalType, selectedId) {
        var externalIdSelect = jQuery(this.container).find("select.admin-layerrights-role");
        externalIdSelect.html("");
        if (externalType != "0") {
            var a;
            if (selectedId != "0") {
                a = '<option value="0" >-- Valitse tunniste --</option>';
            } else {
                a = '<option value="0" selected="selected">-- Valitse tunniste --</option>';
            }
            for (var d = 0; d < result.external.length; d++) {
                if (result.external[d].id == selectedId) {
                    a += '<option selected="selected" value="' + result.external[d].id + '">' + result.external[d].name + "</option>";
                } else {
                    a += '<option value="' + result.external[d].id + '">' + result.external[d].name + "</option>";
                }
            }
            externalIdSelect.html(a);
        } else {
            externalIdSelect.html("");
        }
    }

}, {
    /**
     * @property {String[]} protocol
     * @static
     */
    'protocol' : ['Oskari.userinterface.Flyout']
});
<commit_msg>[AL-1329] Cleaned up flyout code with JSLint<commit_after>/**
 * @class Oskari.framework.bundle.admin-layerrights.Flyout
 *
 * Renders the layer rights management flyout.
 */
Oskari.clazz.define('Oskari.framework.bundle.admin-layerrights.Flyout',

    /**
     * @method create called automatically on construction
     * @static
     * @param {Oskari.framework.bundle.admin-layerrights.AdminLayerRightsBundleInstance}
     *        instance reference to component that created the tile
     */
    function (instance) {
        "use strict";
        var me = this;
        me.instance = instance;
        me.container = null;
        me.state = null;
        me.template = null;
    }, {
        /**
         * @method getName
         * @return {String} the name for the component
         */
        getName : function () {
            "use strict";
            return 'Oskari.framework.bundle.admin-layerrights.Flyout';
        },

        /**
         * @method setEl
         * @param {Object} el
         *      reference to the container in browser
         * @param {Number} width
         *      container size(?) - not used
         * @param {Number} height
         *      container size(?) - not used
         *
         * Interface method implementation
         */
        setEl : function (el, width, height) {
            "use strict";
            this.container = el[0];
            if (!jQuery(this.container).hasClass('admin-layerrights')) {
                jQuery(this.container).addClass('admin-layerrights');
            }
        },

        /**
         * @method startPlugin
         *
         * Interface method implementation, assigns the HTML templates
         * that will be used to create the UI
         */
        startPlugin : function () {
            "use strict";
            this.template = jQuery(
                '
\n' +
                    '   <form method="post" id="admin-layerrights-form">' +
                    '       ' +
                    '          <select class="admin-layerrights-role"></select>\n' +
                    '       ' + /*
                    '       Theme' +
                    '       <select id="admin-layerrights-theme"></select>\n' +
                    '       Data provider' +
                    '       <select id="admin-layerrights-dataprovider"></select>\n' +*/
                    '
' +
                    '
' +
                    '

' +
                    '   </form>' +
                    '
\n'
            );
        },

        /**
         * @method stopPlugin
         *
         * Interface method implementation, does nothing atm
         */
        stopPlugin : function () {
            "use strict";
        },

        /**
         * @method getTitle
         * @return {String} localized text for the title of the flyout
         */
        getTitle : function () {
            "use strict";
            return this.instance.getLocalization('title');
        },

        /**
         * @method getDescription
         * @return {String} localized text for the description of the
         * flyout
         */
        getDescription : function () {
            "use strict";
            return this.instance.getLocalization('desc');
        },

        /**
         * @method getOptions
         * Interface method implementation, does nothing atm
         */
        getOptions : function () {
            "use strict";
        },

        /**
         * @method setState
         * @param {Object} state
         */
        setState : function (state) {
            "use strict";
            this.state = state;
        },

        /**
         * @method getState
         * @return {Object} state
         */
        getState : function () {
            "use strict";
            if (!this.state) {
                return {};
            }
            return this.state;
        },

        doSave : function () {
            "use strict";
            // save data
            // get stuff from slickgrid, pass it as json to actionroute
            alert("Savety save.");
        },

        /**
         * @method setContent
         * Creates the UI for a fresh start
         */
        setContent : function (content) {
            "use strict";
            // TODO add filters (provider/theme etc.)
            var me = this,
                //sandbox = me.instance.getSandbox(),
                flyout = jQuery(this.container),
                container = this.template.clone(),
                button = Oskari.clazz.create('Oskari.userinterface.component.Button'),
                controls = container.find('div.controls'),
                roleSelectLabel = container.find('label > span'),
                roleSelect = container.find('select.admin-layerrights-role');

            flyout.empty();
            button.setTitle(me.instance.getLocalization('save'));

            button.setHandler(me.doSave);
            // Not sure if we want save on enter
            //field.bindEnterKey(doSave);

            controls.append(button.getButton());

            roleSelectLabel.html(this.instance.getLocalization('selectRole'));
            container.append(content);

            roleSelect.change(function () {
                me.updatePermissionsTable(roleSelect.find("option:selected").val(), "ROLE");
            });
            flyout.append(container);
            // We're only supporting ROLE ATM, USER support might be added later
            me.getExternalIdsAjaxRequest("ROLE", 0);

        },

        updatePermissionsTable : function (activeRole, externalType) {
            "use strict";
            alert("Update permissions table with role: " + activeRole);
            jQuery.getJSON(ajaxUrl, {
                cmd: "ajax.jsp",
                lang: Oskari.getLang(),
                timestamp: new Date().getTime(),
                externalId: activeRole,
                resourceType: "WMS_LAYER", // default karttataso, hardcoded for now (TODO move to backend)
                externalType: externalType
            }, function (result) {
                console.log(result);
            });
        },

        getExternalIdsAjaxRequest : function (externalType, selectedId) {
            "use strict";
            var me = this;

            //ajaxRequestGoing = true;
            // TODO add error handling
            jQuery.getJSON(ajaxUrl, {
                action_route: "GetAllRoles",
                getExternalIds: externalType
            }, function (result) {
                me.makeExternalIdsSelect(result, externalType, selectedId);
            });
        },

        // result, (c)0/user/role (STRING!), (b)selected id
        /**
         * @param (Object) result
         * @param {String} externalType
         * @param {String} selectedId
         */
        makeExternalIdsSelect : function (result, externalType, selectedId) {
            "use strict";
            var externalIdSelect = jQuery(this.container).find("select.admin-layerrights-role"),
                a,
                d;
            externalIdSelect.html("");
            if (externalType !== "0") {
                if (selectedId !== "0") {
                    a = '<option value="0" >-- Valitse tunniste --</option>';
                } else {
                    a = '<option value="0" selected="selected">-- Valitse tunniste --</option>';
                }
                for (d = 0; d < result.external.length; d += 1) {
                    if (result.external[d].id === selectedId) {
                        a += '<option selected="selected" value="' + result.external[d].id + '">' + result.external[d].name + "</option>";
                    } else {
                        a += '<option value="' + result.external[d].id + '">' + result.external[d].name + "</option>";
                    }
                }
                externalIdSelect.html(a);
            } else {
                externalIdSelect.html("");
            }
        }

    }, {
        /**
         * @property {String[]} protocol
         * @static
         */
        'protocol' : ['Oskari.userinterface.Flyout']
    });
<|endoftext|>


<commit_before>const {homedir} = require('os');
const {statSync, renameSync, readFileSync, writeFileSync} = require('fs');
const {resolve} = require('path');
const vm = require('vm');

const {dialog} = require('electron');
const isDev = require('electron-is-dev');
const gaze = require('gaze');
const Config = require('electron-config');
const notify = require('./notify');

// local storage
const winCfg = new Config({
  defaults: {
    windowPosition: [50, 50],
    windowSize: [540, 380]
  }
});

let configDir = homedir();
if (isDev) {
  // if a local config file exists, use it
  try {
    const devDir = resolve(__dirname, '..');
    const devConfig = resolve(devDir, '.hyper.js');
    statSync(devConfig);
    configDir = devDir;
    console.log('using config file:', devConfig);
  } catch (err) {
    // ignore
  }
}

const path = resolve(configDir, '.hyper.js');
const pathLegacy = resolve(configDir, '.hyperterm.js');

const watchers = [];

let cfg = {};

function watch() {
  gaze(path, function (err) {
    if (err) {
      throw err;
    }
    this.on('changed', () => {
      try {
        if (exec(readFileSync(path, 'utf8'))) {
          notify('Hyper configuration reloaded!');
          watchers.forEach(fn => fn());
        }
      } catch (err) {
        dialog.showMessageBox({
          message: `An error occurred loading your configuration (${path}): ${err.message}`,
          buttons: ['Ok']
        });
      }
    });
    this.on('error', () => {
      // Ignore file watching errors
    });
  });
}

let _str; // last script
function exec(str) {
  if (str === _str) {
    return false;
  }
  _str = str;
  const script = new vm.Script(str);
  const module = {};
  script.runInNewContext({module});
  if (!module.exports) {
    throw new Error('Error reading configuration: `module.exports` not set');
  }
  const _cfg = module.exports;
  if (!_cfg.config) {
    throw new Error('Error reading configuration: `config` key is missing');
  }
  _cfg.plugins = _cfg.plugins || [];
  _cfg.localPlugins = _cfg.localPlugins || [];
  cfg = _cfg;
  return true;
}

exports.subscribe = function (fn) {
  watchers.push(fn);
  return () => {
    watchers.splice(watchers.indexOf(fn), 1);
  };
};

exports.init = function () {
  // for backwards compatibility with hyperterm
  // (prior to the rename), we try to rename
  // on behalf of the user
  try {
    statSync(pathLegacy);
    renameSync(pathLegacy, path);
  } catch (err) {
    // ignore
  }

  try {
    exec(readFileSync(path, 'utf8'));
  } catch (err) {
    console.log('read error', path, err.message);
    const defaultConfig = readFileSync(resolve(__dirname, 'config-default.js'));
    try {
      console.log('attempting to write default config to', path);
      exec(defaultConfig);
      writeFileSync(path, defaultConfig);
    } catch (err) {
      throw new Error(`Failed to write config to ${path}`);
    }
  }
  watch();
};

exports.getConfigDir = function () {
  // expose config directory to load plugin from the right place
  return configDir;
};

exports.getConfig = function () {
  return cfg.config;
};

exports.getPlugins = function () {
  return {
    plugins: cfg.plugins,
    localPlugins: cfg.localPlugins
  };
};

exports.window = {
  get() {
    const position = winCfg.get('windowPosition');
    const size = winCfg.get('windowSize');
    return {position, size};
  },
  recordState(win) {
    winCfg.set('windowPosition', win.getPosition());
    winCfg.set('windowSize', win.getSize());
  }
};
<commit_msg>On Win32, write out default .hyper.js as CRLF so Notepad doesn't have a fit (#1383)<commit_after>const {homedir} = require('os');
const {statSync, renameSync, readFileSync, writeFileSync} = require('fs');
const {resolve} = require('path');
const vm = require('vm');

const {dialog} = require('electron');
const isDev = require('electron-is-dev');
const gaze = require('gaze');
const Config = require('electron-config');
const notify = require('./notify');

// local storage
const winCfg = new Config({
  defaults: {
    windowPosition: [50, 50],
    windowSize: [540, 380]
  }
});

let configDir = homedir();
if (isDev) {
  // if a local config file exists, use it
  try {
    const devDir = resolve(__dirname, '..');
    const devConfig = resolve(devDir, '.hyper.js');
    statSync(devConfig);
    configDir = devDir;
    console.log('using config file:', devConfig);
  } catch (err) {
    // ignore
  }
}

const path = resolve(configDir, '.hyper.js');
const pathLegacy = resolve(configDir, '.hyperterm.js');

const watchers = [];

let cfg = {};

function watch() {
  gaze(path, function (err) {
    if (err) {
      throw err;
    }
    this.on('changed', () => {
      try {
        if (exec(readFileSync(path, 'utf8'))) {
          notify('Hyper configuration reloaded!');
          watchers.forEach(fn => fn());
        }
      } catch (err) {
        dialog.showMessageBox({
          message: `An error occurred loading your configuration (${path}): ${err.message}`,
          buttons: ['Ok']
        });
      }
    });
    this.on('error', () => {
      // Ignore file watching errors
    });
  });
}

let _str; // last script
function exec(str) {
  if (str === _str) {
    return false;
  }
  _str = str;
  const script = new vm.Script(str);
  const module = {};
  script.runInNewContext({module});
  if (!module.exports) {
    throw new Error('Error reading configuration: `module.exports` not set');
  }
  const _cfg = module.exports;
  if (!_cfg.config) {
    throw new Error('Error reading configuration: `config` key is missing');
  }
  _cfg.plugins = _cfg.plugins || [];
  _cfg.localPlugins = _cfg.localPlugins || [];
  cfg = _cfg;
  return true;
}

// This method will take text formatted as Unix line endings and transform it
// to text formatted with DOS line endings. We do this because the default
// text editor on Windows (notepad) doesn't Deal with LF files. Still. In 2017.
function crlfify(str) {
  return str.split('\n').map(x => x.indexOf('\r') < 0 ? x : `${x}\r`).join('\n');
}

exports.subscribe = function (fn) {
  watchers.push(fn);
  return () => {
    watchers.splice(watchers.indexOf(fn), 1);
  };
};

exports.init = function () {
  // for backwards compatibility with hyperterm
  // (prior to the rename), we try to rename
  // on behalf of the user
  try {
    statSync(pathLegacy);
    renameSync(pathLegacy, path);
  } catch (err) {
    // ignore
  }

  try {
    exec(readFileSync(path, 'utf8'));
  } catch (err) {
    console.log('read error', path, err.message);
    const defaultConfig = readFileSync(resolve(__dirname, 'config-default.js'));
    try {
      console.log('attempting to write default config to', path);
      exec(defaultConfig);

      writeFileSync(
        path,
        process.platform === 'win32' ? crlfify(defaultConfig.toString()) : defaultConfig);
    } catch (err) {
      throw new Error(`Failed to write config to ${path}: ${err.message}`);
    }
  }
  watch();
};

exports.getConfigDir = function () {
  // expose config directory to load plugin from the right place
  return configDir;
};

exports.getConfig = function () {
  return cfg.config;
};

exports.getPlugins = function () {
  return {
    plugins: cfg.plugins,
    localPlugins: cfg.localPlugins
  };
};

exports.window = {
  get() {
    const position = winCfg.get('windowPosition');
    const size = winCfg.get('windowSize');
    return {position, size};
  },
  recordState(win) {
    winCfg.set('windowPosition', win.getPosition());
    winCfg.set('windowSize', win.getSize());
  }
};
<|endoftext|>


<commit_before>module.exports = function($mdThemingProvider) {

  'ngInject';

  var contaAzulBlueMap = $mdThemingProvider.extendPalette('blue', {
    '500': '#2585e9',
    // 'contrastDefaultColor': 'dark'
  });

  // Register the new color palette map with the name neonRed
  $mdThemingProvider.definePalette('contaAzul', contaAzulBlueMap);

  $mdThemingProvider
    .theme('default')
    .primaryPalette('contaAzul')
    .accentPalette('amber');

  $mdThemingProvider.alwaysWatchTheme(true);

};
<commit_msg>code review problems<commit_after>module.exports = function($mdThemingProvider) {

  'ngInject';

  var contaAzulBlueMap = $mdThemingProvider.extendPalette('blue', {
    '500': '#2585e9',
  });

  $mdThemingProvider.definePalette('contaAzul', contaAzulBlueMap);

  $mdThemingProvider
    .theme('default')
    .primaryPalette('contaAzul')
    .accentPalette('amber');

  $mdThemingProvider.alwaysWatchTheme(true);

};
<|endoftext|>


<commit_before>/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    config.js

    - Configuration for Civic Seed using nconf, depending on environment
    - This script looks at the NODE_ENV environment variable to know
      what environment CivicSeed is running in.

      - 'development'     local development instance (default)
      - 'production'      production environment, generally Amazon S3
      - 'heroku'          remote testing environment, specifically Heroku

    - Configuration variables for each environment is stored in the
      associated `config/environment.json` file
    - Authentication credentials should NEVER be stored in these files
      because they could be made available through a public repository,
      like GitHub. Instead, they should ALWAYS be stored on the environment
      itself using environment variables.
    - See also README.md and documentation for more information.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var rootDir        = process.cwd(),
    nconf          = require('nconf'),
    env            = require('node-env-file'),
    fs             = require('fs')

// Read environment variables from an optional .env, if present
var envFile = rootDir + '/.env'
if (fs.existsSync(envFile)) {
  env(envFile, {verbose: true, overwrite: true})
}

var NODE_ENV       = process.env.NODE_ENV || 'development',
    CONFIG_FILE    = process.env.CONFIG_FILE || rootDir + '/config/' + NODE_ENV + '.json'

var json           = JSON.parse(fs.readFileSync(rootDir + '/package.json', 'utf8'))

// Hierarchical configuration with nconf
nconf.argv().env().file({
  file: CONFIG_FILE
});

nconf.set('VERSION', json.version);
nconf.set('NODE_ENV', NODE_ENV);

// Get authentication credentials stored as environment variables.
nconf.set('EMAIL_USER', process.env.EMAIL_USER || '')
nconf.set('EMAIL_PW',   process.env.EMAIL_PW   || '')
nconf.set('EMAIL_TO',   process.env.EMAIL_TO   || '')
nconf.set('REDIS_PW',   process.env.REDIS_PW   || '')

// HEROKU ENVIRONMENT
// Environment variables for Redis and Mongo are pre-set by Heroku add-ons.
// These use slightly different names we parse them here and add them via nconf.
// Heroku still relies on the existence of the config_heroku.json file but unused settings are removed in it.
if (NODE_ENV === 'heroku') {
  console.log('   * * * * * * * * * * * *   Heroku Dev Environment   * * * * * * * * * * * *   ')
  nconf.set('MONGO_URL', process.env.MONGOHQ_URL)

  // Set up RedisToGo on Heroku environment
  // See: https://devcenter.heroku.com/articles/redistogo#using-with-node
  var rtg   = require('url').parse(process.env.REDISTOGO_URL)
  nconf.set('REDIS_HOST', rtg.hostname)
  nconf.set('REDIS_PORT', rtg.port)
  nconf.set('REDIS_PW', rtg.auth.split(':')[1])
}

module.exports = nconf;
<commit_msg>Add winston logger for config.js<commit_after>/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

    config.js

    - Configuration for Civic Seed using nconf, depending on environment
    - This script looks at the NODE_ENV environment variable to know
      what environment CivicSeed is running in.

      - 'development'     local development instance (default)
      - 'production'      production environment, generally Amazon S3
      - 'heroku'          remote testing environment, specifically Heroku

    - Configuration variables for each environment is stored in the
      associated `config/environment.json` file
    - Authentication credentials should NEVER be stored in these files
      because they could be made available through a public repository,
      like GitHub. Instead, they should ALWAYS be stored on the environment
      itself using environment variables.
    - See also README.md and documentation for more information.

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var rootDir        = process.cwd(),
    nconf          = require('nconf'),
    env            = require('node-env-file'),
    fs             = require('fs'),
    winston        = require('winston')

// Read environment variables from an optional .env, if present
var envFile = rootDir + '/.env'
if (fs.existsSync(envFile)) {
  env(envFile, {verbose: true, overwrite: true})
}

var NODE_ENV       = process.env.NODE_ENV || 'development',
    CONFIG_FILE    = process.env.CONFIG_FILE || rootDir + '/config/' + NODE_ENV + '.json'

var json           = JSON.parse(fs.readFileSync(rootDir + '/package.json', 'utf8'))

// Hierarchical configuration with nconf
nconf.argv().env().file({
  file: CONFIG_FILE
})

nconf.set('VERSION', json.version)
nconf.set('NODE_ENV', NODE_ENV)

// Get authentication credentials stored as environment variables.
nconf.set('EMAIL_USER', process.env.EMAIL_USER || '')
nconf.set('EMAIL_PW',   process.env.EMAIL_PW   || '')
nconf.set('EMAIL_TO',   process.env.EMAIL_TO   || '')
nconf.set('REDIS_PW',   process.env.REDIS_PW   || '')

// HEROKU ENVIRONMENT
// Environment variables for Redis and Mongo are pre-set by Heroku add-ons.
// These use slightly different names we parse them here and add them via nconf.
// Heroku still relies on the existence of the config_heroku.json file but unused settings are removed in it.
if (NODE_ENV === 'heroku') {
  winston.info('Configuring Heroku environment ...'.yellow)
  nconf.set('MONGO_URL', process.env.MONGOHQ_URL)

  // Set up RedisToGo on Heroku environment
  // See: https://devcenter.heroku.com/articles/redistogo#using-with-node
  var rtg   = require('url').parse(process.env.REDISTOGO_URL)
  nconf.set('REDIS_HOST', rtg.hostname)
  nconf.set('REDIS_PORT', rtg.port)
  nconf.set('REDIS_PW', rtg.auth.split(':')[1])
}

module.exports = nconf
<|endoftext|>