<commit_before>import Component from '@ember/component';
import Editor from 'mobiledoc-kit/editor/editor';
import cleanBasicHtml from '@tryghost/kg-clean-basic-html';
import parserPlugins from '../options/basic-html-parser-plugins';
import registerKeyCommands, {BASIC_KEY_COMMANDS} from '../options/key-commands';
import validator from 'validator';
import {BLANK_DOC, MOBILEDOC_VERSION} from './koenig-editor';
import {DRAG_DISABLED_DATA_ATTR} from '../lib/dnd/constants';
import {arrayToMap, toggleSpecialFormatEditState} from './koenig-editor';
import {assign} from '@ember/polyfills';
import {computed} from '@ember/object';
import {getContentFromPasteEvent} from 'mobiledoc-kit/utils/parse-utils';
import {getLinkMarkupFromRange} from '../utils/markup-utils';
import {registerBasicTextExpansions} from '../options/text-expansions';
import {run} from '@ember/runloop';

const UNDO_DEPTH = 50;

// TODO: extract core to share functionality between this and `{{koenig-editor}}`

export default Component.extend({
    // public attrs
    autofocus: false,
    html: null,
    placeholder: '',
    spellcheck: true,

    // internal properties
    activeMarkupTagNames: null,
    editor: null,
    linkRange: null,
    mobiledoc: null,
    selectedRange: null,

    // private properties
    _hasFocus: false,
    _lastMobiledoc: null,
    _startedRunLoop: false,

    // closure actions
    willCreateEditor() {},
    didCreateEditor() {},
    onChange() {},
    onNewline() {},
    onFocus() {},
    onBlur() {},

    /* computed properties -------------------------------------------------- */

    cleanHTML: computed('html', function () {
        return cleanBasicHtml(this.html);
    }),

    // merge in named options with any passed in `options` property data-bag
    editorOptions: computed('cleanHTML', function () {
        let options = this.options || {};
        let atoms = this.atoms || [];
        let cards = this.cards || [];

        return assign({
            html: `<p>${this.cleanHTML || ''}</p>`,
            placeholder: this.placeholder,
            spellcheck: this.spellcheck,
            autofocus: this.autofocus,
            cards,
            atoms,
            unknownCardHandler() {},
            unknownAtomHandler() {}
        }, options);
    }),

    /* lifecycle hooks ------------------------------------------------------ */

    init() {
        this._super(...arguments);
        this.SPECIAL_MARKUPS = [];
    },

    didReceiveAttrs() {
        this._super(...arguments);

        // reset local mobiledoc if html has been changed upstream so that
        // the html will be re-parsed by the mobiledoc-kit editor
        if (this.cleanHTML !== this._getHTML()) {
            this.set('mobiledoc', null);
        }
    },

    willRender() {
        let mobiledoc = this.mobiledoc;

        if (!mobiledoc && !this.cleanHTML) {
            mobiledoc = BLANK_DOC;
        }

        let mobiledocIsSame =
            (this._lastMobiledoc && this._lastMobiledoc === mobiledoc);
        let isEditingDisabledIsSame =
            this._lastIsEditingDisabled === this.isEditingDisabled;

        // no change to mobiledoc, no need to recreate the editor
        if (mobiledocIsSame && isEditingDisabledIsSame) {
            return;
        }

        // update our internal references
        this._lastIsEditingDisabled = this.isEditingDisabled;

        // trigger the willCreateEditor closure action
        this.willCreateEditor();

        // teardown any old editor that might be around
        let editor = this.editor;
        if (editor) {
            editor.destroy();
        }

        // create a new editor
        let editorOptions = this.editorOptions;
        editorOptions.mobiledoc = mobiledoc;
        editorOptions.showLinkTooltips = false;
        editorOptions.undoDepth = UNDO_DEPTH;
        editorOptions.parserPlugins = parserPlugins;

        editor = new Editor(editorOptions);

        registerKeyCommands(editor, this, BASIC_KEY_COMMANDS);
        registerBasicTextExpansions(editor);

        // set up editor hooks
        editor.willRender(() => {
            // The editor's render/rerender will happen after this `editor.willRender`,
            // so we explicitly start a runloop here if there is none, so that the
            // add/remove card hooks happen inside a runloop.
            // When pasting text that gets turned into a card, for example,
            // the add card hook would run outside the runloop if we didn't begin a new
            // one now.
            if (!run.currentRunLoop) {
                this._startedRunLoop = true;
                run.begin();
            }
        });

        editor.didRender(() => {
            // if we had explicitly started a runloop in `editor.willRender`,
            // we must explicitly end it here
            if (this._startedRunLoop) {
                this._startedRunLoop = false;
                run.end();
            }
        });

        editor.willHandleNewline((event) => {
            run.join(() => {
                this.willHandleNewline(event);
            });
        });

        editor.didUpdatePost((postEditor) => {
            run.join(() => {
                this.didUpdatePost(postEditor);
            });
        });

        editor.postDidChange(() => {
            run.join(() => {
                this.postDidChange(editor);
            });
        });

        editor.cursorDidChange(() => {
            run.join(() => {
                this.cursorDidChange(editor);
            });
        });

        editor.inputModeDidChange(() => {
            if (this.isDestroyed) {
                return;
            }
            run.join(() => {
                this.inputModeDidChange(editor);
            });
        });

        if (this.isEditingDisabled) {
            editor.disableEditing();
        }

        // update mobiledoc reference to match initial editor state from parsed
        // html. We use this value to compare on re-renders in case we need to
        // re-parse from html
        this.mobiledoc = editor.serialize(MOBILEDOC_VERSION);
        this._lastMobiledoc = this.mobiledoc;

        this.set('editor', editor);
        this.didCreateEditor(editor);
    },

    didInsertElement() {
        this._super(...arguments);
        let editorElement = this.element.querySelector('[data-kg="editor"]');

        this._pasteHandler = run.bind(this, this.handlePaste);
        editorElement.addEventListener('paste', this._pasteHandler);

        this.element.dataset[DRAG_DISABLED_DATA_ATTR] = 'true';
    },

    // our ember component has rendered, now we need to render the mobiledoc
    // editor itself if necessary
    didRender() {
        this._super(...arguments);
        let {editor} = this;
        if (!editor.hasRendered) {
            let editorElement = this.element.querySelector('[data-kg="editor"]');
            this._isRenderingEditor = true;
            editor.render(editorElement);
            this._isRenderingEditor = false;
        }
    },

    willDestroyElement() {
        this._super(...arguments);

        let editorElement = this.element.querySelector('[data-kg="editor"]');
        editorElement.removeEventListener('paste', this._pasteHandler);

        this.editor.destroy();
    },

    actions: {
        toggleMarkup(markupTagName, postEditor) {
            (postEditor || this.editor).toggleMarkup(markupTagName);
        },

        // range should be set to the full extent of the selection or the
        // appropriate <a> markup. If there's a selection when the link edit
        // component renders it will re-select when finished which should
        // trigger the normal toolbar
        editLink(range) {
            let linkMarkup = getLinkMarkupFromRange(range);
            if ((!range.isCollapsed || linkMarkup) && range.headSection.isMarkerable) {
                this.set('linkRange', range);
            }
        },

        cancelEditLink() {
            this.set('linkRange', null);
        }
    },

    /* ember event handlers --------------------------------------------------*/

    // handle focusin/focusout at the component level so that we don't trigger blur
    // actions when clicking on toolbar buttons
    focusIn(event) {
        if (!this._hasFocus) {
            this._hasFocus = true;
            run.scheduleOnce('actions', this, this.onFocus, event);
        }
    },

    focusOut(event) {
        if (!event.relatedTarget || !this.element.contains(event.relatedTarget)) {
            this._hasFocus = false;
            run.scheduleOnce('actions', this, this.onBlur, event);
        }
    },

    /* custom event handlers ------------------------------------------------ */

    handlePaste(event) {
        let {editor, editor: {range}} = this;
        let {text} = getContentFromPasteEvent(event);

        if (!editor.cursor.isAddressable(event.target)) {
            return;
        }

        if (text && validator.isURL(text)) {
            // if we have a text selection, make that selection a link
            if (range && !range.isCollapsed && range.headSection === range.tailSection && range.headSection.isMarkerable) {
                let linkMarkup = editor.builder.createMarkup('a', {href: text});
                editor.run((postEditor) => {
                    postEditor.addMarkupToRange(range, linkMarkup);
                });
                editor.selectRange(range.tail);

                // prevent mobiledoc's default paste event handler firing
                event.preventDefault();
                event.stopImmediatePropagation();
                return;
            }
        }
    },

    /* mobiledoc event handlers ----------------------------------------------*/

    willHandleNewline(event) {
        event.preventDefault();
        this.onNewline();
    },

    // manipulate mobiledoc content before committing changes
    // - only one section
    // - first section must be a markerable section
    // - if first section is a list, grab the content of the first list item
    didUpdatePost(postEditor) {
        let {builder, editor, editor: {post}} = postEditor;

        // remove any non-markerable sections
        post.sections.forEach((section) => {
            if (!section.isMarkerable && !section.isListSection) {
                let reposition = section === editor.activeSection;
                postEditor.removeSection(section);
                if (reposition) {
                    postEditor.setRange(post.sections.head.tailPosition());
                }
            }
        });

        // strip all sections other than the first
        if (post.sections.length > 1) {
            while (post.sections.length > 1) {
                postEditor.removeSection(post.sections.tail);
            }
            postEditor.setRange(post.sections.head.tailPosition());
        }

        // convert list section to a paragraph section
        if (post.sections.head.isListSection) {
            let list = post.sections.head;
            let listItem = list.items.head;
            let newMarkers = listItem.markers.map(m => m.clone());
            let p = builder.createMarkupSection('p', newMarkers);
            postEditor.replaceSection(list, p);
            postEditor.setRange(post.sections.head.tailPosition());
        }
    },

    postDidChange() {
        // trigger closure action
        this.onChange(this._getHTML());
    },

    cursorDidChange(editor) {
        // if we have `code` or ~strike~ formatting to the left but not the right
        // then toggle the formatting - these formats should only be creatable
        // through the text expansions
        toggleSpecialFormatEditState(editor);

        // pass the selected range through to the toolbar + menu components
        this.set('selectedRange', editor.range);
    },

    // fired when the active section(s) or markup(s) at the current cursor
    // position or selection have changed. We use this event to update the
    // activeMarkup/section tag lists which control button states in our popup
    // toolbar
    inputModeDidChange(editor) {
        let markupTags = arrayToMap(editor.activeMarkups.map(m => m.tagName));

        // On keyboard cursor movement our `cursorDidChange` toggle for special
        // formats happens before mobiledoc's readstate updates the edit states
        // so we have to re-do it here
        // TODO: can we make the event order consistent in mobiledoc-kit?
        toggleSpecialFormatEditState(editor);

        // Avoid updating this component's properties synchronously while
        // rendering the editor (after rendering the component) because it
        // causes Ember to display deprecation warnings
        if (this._isRenderingEditor) {
            run.schedule('afterRender', () => {
                this.set('activeMarkupTagNames', markupTags);
            });
        } else {
            this.set('activeMarkupTagNames', markupTags);
        }
    },

    /* private methods -------------------------------------------------------*/

    // rather than parsing mobiledoc to HTML we can grab the HTML directly from
    // inside the editor element because we should only be dealing with
    // inline markup that directly maps to HTML elements
    _getHTML() {
        if (this.editor && this.editor.element) {
            let firstParagraph = this.editor.element.querySelector('p');

            if (!firstParagraph) {
                return '';
            }

            let html = firstParagraph.innerHTML;
            return cleanBasicHtml(html);
        }
    }
});
<commit_msg>🐛 Fixed email design settings not opening<commit_after>import Component from '@ember/component';
import Editor from 'mobiledoc-kit/editor/editor';
import cleanBasicHtml from '@tryghost/kg-clean-basic-html';
import parserPlugins from '../options/basic-html-parser-plugins';
import registerKeyCommands, {BASIC_KEY_COMMANDS} from '../options/key-commands';
import validator from 'validator';
import {BLANK_DOC, MOBILEDOC_VERSION} from './koenig-editor';
import {DRAG_DISABLED_DATA_ATTR} from '../lib/dnd/constants';
import {arrayToMap, toggleSpecialFormatEditState} from './koenig-editor';
import {assign} from '@ember/polyfills';
import {computed} from '@ember/object';
import {getContentFromPasteEvent} from 'mobiledoc-kit/utils/parse-utils';
import {getLinkMarkupFromRange} from '../utils/markup-utils';
import {registerBasicTextExpansions} from '../options/text-expansions';
import {run} from '@ember/runloop';

const UNDO_DEPTH = 50;

// TODO: extract core to share functionality between this and `{{koenig-editor}}`

export default Component.extend({
    // public attrs
    autofocus: false,
    html: null,
    placeholder: '',
    spellcheck: true,

    // internal properties
    activeMarkupTagNames: null,
    editor: null,
    linkRange: null,
    mobiledoc: null,
    selectedRange: null,

    // private properties
    _hasFocus: false,
    _lastMobiledoc: null,
    _startedRunLoop: false,

    // closure actions
    willCreateEditor() {},
    didCreateEditor() {},
    onChange() {},
    onNewline() {},
    onFocus() {},
    onBlur() {},

    /* computed properties -------------------------------------------------- */

    cleanHTML: computed('html', function () {
        return cleanBasicHtml(this.html || '');
    }),

    // merge in named options with any passed in `options` property data-bag
    editorOptions: computed('cleanHTML', function () {
        let options = this.options || {};
        let atoms = this.atoms || [];
        let cards = this.cards || [];

        return assign({
            html: `<p>${this.cleanHTML || ''}</p>`,
            placeholder: this.placeholder,
            spellcheck: this.spellcheck,
            autofocus: this.autofocus,
            cards,
            atoms,
            unknownCardHandler() {},
            unknownAtomHandler() {}
        }, options);
    }),

    /* lifecycle hooks ------------------------------------------------------ */

    init() {
        this._super(...arguments);
        this.SPECIAL_MARKUPS = [];
    },

    didReceiveAttrs() {
        this._super(...arguments);

        // reset local mobiledoc if html has been changed upstream so that
        // the html will be re-parsed by the mobiledoc-kit editor
        if (this.cleanHTML !== this._getHTML()) {
            this.set('mobiledoc', null);
        }
    },

    willRender() {
        let mobiledoc = this.mobiledoc;

        if (!mobiledoc && !this.cleanHTML) {
            mobiledoc = BLANK_DOC;
        }

        let mobiledocIsSame =
            (this._lastMobiledoc && this._lastMobiledoc === mobiledoc);
        let isEditingDisabledIsSame =
            this._lastIsEditingDisabled === this.isEditingDisabled;

        // no change to mobiledoc, no need to recreate the editor
        if (mobiledocIsSame && isEditingDisabledIsSame) {
            return;
        }

        // update our internal references
        this._lastIsEditingDisabled = this.isEditingDisabled;

        // trigger the willCreateEditor closure action
        this.willCreateEditor();

        // teardown any old editor that might be around
        let editor = this.editor;
        if (editor) {
            editor.destroy();
        }

        // create a new editor
        let editorOptions = this.editorOptions;
        editorOptions.mobiledoc = mobiledoc;
        editorOptions.showLinkTooltips = false;
        editorOptions.undoDepth = UNDO_DEPTH;
        editorOptions.parserPlugins = parserPlugins;

        editor = new Editor(editorOptions);

        registerKeyCommands(editor, this, BASIC_KEY_COMMANDS);
        registerBasicTextExpansions(editor);

        // set up editor hooks
        editor.willRender(() => {
            // The editor's render/rerender will happen after this `editor.willRender`,
            // so we explicitly start a runloop here if there is none, so that the
            // add/remove card hooks happen inside a runloop.
            // When pasting text that gets turned into a card, for example,
            // the add card hook would run outside the runloop if we didn't begin a new
            // one now.
            if (!run.currentRunLoop) {
                this._startedRunLoop = true;
                run.begin();
            }
        });

        editor.didRender(() => {
            // if we had explicitly started a runloop in `editor.willRender`,
            // we must explicitly end it here
            if (this._startedRunLoop) {
                this._startedRunLoop = false;
                run.end();
            }
        });

        editor.willHandleNewline((event) => {
            run.join(() => {
                this.willHandleNewline(event);
            });
        });

        editor.didUpdatePost((postEditor) => {
            run.join(() => {
                this.didUpdatePost(postEditor);
            });
        });

        editor.postDidChange(() => {
            run.join(() => {
                this.postDidChange(editor);
            });
        });

        editor.cursorDidChange(() => {
            run.join(() => {
                this.cursorDidChange(editor);
            });
        });

        editor.inputModeDidChange(() => {
            if (this.isDestroyed) {
                return;
            }
            run.join(() => {
                this.inputModeDidChange(editor);
            });
        });

        if (this.isEditingDisabled) {
            editor.disableEditing();
        }

        // update mobiledoc reference to match initial editor state from parsed
        // html. We use this value to compare on re-renders in case we need to
        // re-parse from html
        this.mobiledoc = editor.serialize(MOBILEDOC_VERSION);
        this._lastMobiledoc = this.mobiledoc;

        this.set('editor', editor);
        this.didCreateEditor(editor);
    },

    didInsertElement() {
        this._super(...arguments);
        let editorElement = this.element.querySelector('[data-kg="editor"]');

        this._pasteHandler = run.bind(this, this.handlePaste);
        editorElement.addEventListener('paste', this._pasteHandler);

        this.element.dataset[DRAG_DISABLED_DATA_ATTR] = 'true';
    },

    // our ember component has rendered, now we need to render the mobiledoc
    // editor itself if necessary
    didRender() {
        this._super(...arguments);
        let {editor} = this;
        if (!editor.hasRendered) {
            let editorElement = this.element.querySelector('[data-kg="editor"]');
            this._isRenderingEditor = true;
            editor.render(editorElement);
            this._isRenderingEditor = false;
        }
    },

    willDestroyElement() {
        this._super(...arguments);

        let editorElement = this.element.querySelector('[data-kg="editor"]');
        editorElement.removeEventListener('paste', this._pasteHandler);

        this.editor.destroy();
    },

    actions: {
        toggleMarkup(markupTagName, postEditor) {
            (postEditor || this.editor).toggleMarkup(markupTagName);
        },

        // range should be set to the full extent of the selection or the
        // appropriate <a> markup. If there's a selection when the link edit
        // component renders it will re-select when finished which should
        // trigger the normal toolbar
        editLink(range) {
            let linkMarkup = getLinkMarkupFromRange(range);
            if ((!range.isCollapsed || linkMarkup) && range.headSection.isMarkerable) {
                this.set('linkRange', range);
            }
        },

        cancelEditLink() {
            this.set('linkRange', null);
        }
    },

    /* ember event handlers --------------------------------------------------*/

    // handle focusin/focusout at the component level so that we don't trigger blur
    // actions when clicking on toolbar buttons
    focusIn(event) {
        if (!this._hasFocus) {
            this._hasFocus = true;
            run.scheduleOnce('actions', this, this.onFocus, event);
        }
    },

    focusOut(event) {
        if (!event.relatedTarget || !this.element.contains(event.relatedTarget)) {
            this._hasFocus = false;
            run.scheduleOnce('actions', this, this.onBlur, event);
        }
    },

    /* custom event handlers ------------------------------------------------ */

    handlePaste(event) {
        let {editor, editor: {range}} = this;
        let {text} = getContentFromPasteEvent(event);

        if (!editor.cursor.isAddressable(event.target)) {
            return;
        }

        if (text && validator.isURL(text)) {
            // if we have a text selection, make that selection a link
            if (range && !range.isCollapsed && range.headSection === range.tailSection && range.headSection.isMarkerable) {
                let linkMarkup = editor.builder.createMarkup('a', {href: text});
                editor.run((postEditor) => {
                    postEditor.addMarkupToRange(range, linkMarkup);
                });
                editor.selectRange(range.tail);

                // prevent mobiledoc's default paste event handler firing
                event.preventDefault();
                event.stopImmediatePropagation();
                return;
            }
        }
    },

    /* mobiledoc event handlers ----------------------------------------------*/

    willHandleNewline(event) {
        event.preventDefault();
        this.onNewline();
    },

    // manipulate mobiledoc content before committing changes
    // - only one section
    // - first section must be a markerable section
    // - if first section is a list, grab the content of the first list item
    didUpdatePost(postEditor) {
        let {builder, editor, editor: {post}} = postEditor;

        // remove any non-markerable sections
        post.sections.forEach((section) => {
            if (!section.isMarkerable && !section.isListSection) {
                let reposition = section === editor.activeSection;
                postEditor.removeSection(section);
                if (reposition) {
                    postEditor.setRange(post.sections.head.tailPosition());
                }
            }
        });

        // strip all sections other than the first
        if (post.sections.length > 1) {
            while (post.sections.length > 1) {
                postEditor.removeSection(post.sections.tail);
            }
            postEditor.setRange(post.sections.head.tailPosition());
        }

        // convert list section to a paragraph section
        if (post.sections.head.isListSection) {
            let list = post.sections.head;
            let listItem = list.items.head;
            let newMarkers = listItem.markers.map(m => m.clone());
            let p = builder.createMarkupSection('p', newMarkers);
            postEditor.replaceSection(list, p);
            postEditor.setRange(post.sections.head.tailPosition());
        }
    },

    postDidChange() {
        // trigger closure action
        this.onChange(this._getHTML());
    },

    cursorDidChange(editor) {
        // if we have `code` or ~strike~ formatting to the left but not the right
        // then toggle the formatting - these formats should only be creatable
        // through the text expansions
        toggleSpecialFormatEditState(editor);

        // pass the selected range through to the toolbar + menu components
        this.set('selectedRange', editor.range);
    },

    // fired when the active section(s) or markup(s) at the current cursor
    // position or selection have changed. We use this event to update the
    // activeMarkup/section tag lists which control button states in our popup
    // toolbar
    inputModeDidChange(editor) {
        let markupTags = arrayToMap(editor.activeMarkups.map(m => m.tagName));

        // On keyboard cursor movement our `cursorDidChange` toggle for special
        // formats happens before mobiledoc's readstate updates the edit states
        // so we have to re-do it here
        // TODO: can we make the event order consistent in mobiledoc-kit?
        toggleSpecialFormatEditState(editor);

        // Avoid updating this component's properties synchronously while
        // rendering the editor (after rendering the component) because it
        // causes Ember to display deprecation warnings
        if (this._isRenderingEditor) {
            run.schedule('afterRender', () => {
                this.set('activeMarkupTagNames', markupTags);
            });
        } else {
            this.set('activeMarkupTagNames', markupTags);
        }
    },

    /* private methods -------------------------------------------------------*/

    // rather than parsing mobiledoc to HTML we can grab the HTML directly from
    // inside the editor element because we should only be dealing with
    // inline markup that directly maps to HTML elements
    _getHTML() {
        if (this.editor && this.editor.element) {
            let firstParagraph = this.editor.element.querySelector('p');

            if (!firstParagraph) {
                return '';
            }

            let html = firstParagraph.innerHTML || '';
            return cleanBasicHtml(html);
        }
    }
});
<|endoftext|>


<commit_before>/* @flow */

const path = require('path')

const { id } = require('../../metadata')
const stater = require('../stater')

// Drop incomplete events after this delay (in milliseconds).
// TODO tweak the value (the initial value was chosen because it looks like a
//      good value, it is not something that was computed)
const DELAY = 3000

// TODO add unit tests and logs

/*::
import type Buffer from './buffer'
import type { AtomWatcherEvent } from './event'
import type { Checksumer } from '../checksumer'

type IncompleteItem = {
  event: AtomWatcherEvent,
  timestamp: number,
}
*/

async function rebuildIncompleteEvent (item /*: IncompleteItem */, event /*: AtomWatcherEvent */, opts /*: { syncPath: string , checksumer: Checksumer } */) /*: Promise<AtomWatcherEvent> */ {
  // The || '' is just a trick to please flow
  const oldPath /*: string */ = event.oldPath || ''
  const p = item.event.path.replace(oldPath, event.path)
  const absPath = path.join(opts.syncPath, p)
  const stats = await stater.stat(absPath)
  const kind = stater.kind(stats)
  let md5sum
  if (kind === 'file') {
    md5sum = await opts.checksumer.push(absPath)
  }
  return {
    action: item.event.action,
    path: p,
    _id: id(p),
    kind,
    stats,
    md5sum
  }
}

// When a file is added or updated, and it is moved just after, the first event
// is marked as incomplete by addChecksum because we cannot compute the
// checksum at the given path. But the event is still relevant, in particular if
// a directory that is an ancestor of this file has been moved. With the renamed
// event, by comparing the path, we can extrapolate the new path and check with
// fs.stats if we have a file here.
//
// Cf test/property/local_watcher/swedish_krona.json
module.exports = function (buffer /*: Buffer */, opts /*: { syncPath: string , checksumer: Checksumer } */) /*: Buffer */ {
  const incompletes = []
  return buffer.asyncMap(async (events) => {
    // Filter out the incomplete events
    const batch = []
    for (const event of events) {
      if (event.incomplete) {
        incompletes.push({ event, timestamp: Date.now() })
        continue
      }
      batch.push(event)
    }

    // Let's see if we can match an incomplete event with this renamed event
    for (const event of batch) {
      if (incompletes.length === 0 || event.action !== 'renamed') {
        continue
      }

      const now = Date.now()
      for (let i = 0; i < incompletes.length; i++) {
        const item = incompletes[i]

        // Remove the expired incomplete events
        if (item.timestamp + DELAY < now) {
          incompletes.splice(i, 1)
          i--
          continue
        }

        if (event.oldPath && item.event.path.startsWith(event.oldPath + '/')) {
          // We have a match, try to rebuild the incomplete event
          try {
            const rebuilt = await rebuildIncompleteEvent(item, event, opts)
            batch.push(rebuilt)
          } catch (err) {
            // If we have an error, there is probably not much that we can do
          }
          incompletes.splice(i, 1)
          break
        }
      }
    }

    return batch
  })
}
<commit_msg>core/local/steps/incomplete_fixer: Fix path sep (#1430)<commit_after>/* @flow */

const path = require('path')

const { id } = require('../../metadata')
const stater = require('../stater')

// Drop incomplete events after this delay (in milliseconds).
// TODO tweak the value (the initial value was chosen because it looks like a
//      good value, it is not something that was computed)
const DELAY = 3000

// TODO add unit tests and logs

/*::
import type Buffer from './buffer'
import type { AtomWatcherEvent } from './event'
import type { Checksumer } from '../checksumer'

type IncompleteItem = {
  event: AtomWatcherEvent,
  timestamp: number,
}
*/

async function rebuildIncompleteEvent (item /*: IncompleteItem */, event /*: AtomWatcherEvent */, opts /*: { syncPath: string , checksumer: Checksumer } */) /*: Promise<AtomWatcherEvent> */ {
  // The || '' is just a trick to please flow
  const oldPath /*: string */ = event.oldPath || ''
  const p = item.event.path.replace(oldPath, event.path)
  const absPath = path.join(opts.syncPath, p)
  const stats = await stater.stat(absPath)
  const kind = stater.kind(stats)
  let md5sum
  if (kind === 'file') {
    md5sum = await opts.checksumer.push(absPath)
  }
  return {
    action: item.event.action,
    path: p,
    _id: id(p),
    kind,
    stats,
    md5sum
  }
}

// When a file is added or updated, and it is moved just after, the first event
// is marked as incomplete by addChecksum because we cannot compute the
// checksum at the given path. But the event is still relevant, in particular if
// a directory that is an ancestor of this file has been moved. With the renamed
// event, by comparing the path, we can extrapolate the new path and check with
// fs.stats if we have a file here.
//
// Cf test/property/local_watcher/swedish_krona.json
module.exports = function (buffer /*: Buffer */, opts /*: { syncPath: string , checksumer: Checksumer } */) /*: Buffer */ {
  const incompletes = []
  return buffer.asyncMap(async (events) => {
    // Filter out the incomplete events
    const batch = []
    for (const event of events) {
      if (event.incomplete) {
        incompletes.push({ event, timestamp: Date.now() })
        continue
      }
      batch.push(event)
    }

    // Let's see if we can match an incomplete event with this renamed event
    for (const event of batch) {
      if (incompletes.length === 0 || event.action !== 'renamed') {
        continue
      }

      const now = Date.now()
      for (let i = 0; i < incompletes.length; i++) {
        const item = incompletes[i]

        // Remove the expired incomplete events
        if (item.timestamp + DELAY < now) {
          incompletes.splice(i, 1)
          i--
          continue
        }

        if (event.oldPath && item.event.path.startsWith(event.oldPath + path.sep)) {
          // We have a match, try to rebuild the incomplete event
          try {
            const rebuilt = await rebuildIncompleteEvent(item, event, opts)
            batch.push(rebuilt)
          } catch (err) {
            // If we have an error, there is probably not much that we can do
          }
          incompletes.splice(i, 1)
          break
        }
      }
    }

    return batch
  })
}
<|endoftext|>


<commit_before>//return capitalized abbreviations for the input
function getInitials(name) {
	return name.split(‘ ‘)
		.map(word => word.charAt(0))
		.join(‘’);
}
<commit_msg>Added uppercasing feature<commit_after>//return capitalized abbreviations for the input
function getInitials(name) {
	return name.split(‘ ‘)
		.map(word => word.charAt(0).toUpperCase())
		.join(‘’);
}
<|endoftext|>


<commit_before>var _ = require('lodash'),
  CliSelfInstall = require('./cli-self-install');

module.exports = function(packageName, opts) {
  return CliSelfInstall(_.extend({
    filter: packageName
  }, opts));
};
<commit_msg>forgot about Interview being required by npme.<commit_after>var _ = require('lodash'),
  CliSelfInstall = require('./cli-self-install'),
  ndm = function(packageName, opts) {
    return CliSelfInstall(_.extend({
      filter: packageName
    }, opts));
  };

// some other exports are needed.for npme.
ndm.Interview = require('./interview');

module.exports = ndm;
<|endoftext|>


<commit_before>function padZero(value) {
	return (value < 10 ? '0' + value : value);
}

$(function () {
	// init
	if ($('.logged-in').length) {
		if (typeof DexEdit !== 'undefined') {
			DexEdit.init();
			$('[contenteditable="true"]').each(function () {
				var bg = $(this).css('backgroundColor');
				$(this).animate({'backgroundColor': '#A9CC66'}, 50, function () {
					$(this).animate({'backgroundColor': bg}, 2000);
				});
			});

			if (typeof author !== 'undefined' && typeof last_save !== 'undefined') {
				apiStatusSuccess(author + ' (' + padZero(last_save.getDate()) + '-' + padZero(last_save.getMonth() + 1) + ' ' + last_save.getHours() + ':' + padZero(last_save.getMinutes()) + ')');
			}
		}
	}

	// saving
	var saveTimeout = null;
	$('[data-dexeditable]').on('input', function (e) {
		apiStatusClear();
		clearTimeout(saveTimeout);
		saveTimeout = setTimeout(save, 1000);
	});

	function save() {
		$.event.trigger({
			type: 'save'
		});

		var time = new Date();
		apiStatusSuccess(time.getHours() + ':' + padZero(time.getMinutes()));
	}

	$(document).on('keydown', function (e) {
	    if (e.ctrlKey && e.which === 83) {
	        e.preventDefault();
	        save();
	        return false;
	    }
	});

	// logging out
	function adminBarLogOut() {
		$('.dex-api').fadeOut().remove();
		$('.dex-admin-bar .logged-in').fadeOut(function () {
			$('.dex-admin-bar .logged-out').fadeIn('fast');
		}, 'fast');

		if (typeof DexEdit !== 'undefined') {
			DexEdit.destroy();
		}
	}

	function adminBarForget() {
		$('.dex-admin-bar').slideUp(function () {
			$(this).remove();
		}, 'fast');

		$('body').animate({
			'padding-top': '0'
		}, 'fast');
	}

	function session() {
		api('/' + base_url + 'api/core/users/', {
			'action': 'timeleft'
		}, function(data) {
			if (data['timeleft'] > 0) {
				setTimeout(session, data['timeleft'] * 1000);
			} else {
				adminBarLogOut();
			}
		}, function () {
			adminBarLogOut();
		});
	}
	setTimeout(session, session_time * 1000);

	$('.dex-admin-bar .logged-in .current-user a').click(function (e) {
		e.preventDefault();
		var admin = ($(this).attr('data-admin') === '1' ? 1 : 0);
		api('/' + base_url + 'api/core/users/', {
			'action': 'logout',
			'admin': admin
		}, function(data) {
			if (admin === 0) {
				adminBarLogOut();
			} else {
				window.location = '/' + base_url + 'admin/';
			}
		});
	});

	$('.dex-admin-bar .logged-out .current-user a').click(function () {
		api('/' + base_url + 'api/core/users/', {
			'action': 'forget'
		}, function (data) {
			adminBarForget();
		});
	});
});

$('html').on('click', 'a[href="#"]', function (e) {
	e.preventDefault();
});

function switchPopupFrame(popup) {
	$('.fancybox-inner').animate({'scrollTop': 0});

	var frames = popup.find('> div');
	frames.eq(1).css('display', 'inline-block');
	popup.animate({'margin-left': '-' + frames.eq(0).width() + 'px'}, function () {
		popup.css({
			'margin-left': '0'
		});
		frames.eq(0).css('display', 'none');
		parent.$.fancybox.update();
	});
}

function preSwitchPopupFrame(popup) {
	var frames = popup.find('> div');
	frames.eq(1).css('display', 'inline-block');
	frames.eq(0).css('display', 'none');
	parent.$.fancybox.update();
}<commit_msg>Bugfix<commit_after>function padZero(value) {
	return (value < 10 ? '0' + value : value);
}

$(function () {
	// init
	if ($('.logged-in').length) {
		if (typeof DexEdit !== 'undefined') {
			DexEdit.init();
			$('[contenteditable="true"]').each(function () {
				var bg = $(this).css('backgroundColor');
				$(this).animate({'backgroundColor': '#A9CC66'}, 50, function () {
					$(this).animate({'backgroundColor': bg}, 2000);
				});
			});

			if (typeof author !== 'undefined' && typeof last_save !== 'undefined') {
				apiStatusSuccess(author + ' (' + padZero(last_save.getDate()) + '-' + padZero(last_save.getMonth() + 1) + ' ' + last_save.getHours() + ':' + padZero(last_save.getMinutes()) + ')');
			}
		}
	}

	// saving
	var saveTimeout = null;
	$('[data-dexeditable]').on('input', function (e) {
		apiStatusClear();
		clearTimeout(saveTimeout);
		saveTimeout = setTimeout(save, 1000);
	});

	function save() {
		$.event.trigger({
			type: 'save'
		});

		var time = new Date();
		apiStatusSuccess(time.getHours() + ':' + padZero(time.getMinutes()));
	}

	$(document).on('keydown', function (e) {
	    if (e.ctrlKey && e.which === 83) {
	        e.preventDefault();
	        save();
	        return false;
	    }
	});

	// logging out
	function adminBarLogOut() {
		$('.dex-api').fadeOut('fast').remove();
		$('.dex-admin-bar .logged-in').fadeOut('fast', function () {
			$('.dex-admin-bar .logged-out').fadeIn('fast');
		});

		if (typeof DexEdit !== 'undefined') {
			DexEdit.destroy();
		}
	}

	function adminBarForget() {
		$('.dex-admin-bar').slideUp('fast', function () {
			$(this).remove();
		});

		$('body').animate({
			'padding-top': '0'
		}, 'fast');
	}

	function session() {
		api('/' + base_url + 'api/core/users/', {
			'action': 'timeleft'
		}, function(data) {
			if (data['timeleft'] > 0) {
				setTimeout(session, data['timeleft'] * 1000);
			} else {
				adminBarLogOut();
			}
		}, function () {
			adminBarLogOut();
		});
	}
	setTimeout(session, session_time * 1000);

	$('.dex-admin-bar .logged-in .current-user a').click(function (e) {
		e.preventDefault();
		var admin = ($(this).attr('data-admin') === '1' ? 1 : 0);
		api('/' + base_url + 'api/core/users/', {
			'action': 'logout',
			'admin': admin
		}, function(data) {
			if (admin === 0) {
				adminBarLogOut();
			} else {
				window.location = '/' + base_url + 'admin/';
			}
		});
	});

	$('.dex-admin-bar .logged-out .current-user a').click(function () {
		api('/' + base_url + 'api/core/users/', {
			'action': 'forget'
		}, function (data) {
			adminBarForget();
		});
	});
});

$('html').on('click', 'a[href="#"]', function (e) {
	e.preventDefault();
});

function switchPopupFrame(popup) {
	$('.fancybox-inner').animate({'scrollTop': 0});

	var frames = popup.find('> div');
	frames.eq(1).css('display', 'inline-block');
	popup.animate({'margin-left': '-' + frames.eq(0).width() + 'px'}, function () {
		popup.css({
			'margin-left': '0'
		});
		frames.eq(0).css('display', 'none');
		parent.$.fancybox.update();
	});
}

function preSwitchPopupFrame(popup) {
	var frames = popup.find('> div');
	frames.eq(1).css('display', 'inline-block');
	frames.eq(0).css('display', 'none');
	parent.$.fancybox.update();
}<|endoftext|>


<commit_before>"use strict";

module.exports = {
	Bot: require(__dirname + '/viber-bot'),
	Events: require(__dirname + '/event-consts'),
	Message: {
		Text: require(__dirname + '/message/text-message'),
		Url: require(__dirname + '/message/url-message'),
		Contact: require(__dirname + '/message/contact-message'),
		File: require(__dirname + '/message/file-message'),
		Location: require(__dirname + '/message/location-message'),
		Picture: require(__dirname + '/message/picture-message'),
		Video: require(__dirname + '/message/video-message'),
		Sticker: require(__dirname + '/message/sticker-message'),
	}
};
<commit_msg>expose UserProfile to the package consumers<commit_after>"use strict";

module.exports = {
	Bot: require(__dirname + '/viber-bot'),
	Events: require(__dirname + '/event-consts'),
	UserProfile: require(__dirname + '/user-profile'),
	Message: {
		Text: require(__dirname + '/message/text-message'),
		Url: require(__dirname + '/message/url-message'),
		Contact: require(__dirname + '/message/contact-message'),
		File: require(__dirname + '/message/file-message'),
		Location: require(__dirname + '/message/location-message'),
		Picture: require(__dirname + '/message/picture-message'),
		Video: require(__dirname + '/message/video-message'),
		Sticker: require(__dirname + '/message/sticker-message'),
	}
};
<|endoftext|>


<commit_before>'use strict';

var API_KEY;

/**
 * Validates input for upload method
 * @param {String} path Path to the image to be uploaded
 * @param {Object} options Options for upload
 * @param {Array} [options.tags] Array of tags to be associated to the image
 * @return {String|undefined} Error message if validation fails; undefined otherwise.
 */

function validateUpload(path, options) {
	if(!path){
		return '"path" is required';
	}
	if(!API_KEY){
		return 'API Key not set';
	}
	if(options){
		if(options.tags){
			if(!Array.isArray(options.tags)){
				return 'Invalid format for "tags"';
			}
		}
	}
	return;
}

/**
 * Validates input for transform method
 * @param {String} path Path to originial image on img-cloud server
 * @options {Object} options Transformation options
 * @return {String|undefined} Error message if validation fails; undefined otherwise.
 */
function validateTransform(path, options){
	if(!path){
		return '"path" is required';
	}
	if(options){
		if(options.height){
			try {
				parseInt(options.height);
			}catch(e){
				console.error(e);
				return 'Invalid type for height. Must be a number';
			}
		}
		if(options.width){
			try {
				parseInt(options.width);
			}catch(e){
				console.error(e);
				return 'Invalid type for width. Must be a number';
			}
		}
	}
	return;
}

/**
 * Returns the URL for the transformed image
 * @param {String} path Relative path for the original image file on img-cloud server
 * @param {Object} options Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 */
function getImageTagSource(path, options) {
	var tokens = path.split('/'),
		fileName = tokens[tokens.length - 1],
		transformationCsv = '';
	if(options.width){
		transformationCsv += 'w_'+options.height;
	}
	if(options.height){
		transformationCsv += 'h_'+options.height;
	}
	return tokens.slice(0, tokens[tokens.length - 1]).concat([transformationCsv, fileName]).join('/');
}

/**
 * Returns the image tag attributes other than source
 * @param {Object} [options] Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 * @param {String} [options.class] CSS class to be used on the image DOM
 * @param {String} [options.alt] "alt" attribute to be used on the image DOM
 * @param {String} [options.title] "title" attribute to be used on the image DOM
 * @param {String} [options.style] "style" attribute to be used on the image DOM
 */
function getImageTagAttributes(options) {
	if(!options){
		return '';
	}
	var attributes = '';
	if(options.height){
		attributes += ' height="'+options.height+'"';
	}
	if(options.width){
		attributes += ' width="'+options.width+'"';
	}
	if(options.class){
		attributes += ' class="'+options.class+'"';
	}
	if(options.alt){
		attributes += ' alt="'+options.alt+'"';
	}
	if(options.title){
		attributes += ' title="'+options.title+'"';
	}
	if(options.style){
		attributes += ' style="'+options.style+'"';
	}
	return attributes;
}

/**
 * Sets configuration options used for image upload
 * @param {Object} options Configuration options
 * @param {String} options.apiKey API Key for img-cloud account
 */

module.exports.configure = function(options){
	if(!options){
		throw new Error('Missing configuration options');
	}

	if(!options.apiKey){
		throw new Error('Missing API Key');
	}

	API_KEY = options.apiKey;
}

/**
 * Uploads an image to img-cloud server
 * @param {String} path Path to the image to be uploaded - Required
 * @param {Object} options Options for upload
 * @param {String} [options.folder='default'] Name of the folder the image should be added to
 * @param {Array} [options.tags] Array of tags to be associated to the image
 * @example
Example code for uploading a file

 * imgCloud.upload('my_file.png', {folder: 'avatar', tags: ['avatar', 'user']});
 * @return {Object} A JSON object that contains the file URL, folder name and tags
 * @example
Example response

 * {
 *   "url": "http://www.imgcloud.io/ic_564323/4312345676_my_file.png",
 *   "folder": "avatar",
 *   "tags": ["avatar", "user"]
 * }
 */
module.exports.upload = function(path, options, callaback){
	var fs = require('fs'),
		restler = require('restler'),
		config = require('../config'),
		validator = require('./validator');

	var error = validator.validateUpload(path, options);
	if(error){
		if(callaback){
			callaback(error, null);
			return;
		}
	}

	fs.stat(path, function(err, stats) {
		var extension = path.substring(path.lastIndexOf('.') + 1, path.length),
			data = {
				image: restler.file(path, null, stats.size, null, 'img/'+extension),
				apiKey: API_KEY
			};
		if(options.folder){
			data.folder = options.folder
		}
		if(options.tags && options.tags.length){
			data.tags = options.tags;
		}
	    restler.post(config.endPointBase + config.endPoints.upload, {
	        multipart: true,
	        data: data
	    }).on('complete', function(data, response) {
			if(response.statusCode !== 200){
				if(callaback){
					callaback(response, null)
					return;
				}
			}
			if(callaback){
				callaback(null, data);
			}
	    });
	});
};

/**
 * Returns img tag for the transformed image
 * @param {String} path Relative path for the original image file on img-cloud server
 * @param {Object} options Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 */
module.exports.transform = function(path, options){
	var error = validateTransform(path, options);
	if(error){
		throw new Error(error);
		return;
	}
	var source = getImageTagSource(path, options),
		attributes = getImageTagAttributes(options);

	return '';
}

<commit_msg>Adding the library methods<commit_after>'use strict';

var API_KEY;

// Private functions

/**
 * Validates input for upload method
 * @param {String} path Path to the image to be uploaded
 * @param {Object} options Options for upload
 * @param {Array} [options.tags] Array of tags to be associated to the image
 * @return {String|undefined} Error message if validation fails; undefined otherwise.
 */

function validateUpload(path, options) {
	if(!path){
		return '"path" is required';
	}
	if(!API_KEY){
		return 'API Key not set';
	}
	if(options){
		if(options.tags){
			if(!Array.isArray(options.tags)){
				return 'Invalid format for "tags"';
			}
		}
	}
	return;
}

/**
 * Validates input for transform method
 * @param {String} path Path to originial image on img-cloud server
 * @options {Object} options Transformation options
 * @return {String|undefined} Error message if validation fails; undefined otherwise.
 */
function validateTransform(path, options){
	if(!path){
		return '"path" is required';
	}
	if(options){
		if(options.height){
			try {
				parseInt(options.height);
			}catch(e){
				console.error(e);
				return 'Invalid type for height. Must be a number';
			}
		}
		if(options.width){
			try {
				parseInt(options.width);
			}catch(e){
				console.error(e);
				return 'Invalid type for width. Must be a number';
			}
		}
	}
	return;
}

/**
 * Returns the URL for the transformed image
 * @param {String} path Relative path for the original image file on img-cloud server
 * @param {Object} options Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 */
function getImageTagSource(path, options) {
	var tokens = path.split('/'),
		fileName = tokens[tokens.length - 1],
		transformationCsv = '';
	if(options.width){
		transformationCsv += 'w_'+options.height;
	}
	if(options.height){
		transformationCsv += 'h_'+options.height;
	}
	return tokens.slice(0, tokens[tokens.length - 1]).concat([transformationCsv, fileName]).join('/');
}

/**
 * Returns the image tag attributes other than source
 * @param {Object} [options] Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 * @param {String} [options.class] CSS class to be used on the image DOM
 * @param {String} [options.alt] "alt" attribute to be used on the image DOM
 * @param {String} [options.title] "title" attribute to be used on the image DOM
 * @param {String} [options.style] "style" attribute to be used on the image DOM
 */
function getImageTagAttributes(options) {
	if(!options){
		return '';
	}
	var attributes = '';
	if(options.height){
		attributes += ' height="'+options.height+'"';
	}
	if(options.width){
		attributes += ' width="'+options.width+'"';
	}
	if(options.class){
		attributes += ' class="'+options.class+'"';
	}
	if(options.alt){
		attributes += ' alt="'+options.alt+'"';
	}
	if(options.title){
		attributes += ' title="'+options.title+'"';
	}
	if(options.style){
		attributes += ' style="'+options.style+'"';
	}
	return attributes;
}

// Public methods

/**
 * Sets configuration options used for image upload
 * @param {Object} options Configuration options
 * @param {String} options.apiKey API Key for img-cloud account
 */

module.exports.configure = function(options){
	if(!options){
		throw new Error('Missing configuration options');
	}

	if(!options.apiKey){
		throw new Error('Missing API Key');
	}

	API_KEY = options.apiKey;
}

/**
 * Uploads an image to img-cloud server
 * @param {String} path Path to the image to be uploaded - Required
 * @param {Object} options Options for upload
 * @param {String} [options.folder='default'] Name of the folder the image should be added to
 * @param {Array} [options.tags] Array of tags to be associated to the image
 * @example
Example code for uploading a file

 * imgCloud.upload('my_file.png', {folder: 'avatar', tags: ['avatar', 'user']});
 * @return {Object} A JSON object that contains the file URL, folder name and tags
 * @example
Example response

 * {
 *   "url": "http://www.imgcloud.io/ic_564323/4312345676_my_file.png",
 *   "folder": "avatar",
 *   "tags": ["avatar", "user"]
 * }
 */
module.exports.upload = function(path, options, callaback){
	var fs = require('fs'),
		restler = require('restler'),
		config = require('../config'),
		validator = require('./validator');

	var error = validator.validateUpload(path, options);
	if(error){
		if(callaback){
			callaback(error, null);
			return;
		}
	}

	fs.stat(path, function(err, stats) {
		var extension = path.substring(path.lastIndexOf('.') + 1, path.length),
			data = {
				image: restler.file(path, null, stats.size, null, 'img/'+extension),
				apiKey: API_KEY
			};
		if(options.folder){
			data.folder = options.folder
		}
		if(options.tags && options.tags.length){
			data.tags = options.tags;
		}
	    restler.post(config.endPointBase + config.endPoints.upload, {
	        multipart: true,
	        data: data
	    }).on('complete', function(data, response) {
			if(response.statusCode !== 200){
				if(callaback){
					callaback(response, null)
					return;
				}
			}
			if(callaback){
				callaback(null, data);
			}
	    });
	});
};

/**
 * Returns img tag for the transformed image
 * @param {String} path Relative path for the original image file on img-cloud server
 * @param {Object} options Transformation options
 * @param {String} [options.height] Expected height of the transformed image
 * @param {String} [options.width] Expected width of the transformed image
 */
module.exports.transform = function(path, options){
	var error = validateTransform(path, options);
	if(error){
		throw new Error(error);
		return;
	}
	var source = getImageTagSource(path, options),
		attributes = getImageTagAttributes(options);

	return '';
}

<|endoftext|>


<commit_before>//function generateData() {

var constituencies = ["Belfast East", "Belfast North", "Belfast South", "Belfast West", "East Antrim", "East Londonderry", "Fermanagh and South Tyrone", "Foyle", "Lagan Valley", "Mid Ulster", "Newry and Armagh", "North Antrim", "North Down", "South Antrim", "South Down", "Strangford", "Upper Bann", "West Tyrone"];

var parties = {
  "Alliance Party": {
    "Party_Id": 19,
    "Party_Abbreviation": "APNI",
    "Hex_Col": "#ffe600"
  },
  "Democratic Unionist Party": {
    "Party_Id": 20,
    "Party_Abbreviation": "DUP",
    "Hex_Col": "#ff8000"
  },
  "Green Party": {
    "Party_Id": 111,
    "Party_Abbreviation": "GPNI",
    "Hex_Col": "#33ff33"
  },
  "Independent": {
    "Party_Id": 21,
    "Party_Abbreviation": "IND",
    "Hex_Col": "#669999"
  },
  "NI21": {
    "Party_Id": 918,
    "Party_Abbreviation": "NI21",
    "Hex_Col": "#581845"
  },
  "Sinn Fein": {
    "Party_Id": 24,
    "Party_Abbreviation": "SF",
    "Hex_Col": "#009900"
  },
  "Social Democratic and Labour Party": {
    "Party_Id": 23,
    "Party_Abbreviation": "SDLP",
    "Hex_Col": "#cc3300"
  },
  "Traditional Unionist Voice": {
    "Party_Id": 141,
    "Party_Abbreviation": "TUV",
    "Hex_Col": "#0000e6"
  },
  "UK Independence Party": {
    "Party_Id": 689,
    "Party_Abbreviation": "UKIP",
    "Hex_Col": "#cc00cc"
  },
  "Ulster Unionist Party": {
    "Party_Id": 26,
    "Party_Abbreviation": "UUP",
    "Hex_Col": "#62cae7"
  },
  "People Before Profit Alliance": {
    "Party_Id": 999,
    "Party_Abbreviation": "PBP",
    "Hex_Col": "#ff3399"
  },
  "Workers Party": {
    "Party_Id": 998,
    "Party_Abbreviation": "WP",
    "Hex_Col": "#ff0000"
  },
  "Procapitalism": {
    "Party_Id": 997,
    "Party_Abbreviation": "CAP",
    "Hex_Col": "#f633ff"
  },
  "Socialist Party": {
    "Party_Id": 996,
    "Party_Abbreviation": "SP",
    "Hex_Col": "#e60000"
  },
  "Socialist Party (NI)": {
    "Party_Id": 996,
    "Party_Abbreviation": "SP",
    "Hex_Col": "#e60000"
  },
  "British Nationalist Party": {
    "Party_Id": 995,
    "Party_Abbreviation": "BNP",
    "Hex_Col": "#b7bdff"
  },
  "Progressive Unionist Party": {
    "Party_Id": 994,
    "Party_Abbreviation": "PUP",
    "Hex_Col": "#1e0082"
  }
}

$.each(constituencies, function(i, constituency) {
    $("#constituencySelect").append("<option>" + constituency + "</option>")
})

var transferData = {};
$.getJSON("2011/ni/party-transfers.json", function (json) {
    console.log(json);
    var transfers = {};
    $.each(json, function (i, constituency) {
        var con = constituency.Constituency_Name;
        transfers[con] = {};
        $.each(constituency.Counts, function (j, count) { // loop thru each count
            for (var party in count.From) break; // get the name of the donor party
            // create party if does not exist
            if (!transfers[con][party]) {
                transfers[con][party] = {total: count.From[party]}
            } else {
//                console.log("party Exists: " + party + " " + count.From[party]);
//                console.log("Current total: " + transfers[con][party].total)
                transfers[con][party].total += count.From[party]
//                console.log("New total: " + transfers[con][party].total)
            }
            $.each(count.To, function (recipient, amount) {
                if (!transfers[con][party][recipient]) { // if recipient does not exist
                    transfers[con][party][recipient] = amount; // set recipient value = amount
                } else {
                    transfers[con][party][recipient] += amount // add to existing recipient amount
                };
            });
        });
    });
    console.log("transfers", transfers);
    // convert constituency objects to arrays of individual transfers
    $.each(transfers, function (cname, constituency) { // loop thru constituencies
        console.log("--- " + cname + " ---");
        var data = [];
        $.each(constituency, function (donor, transfers) { // loop thru donor parties
            var totalTransfers = -transfers.total || 0;
            console.log(donor, totalTransfers);
            $.each(transfers, function (recipient, amount) { // loop thru recipient parties
//                console.log(transfers);
                if (recipient != "total") {
                    data.push({
                        donor: donor,
                        donor_short: parties[donor].Party_Abbreviation,
                        recipient: parties[recipient].Party_Abbreviation,
                        color: parties[donor].Hex_Col,
                        amount: amount / totalTransfers * 100
                    });
                }
            });
        });
        transferData[cname] = data;
    });

    console.log("transferData", transferData);

    function loadViz() {
        $.get("website/json/transferSpec.json", function (json) {
            var spec = JSON5.parse(json);
            var constituency = $("#constituencySelect :selected").text();
            console.log("CONST", constituency);
            var data = transferData[constituency];
            console.log("DATA", data)
            spec.data = [
                {
                    name: "transfers",
                    values: data
                },
                {
                  name: "ty",
                  source: "transfers",
                  transform: [
                    {
                        type: "aggregate",
                        groupby: "donor",
                        summarize: {donor: "distinct"}
                    },
                    {
                        type: "rank",
                        field: "donor"
                    },                    {
                        type: "formula",
                        field: "height",
                        expr: "datum.rank*40"
                    }
                  ]
                },
                {
                  name: "tx",
                  source: "transfers",
                  transform: [
                    {
                        type: "aggregate",
                        groupby: "recipient",
                        summarize: {recipient: "distinct"}
                    },
                    {
                        type: "rank",
                        field: "recipient"
                    },                    {
                        type: "formula",
                        field: "width",
                        expr: "datum.rank*40"
                    }
                  ]
                }
            ];
            vg.parse.spec(spec, function (chart) {
                var view = chart({
                        el: "#transfers"
                    })
                    .on("mouseover", function (event, item) {
                        if (item && item.datum) {
                            console.log(item);
                            $('#tooltip').show();
                            $('#tooltip').html(
                                Math.round(item.datum.amount * 10) /10 + "%"
                            );
                        } else {
                            $('#tooltip').hide();
                        }
                    })
                    .update();
            });
        }, "text");
    }

    $("#constituencySelect").change(loadViz);
    loadViz();
});

<commit_msg>Fixed typo in URL<commit_after>//function generateData() {

var constituencies = ["Belfast East", "Belfast North", "Belfast South", "Belfast West", "East Antrim", "East Londonderry", "Fermanagh and South Tyrone", "Foyle", "Lagan Valley", "Mid Ulster", "Newry and Armagh", "North Antrim", "North Down", "South Antrim", "South Down", "Strangford", "Upper Bann", "West Tyrone"];

var parties = {
  "Alliance Party": {
    "Party_Id": 19,
    "Party_Abbreviation": "APNI",
    "Hex_Col": "#ffe600"
  },
  "Democratic Unionist Party": {
    "Party_Id": 20,
    "Party_Abbreviation": "DUP",
    "Hex_Col": "#ff8000"
  },
  "Green Party": {
    "Party_Id": 111,
    "Party_Abbreviation": "GPNI",
    "Hex_Col": "#33ff33"
  },
  "Independent": {
    "Party_Id": 21,
    "Party_Abbreviation": "IND",
    "Hex_Col": "#669999"
  },
  "NI21": {
    "Party_Id": 918,
    "Party_Abbreviation": "NI21",
    "Hex_Col": "#581845"
  },
  "Sinn Fein": {
    "Party_Id": 24,
    "Party_Abbreviation": "SF",
    "Hex_Col": "#009900"
  },
  "Social Democratic and Labour Party": {
    "Party_Id": 23,
    "Party_Abbreviation": "SDLP",
    "Hex_Col": "#cc3300"
  },
  "Traditional Unionist Voice": {
    "Party_Id": 141,
    "Party_Abbreviation": "TUV",
    "Hex_Col": "#0000e6"
  },
  "UK Independence Party": {
    "Party_Id": 689,
    "Party_Abbreviation": "UKIP",
    "Hex_Col": "#cc00cc"
  },
  "Ulster Unionist Party": {
    "Party_Id": 26,
    "Party_Abbreviation": "UUP",
    "Hex_Col": "#62cae7"
  },
  "People Before Profit Alliance": {
    "Party_Id": 999,
    "Party_Abbreviation": "PBP",
    "Hex_Col": "#ff3399"
  },
  "Workers Party": {
    "Party_Id": 998,
    "Party_Abbreviation": "WP",
    "Hex_Col": "#ff0000"
  },
  "Procapitalism": {
    "Party_Id": 997,
    "Party_Abbreviation": "CAP",
    "Hex_Col": "#f633ff"
  },
  "Socialist Party": {
    "Party_Id": 996,
    "Party_Abbreviation": "SP",
    "Hex_Col": "#e60000"
  },
  "Socialist Party (NI)": {
    "Party_Id": 996,
    "Party_Abbreviation": "SP",
    "Hex_Col": "#e60000"
  },
  "British Nationalist Party": {
    "Party_Id": 995,
    "Party_Abbreviation": "BNP",
    "Hex_Col": "#b7bdff"
  },
  "Progressive Unionist Party": {
    "Party_Id": 994,
    "Party_Abbreviation": "PUP",
    "Hex_Col": "#1e0082"
  }
}

$.each(constituencies, function(i, constituency) {
    $("#constituencySelect").append("<option>" + constituency + "</option>")
})

var transferData = {};
$.getJSON("2011/NI/party-transfers.json", function (json) {
    console.log(json);
    var transfers = {};
    $.each(json, function (i, constituency) {
        var con = constituency.Constituency_Name;
        transfers[con] = {};
        $.each(constituency.Counts, function (j, count) { // loop thru each count
            for (var party in count.From) break; // get the name of the donor party
            // create party if does not exist
            if (!transfers[con][party]) {
                transfers[con][party] = {total: count.From[party]}
            } else {
//                console.log("party Exists: " + party + " " + count.From[party]);
//                console.log("Current total: " + transfers[con][party].total)
                transfers[con][party].total += count.From[party]
//                console.log("New total: " + transfers[con][party].total)
            }
            $.each(count.To, function (recipient, amount) {
                if (!transfers[con][party][recipient]) { // if recipient does not exist
                    transfers[con][party][recipient] = amount; // set recipient value = amount
                } else {
                    transfers[con][party][recipient] += amount // add to existing recipient amount
                };
            });
        });
    });
    console.log("transfers", transfers);
    // convert constituency objects to arrays of individual transfers
    $.each(transfers, function (cname, constituency) { // loop thru constituencies
        console.log("--- " + cname + " ---");
        var data = [];
        $.each(constituency, function (donor, transfers) { // loop thru donor parties
            var totalTransfers = -transfers.total || 0;
            console.log(donor, totalTransfers);
            $.each(transfers, function (recipient, amount) { // loop thru recipient parties
//                console.log(transfers);
                if (recipient != "total") {
                    data.push({
                        donor: donor,
                        donor_short: parties[donor].Party_Abbreviation,
                        recipient: parties[recipient].Party_Abbreviation,
                        color: parties[donor].Hex_Col,
                        amount: amount / totalTransfers * 100
                    });
                }
            });
        });
        transferData[cname] = data;
    });

    console.log("transferData", transferData);

    function loadViz() {
        $.get("website/json/transferSpec.json", function (json) {
            var spec = JSON5.parse(json);
            var constituency = $("#constituencySelect :selected").text();
            console.log("CONST", constituency);
            var data = transferData[constituency];
            console.log("DATA", data)
            spec.data = [
                {
                    name: "transfers",
                    values: data
                },
                {
                  name: "ty",
                  source: "transfers",
                  transform: [
                    {
                        type: "aggregate",
                        groupby: "donor",
                        summarize: {donor: "distinct"}
                    },
                    {
                        type: "rank",
                        field: "donor"
                    },                    {
                        type: "formula",
                        field: "height",
                        expr: "datum.rank*40"
                    }
                  ]
                },
                {
                  name: "tx",
                  source: "transfers",
                  transform: [
                    {
                        type: "aggregate",
                        groupby: "recipient",
                        summarize: {recipient: "distinct"}
                    },
                    {
                        type: "rank",
                        field: "recipient"
                    },                    {
                        type: "formula",
                        field: "width",
                        expr: "datum.rank*40"
                    }
                  ]
                }
            ];
            vg.parse.spec(spec, function (chart) {
                var view = chart({
                        el: "#transfers"
                    })
                    .on("mouseover", function (event, item) {
                        if (item && item.datum) {
                            console.log(item);
                            $('#tooltip').show();
                            $('#tooltip').html(
                                Math.round(item.datum.amount * 10) /10 + "%"
                            );
                        } else {
                            $('#tooltip').hide();
                        }
                    })
                    .update();
            });
        }, "text");
    }

    $("#constituencySelect").change(loadViz);
    loadViz();
});

<|endoftext|>


<commit_before>'use strict';

var cp = require('child_process');
var path = require('path');
var fs = require('fs');
var util = require('./util');

module.exports = function(input, output, cb) {
  var suffix = '-' + Date.now() + '-pngdefry';
  var pngdefryBinPath = util.getPngdefryBinPath();
  var newOutput = getNewOutput(output);
  var outputFilePath = getOutputFilePath(input, newOutput);

  convert(input, newOutput, outputFilePath, function(err) {
    if (err) {
      cb(err);
      return;
    }

    cb();
  });
};

function getNewOutput(output) {
  var arr = output.split(path.sep);
  arr.pop();
  return arr.join(path.sep);
}

function getOutputFilePath(input, newOutput) {
  var inputArr = input.split(path.sep);
  var originFileName = inputArr[inputArr.length - 1];
  var outputFilePath = path.join(newOutput, originFileName.replace(/\.png$/, suffix + '.png'));
  return outputFilePath;
}

function convert(input, newOutput, outputFilePath, cb) {
  var command = pngdefryBinPath + ' -s ' + suffix + ' -o ' + newOutput + ' ' + input;
  console.log(command);

  cp.exec(command, {}, function(error, stdout, stderr) {
    if (error) {
      return cb(error);
    }

    if (stdout.indexOf('not a PNG file') > -1) {
      return cb('convert fail, not a PNG file');
    }

    if (!util.fsExistsSync(outputFilePath)) {
      console.log('pngdefry is taking too much time to write the file on disk.');
    }

    console.log(stdout);
    try {
      fs.renameSync(outputFilePath, output);
      cb();
    } catch (e) {
      console.log(e);
      return false;
    }
  });
}
<commit_msg>refactor index.js<commit_after>'use strict';

var cp = require('child_process');
var path = require('path');
var fs = require('fs');
var util = require('./util');

module.exports = pngdefry;

function pngdefry(input, output, cb) {
  var suffix = '-' + Date.now() + '-pngdefry';
  var pngdefryBinPath = util.getPngdefryBinPath();
  var newOutput = getNewOutput(output);
  var outputFilePath = getOutputFilePath(input, newOutput);

  convert(input, newOutput, outputFilePath, function(err) {
    if (err) {
      cb(err);
      return;
    }

    cb();
  });
}

function getNewOutput(output) {
  var arr = output.split(path.sep);
  arr.pop();
  return arr.join(path.sep);
}

function getOutputFilePath(input, newOutput) {
  var inputArr = input.split(path.sep);
  var originFileName = inputArr[inputArr.length - 1];
  var outputFilePath = path.join(newOutput, originFileName.replace(/\.png$/, suffix + '.png'));
  return outputFilePath;
}

function convert(input, newOutput, outputFilePath, cb) {
  var command = pngdefryBinPath + ' -s ' + suffix + ' -o ' + newOutput + ' ' + input;
  console.log(command);

  cp.exec(command, {}, function(error, stdout, stderr) {
    if (error) {
      return cb(error);
    }

    if (stdout.indexOf('not a PNG file') > -1) {
      return cb('convert fail, not a PNG file');
    }

    if (!util.fsExistsSync(outputFilePath)) {
      console.log('pngdefry is taking too much time to write the file on disk.');
    }

    console.log(stdout);
    try {
      fs.renameSync(outputFilePath, output);
      cb();
    } catch (e) {
      console.log(e);
      return false;
    }
  });
}
<|endoftext|>


<commit_before>var initOpenUI5 = function (files, ui5Config) {
	if (ui5Config.useMockServer) {
		files.unshift({pattern: __dirname + '/mockserver.js', included: true, served: true, watched: false});
	}
	files.unshift({pattern: ui5Config.path, included: true, watched: false, served: false});
	files.unshift({pattern: __dirname + '/adapter.js', included: true, served: true, watched: false});
};

initOpenUI5.$inject = ['config.files', 'config.openui5'];

module.exports = {
	'framework:openui5': ['factory', initOpenUI5]
};
<commit_msg>Fix: Resolve path to local sap-ui-core.js correctly<commit_after>var path = require('path');

var ABS_URL = /^https?:\/\//;
var isUrlAbsolute = function(url) {
	return ABS_URL.test(url);
};

var initOpenUI5 = function (files, ui5Config, basePath) {
	if (ui5Config.useMockServer) {
		files.unshift({pattern: __dirname + '/mockserver.js', included: true, served: true, watched: false});
	}
	var ui5path;
	if (isUrlAbsolute(ui5Config.path)) {
		ui5path = ui5Config.path;
	} else {
		ui5path = path.resolve(basePath, ui5Config.path);
	}
	files.unshift({pattern: ui5path, included: true, watched: false, served: true});
	files.unshift({pattern: __dirname + '/adapter.js', included: true, served: true, watched: false});
};

initOpenUI5.$inject = ['config.files', 'config.openui5', 'config.basePath'];

module.exports = {
	'framework:openui5': ['factory', initOpenUI5]
};
<|endoftext|>


<commit_before>// Eloquent JavaScript
// Chapter 9 Exercises
// regular expressions
// boots
var a;

// Regexp Golf

/* car and cat */
a = /car|t/;
assert(a.test("car") &&
        a.test("cat"),
        "car and cat");

/* pop and prop */
a = /pr?op/;
assert(a.test("pop") &&
        a.test("prop"),
        "pop and prop");

/* ferret, ferry, and ferrari */
a = /ferr(et$|y$|ari$)/;
assert(a.test("ferret") &&
        a.test("ferry") &&
        a.test("ferrari"),
        "ferret, ferry, and ferrari");

/* Any word ending in ious */
a = /[a-z]*ious$/i;
assert(a.test("pious") &&
        a.test("envious") &&
        !a.test("fabulous") &&
        !a.test("iousity"),
        "any word ending in ious");

/* A whitespace character followed by a dot, comma, colon, or semicolon */
a = /\s[.,:;]/;
assert(a.test(" .") &&
        a.test("\r,") &&
        a.test("\n;") &&
        a.test("\t:") &&
        !a.test(".") &&
        !a.test(";"),
        "A whitespace character followed by a dot, comma, colon, or semicolon");

/* A word longer than six letters */
a = /[a-z]{6,}/i;
assert(a.test("hellotoday") &&
        a.test("mystery") &&
        !a.test("boat") &&
        !a.test("asdf;fsdf"),
        "A word longer than six letters");

/* A word without the letter e */
a = /^[a-df-zA-Z]*$/; // shorter, but felt like cheating: /^[^e]$/;
assert(a.test("brAnks") &&
        a.test("sin") &&
        !a.test("ebooks") &&
        !a.test("simple") &&
        !a.test("hello"),
        "A word without the letter e");

// Testers
function assert(predicate, message) {
    var toPrint = predicate ? "[PASSED] " : "☹ [FAILED] ";
    console.log(toPrint + message);
}<commit_msg>finishing up chapter 9 exercises. needs another go on the last problem<commit_after>// Eloquent JavaScript
// Chapter 9 Exercises
// regular expressions
// boots
"use strict";
var a, b, c, d;

// Regexp Golf

/* car and cat */
a = /car|t/;
assert(a.test("car") &&
        a.test("cat"),
        "car and cat");

/* pop and prop */
a = /pr?op/;
assert(a.test("pop") &&
        a.test("prop"),
        "pop and prop");

/* ferret, ferry, and ferrari */
a = /ferr(et$|y$|ari$)/;
assert(a.test("ferret") &&
        a.test("ferry") &&
        a.test("ferrari"),
        "ferret, ferry, and ferrari");

/* Any word ending in ious */
a = /[a-z]*ious$/i;
assert(a.test("pious") &&
        a.test("envious") &&
        !a.test("fabulous") &&
        !a.test("iousity"),
        "any word ending in ious");

/* A whitespace character followed by a dot, comma, colon, or semicolon */
a = /\s[.,:;]/;
assert(a.test(" .") &&
        a.test("\r,") &&
        a.test("\n;") &&
        a.test("\t:") &&
        !a.test(".") &&
        !a.test(";"),
        "A whitespace character followed by a dot, comma, colon, or semicolon");

/* A word longer than six letters */
a = /[a-z]{6,}/i;
assert(a.test("hellotoday") &&
        a.test("mystery") &&
        !a.test("boat") &&
        !a.test("asdf;fsdf"),
        "A word longer than six letters");

/* A word without the letter e */
a = /^[a-df-zA-Z]*$/; // shorter, but felt like cheating: /^[^e]$/;
assert(a.test("brAnks") &&
        a.test("sin") &&
        !a.test("ebooks") &&
        !a.test("simple") &&
        !a.test("hello"),
        "A word without the letter e");

// Quoting Style
a = /\'.*\'([\s.,;:]|$)/;
function replacer(match, suffix) {
    suffix = suffix || "";
    var endIndex = suffix ? -2 : -1;
    return "\"" + match.slice(1, endIndex) + "\"" + suffix;
}
b = "'Hello there!'";
c = '"Hello there!"';
assert(b.replace(a, replacer) === c, "");
b = "'Hello there!'  Ain't it strange.";
c = '"Hello there!"  Ain\'t it strange.';
assert(b.replace(a, replacer) === c, "");
b = "'Hello there!', Ain't it strange.";
c = '"Hello there!", Ain\'t it strange.';
assert(b.replace(a, replacer) === c, "");

// Numbers Again

a = /([\-\+](\d*\.?\d+)|(\d+\.?\d*)|(\d+e[\-\+]?\d+))/;

assert(a.test("1") &&
        a.test("+1") &&
        a.test("-1"), " 1, +1, -1 are numbers");
assert(a.test(".1") &&
        a.test("+.1") &&
        a.test("-.1"), " .1, +.1, -.1 are numbers");
assert(a.test("1.") &&
        a.test("+1.") &&
        a.test("-1."), " 1., +1., -1. are numbers");
assert(!a.test("."), ". is not a number");
assert(!a.test("1e"), "1e is not a number"); // this test is failing

// Testers
function assert(predicate, message) {
    var toPrint = predicate ? "[PASSED] " : "☹ [FAILED] ";
    console.log(toPrint + message);
}<|endoftext|>


<commit_before>define(['require', 'exports', './terminal', './env', './fs/index'], function(require, exports, terminal, env, fs) {
	var Terminal = exports.Terminal = terminal.Terminal;
	var Environment = exports.Environment = env.Environment;
	var Filesystem = exports.Filesystem = fs.Filesystem;

	exports.Machine = (function() {
		function Machine(name, fsData) {
			this.name = name;

			fsData = fsData || {
				root: {
					files: {
						bin: {
							files: {
								echo: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write(args.join(' '));
											context.data.write(args.join(' '));
										}
									}
								},
								pwd: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write(context.fs.pathTo(context.currentDir));
											context.data.write(context.fs.pathTo(context.currentDir));
										}
									}
								},
								ls: {
									executable: true,
									command: {
										exec: function(args, context) {
											var files = context.fs.listFiles(context.fs.getFile(args[0], context.currentDir));

											files.forEach(function(file) {
												context.view.stream.write(file.name);
											});
										}
									}
								},
								cd: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.currentDir = context.fs.getFile(args[0], context.currentDir) || context.currentDir;
										}
									}
								},
								alert: {
									executable: true,
									command: {
										exec: function(args, context) {
											if(args.length > 0 && args[0]) alert(args.join(' '));
											context.data.on('data', function(d) {
												alert(d);
											}).unpause();
										}
									}
								},
								sleep: {
									executable: true,
									command: {
										exec: function(args, context) {
											var time = /^[1-9]\d*$/.test(args[0]) ? parseInt(args[0]) : 1000;

											var startTime = Date.now();
											while((Date.now() - startTime) < time) {
											}

											return 0;
										}
									}
								},
								test: {
									executable: true,
									command: {
										exec: function(args, context) {
											console.log(arguments);
											context.data.on('data', function(d) {
												alert('someone sent me: ' + d);
											});
										}
									}
								},
								badExit: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write('I have an error');
											context.err.write('I have an error');
											return 1;
										}
									}
								}
							}
						}
					}
				}
			};

			this.fs = new Filesystem(fsData);
			this.env = new Environment({
				vars: {
					PATH: "/bin"
				}
			}, this.fs);
		}

		Machine.prototype.createTerminal = function createTerminal(view) {
			return new Terminal(this, view);
		};

		return Machine;
	})();
});
<commit_msg>Switch sleep to asynchronous<commit_after>define(['require', 'exports', './terminal', './env', './fs/index'], function(require, exports, terminal, env, fs) {
	var Terminal = exports.Terminal = terminal.Terminal;
	var Environment = exports.Environment = env.Environment;
	var Filesystem = exports.Filesystem = fs.Filesystem;

	exports.Machine = (function() {
		function Machine(name, fsData) {
			this.name = name;

			fsData = fsData || {
				root: {
					files: {
						bin: {
							files: {
								echo: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write(args.join(' '));
											context.data.write(args.join(' '));
										}
									}
								},
								pwd: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write(context.fs.pathTo(context.currentDir));
											context.data.write(context.fs.pathTo(context.currentDir));
										}
									}
								},
								ls: {
									executable: true,
									command: {
										exec: function(args, context) {
											var files = context.fs.listFiles(context.fs.getFile(args[0], context.currentDir));

											files.forEach(function(file) {
												context.view.stream.write(file.name);
											});
										}
									}
								},
								cd: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.currentDir = context.fs.getFile(args[0], context.currentDir) || context.currentDir;
										}
									}
								},
								alert: {
									executable: true,
									command: {
										exec: function(args, context) {
											if(args.length > 0 && args[0]) alert(args.join(' '));
											context.data.on('data', function(d) {
												alert(d);
											}).unpause();
										}
									}
								},
								sleep: {
									executable: true,
									command: {
										exec: function(args, context, cb) {
											var time = /^[1-9]\d*$/.test(args[0]) ? parseInt(args[0]) : 1000;
											setTimeout(cb, time);
										}
									}
								},
								test: {
									executable: true,
									command: {
										exec: function(args, context) {
											console.log(arguments);
											context.data.on('data', function(d) {
												alert('someone sent me: ' + d);
											});
										}
									}
								},
								badExit: {
									executable: true,
									command: {
										exec: function(args, context) {
											context.view.stream.write('I have an error');
											context.err.write('I have an error');
											return 1;
										}
									}
								}
							}
						}
					}
				}
			};

			this.fs = new Filesystem(fsData);
			this.env = new Environment({
				vars: {
					PATH: "/bin"
				}
			}, this.fs);
		}

		Machine.prototype.createTerminal = function createTerminal(view) {
			return new Terminal(this, view);
		};

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


<commit_before>import PropTypes from "prop-types";
import React, {Component} from 'react';
import Convert from 'ansi-to-html';

import { Col, Row, Card, CardBody, CardFooter, TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';
import classnames from 'classnames';
import {AsyncTypeahead} from 'react-bootstrap-typeahead'
import ReactJson from 'react-json-view';

import Logs from "./Logs";
import "./Console.css";
import {EMBARK_PROCESS_NAME} from '../constants';

const convert = new Convert();

class Console extends Component {
  constructor(props) {
    super(props);
    this.state = {value: '', isLoading: true, options: [], activeTab: EMBARK_PROCESS_NAME};
  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.postCommand(this.state.value);
    this.setState({value: ''});
    this.typeahead.getInstance().clear()
  }

  handleChange(value, cb) {
    this.setState({value: value}, cb);
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      });
      this.props.updateTab(tab);
    }
  }

  renderNav() {
    return (

        {this.props.processes.map((process) => (
          <NavItem key={process.name}>
            <NavLink
              className={classnames({ active: this.state.activeTab === process.name })}
              onClick={() => { this.toggle(process.name); }}
          >
            {process.name}
          </NavLink>
        </NavItem>
        ))}

    )
  }

  isJsonObject(item) {
    if (!item.result) return false;
    try {
      return typeof(JSON.parse(item.result)) === 'object'
    } catch(_err) {
      return false;
    }
  }

  renderTabs() {
    const {processLogs, processes} = this.props;

    return (
      <TabContent activeTab={this.state.activeTab}>
        {processes.map(process => (
          <TabPane key={process.name} tabId={process.name}>
            <Logs>
              {processLogs
                .filter((item) => item.name === process.name)
                .reverse()
                .map((item, i) => {

                  if (this.isJsonObject(item)) {
                    return(

                        <ReactJson src={JSON.parse(item.result)} theme="monokai" sortKeys={true} collapsed={1} />

                    )
                  }

                  return (

                  )
                })
              }
            </Logs>
          </TabPane>
        ))}
      </TabContent>
    );
  }

  render() {
    return (
      <Row>
        <Col>
          <Card>
            <CardBody className="console-container">
              {this.renderNav()}
              {this.renderTabs()}
            </CardBody>
            {this.props.isEmbark() && <CardFooter>
              <AsyncTypeahead
								autoFocus={true}
                emptyLabel={false}
                labelKey="value"
                multiple={false}
                maxResults={10}
                isLoading={this.state.isLoading}
                onInputChange={(text) => this.handleChange(text)}
                onChange={(text) => {
                  if (text && text[0]) {
                    this.handleChange(text[0].value)
                  }
                }}
                ref={(typeahead) => this.typeahead = typeahead}
                searchText={false}
                onKeyDown={(e) => {
                  if (e.keyCode === 13) {
                    this.handleChange(e.target.value, () => {
                      this.handleSubmit(e)
                    })
                  }
                }}
                onSearch={(value) => {
									this.props.postCommandSuggestions(value)
                }}
                filterBy={['value', 'description']}
                maxHeight="200px"
                placeholder="Type a command (e.g help)"
                options={this.props.commandSuggestions}
                renderMenuItemChildren={(option) => (

                    {option.value}

                      {option.command_type} - {option.description}

                )}
              />
            </CardFooter>}
          </Card>
        </Col>
      </Row>
    );
  }
}

Console.propTypes = {
  postCommand: PropTypes.func,
  postCommandSuggestions: PropTypes.func,
  isEmbark: PropTypes.func,
  processes: PropTypes.arrayOf(PropTypes.object).isRequired,
  commandSuggestions: PropTypes.arrayOf(PropTypes.object),
  processLogs: PropTypes.arrayOf(PropTypes.object).isRequired,
  updateTab: PropTypes.func
};

export default Console;
<commit_msg>Remove extra spaces between logs<commit_after>import PropTypes from "prop-types";
import React, {Component} from 'react';
import Convert from 'ansi-to-html';

import { Col, Row, Card, CardBody, CardFooter, TabContent, TabPane, Nav, NavItem, NavLink } from 'reactstrap';
import classnames from 'classnames';
import {AsyncTypeahead} from 'react-bootstrap-typeahead'
import ReactJson from 'react-json-view';

import Logs from "./Logs";
import "./Console.css";
import {EMBARK_PROCESS_NAME} from '../constants';

const convert = new Convert();

class Console extends Component {
  constructor(props) {
    super(props);
    this.state = {value: '', isLoading: true, options: [], activeTab: EMBARK_PROCESS_NAME};
  }

  handleSubmit(event) {
    event.preventDefault();
    this.props.postCommand(this.state.value);
    this.setState({value: ''});
    this.typeahead.getInstance().clear()
  }

  handleChange(value, cb) {
    this.setState({value: value}, cb);
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      });
      this.props.updateTab(tab);
    }
  }

  renderNav() {
    return (

        {this.props.processes.map((process) => (
          <NavItem key={process.name}>
            <NavLink
              className={classnames({ active: this.state.activeTab === process.name })}
              onClick={() => { this.toggle(process.name); }}
          >
            {process.name}
          </NavLink>
        </NavItem>
        ))}

    )
  }

  isJsonObject(item) {
    if (!item.result) return false;
    try {
      return typeof(JSON.parse(item.result)) === 'object'
    } catch(_err) {
      return false;
    }
  }

  logClassName(item) {
    return classnames('m-0', {
      'text-info': item.logLevel === 'debug',
      'text-danger': item.logLevel === 'error',
      'text-warning': item.logLevel === 'warning'
    });
  }

  renderTabs() {
    const {processLogs, processes} = this.props;

    return (
      <TabContent activeTab={this.state.activeTab}>
        {processes.map(process => (
          <TabPane key={process.name} tabId={process.name}>
            <Logs>
              {processLogs
                .filter((item) => item.name === process.name)
                .reverse()
                .map((item, i) => {

                  if (this.isJsonObject(item)) {
                    return(

                        <ReactJson src={JSON.parse(item.result)} theme="monokai" sortKeys={true} collapsed={1} />

                    )
                  }

                  return (

                  )
                })
              }
            </Logs>
          </TabPane>
        ))}
      </TabContent>
    );
  }

  render() {
    return (
      <Row>
        <Col>
          <Card>
            <CardBody className="console-container">
              {this.renderNav()}
              {this.renderTabs()}
            </CardBody>
            {this.props.isEmbark() && <CardFooter>
              <AsyncTypeahead
								autoFocus={true}
                emptyLabel={false}
                labelKey="value"
                multiple={false}
                maxResults={10}
                isLoading={this.state.isLoading}
                onInputChange={(text) => this.handleChange(text)}
                onChange={(text) => {
                  if (text && text[0]) {
                    this.handleChange(text[0].value)
                  }
                }}
                ref={(typeahead) => this.typeahead = typeahead}
                searchText={false}
                onKeyDown={(e) => {
                  if (e.keyCode === 13) {
                    this.handleChange(e.target.value, () => {
                      this.handleSubmit(e)
                    })
                  }
                }}
                onSearch={(value) => {
									this.props.postCommandSuggestions(value)
                }}
                filterBy={['value', 'description']}
                maxHeight="200px"
                placeholder="Type a command (e.g help)"
                options={this.props.commandSuggestions}
                renderMenuItemChildren={(option) => (

                    {option.value}

                      {option.command_type} - {option.description}

                )}
              />
            </CardFooter>}
          </Card>
        </Col>
      </Row>
    );
  }
}

Console.propTypes = {
  postCommand: PropTypes.func,
  postCommandSuggestions: PropTypes.func,
  isEmbark: PropTypes.func,
  processes: PropTypes.arrayOf(PropTypes.object).isRequired,
  commandSuggestions: PropTypes.arrayOf(PropTypes.object),
  processLogs: PropTypes.arrayOf(PropTypes.object).isRequired,
  updateTab: PropTypes.func
};

export default Console;
<|endoftext|>


<commit_before>const defaultOptions = {
    relativeUrls: true,
    compress: false
};

export default (userOptions) => (input) => {
    return function less(log) {
        const Less = require('less');

        return Promise.all(
            input.map(file => {
                const options = {
                    ...defaultOptions,
                    ...userOptions,
                    filename: file.path
                };

                return Less.render(file.data, options).then(result => {
                    log(file.path);

                    return {
                        path: file.path,
                        data: result.css,
                        map: typeof result.map === 'undefined' ? null : result.map
                    };
                });
            })
        );
    };
};
<commit_msg>:heavy_check_mark: fix source maps absoluted paths with `sourceMapBasepath`<commit_after>const defaultOptions = {
    relativeUrls: true,
    compress: false
};

export default (userOptions) => (input) => {
    return function less(log) {
        const path = require('path');
        const Less = require('less');

        return Promise.all(
            input.map(file => {
                const options = {
                    ...defaultOptions,
                    ...userOptions,
                    filename: file.path
                };

                if (options.sourceMap) {
                    options.sourceMap = {
                        outputSourceFiles: true,
                        ...options.sourceMap,
                        sourceMapBasepath: path.dirname(file.path)
                    };
                }

                return Less.render(file.data, options).then(result => {
                    let sourceMap = null;

                    log(file.path);

                    if (typeof result.map !== 'undefined') {
                        sourceMap = JSON.parse(result.map);
                    }

                    return {
                        path: file.path,
                        data: result.css,
                        map: sourceMap
                    };
                });
            })
        );
    };
};
<|endoftext|>