<commit_before>'use babel'

import helpers from '../lib/helpers'

function findStatusBar () {
  const footerPanels = atom.workspace.getFooterPanels()
  if (footerPanels.length > 0) return footerPanels[0].getItem()
  return atom.workspace.getBottomPanels()[0].getItem()
}

describe('line ending selector', () => {
  let lineEndingTile

  beforeEach(() => {
    waitsForPromise(() => {
      return atom.packages.activatePackage('status-bar')
    })

    waitsForPromise(() => {
      return atom.packages.activatePackage('line-ending-selector')
    })

    waits(1)

    runs(() => {
      let statusBar = findStatusBar()

      lineEndingTile = statusBar.getRightTiles()[0].getItem()
      expect(lineEndingTile.className).toMatch(/line-ending-tile/)
      expect(lineEndingTile.textContent).toBe('')
    })
  })

  describe('Commands', () => {
    let editor, editorElement

    beforeEach(() => {
      waitsForPromise(() => {
        return atom.workspace.open('mixed-endings.md').then((e) => {
          editor = e
          editorElement = atom.views.getView(editor)
        })
      })
    })

    describe('When "line-ending-selector:convert-to-LF" is run', () => {
      it('converts the file to LF line endings', () => {
        atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
        expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n')
      })
    })

    describe('When "line-ending-selector:convert-to-LF" is run', () => {
      it('converts the file to CRLF line endings', () => {
        atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-CRLF')
        expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n')
      })
    })
  })

  describe('Status bar tile', () => {
    describe('when an empty file is opened', () => {
      it('uses the default line endings for the platform', () => {
        waitsForPromise(() => {
          spyOn(helpers, 'getProcessPlatform').andReturn('win32')

          return atom.workspace.open('').then((editor) => {
            expect(lineEndingTile.textContent).toBe('CRLF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
          })
        })

        waitsForPromise(() => {
          helpers.getProcessPlatform.andReturn('darwin')

          return atom.workspace.open('').then((editor) => {
            expect(lineEndingTile.textContent).toBe('LF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
          })
        })
      })

      describe('when the "defaultLineEnding" setting is set to "LF"', () => {
        beforeEach(() => {
          atom.config.set('line-ending-selector.defaultLineEnding', 'LF')
        })

        it('uses LF line endings, regardless of the platform', () => {
          waitsForPromise(() => {
            spyOn(helpers, 'getProcessPlatform').andReturn('win32')

            return atom.workspace.open('').then((editor) => {
              expect(lineEndingTile.textContent).toBe('LF')
              expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
            })
          })
        })
      })

      describe('when the "defaultLineEnding" setting is set to "CRLF"', () => {
        beforeEach(() => {
          atom.config.set('line-ending-selector.defaultLineEnding', 'CRLF')
        })

        it('uses CRLF line endings, regardless of the platform', () => {
          waitsForPromise(() => {
            return atom.workspace.open('').then((editor) => {
              expect(lineEndingTile.textContent).toBe('CRLF')
              expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
            })
          })
        })
      })
    })

    describe('when a file is opened that contains only CRLF line endings', () => {
      it('displays "CRLF" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('windows-endings.md').then(() => {
            expect(lineEndingTile.textContent).toBe('CRLF')
          })
        })
      })
    })

    describe('when a file is opened that contains only LF line endings', () => {
      it('displays "LF" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then((editor) => {
            expect(lineEndingTile.textContent).toBe('LF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe(null)
          })
        })
      })
    })

    describe('when a file is opened that contains only legacy CR line endings', () => {
      it('displays "CR" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('old-endings.md').then((editor) => {
            expect(lineEndingTile.textContent).toBe('CR')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe(null)
          })
        })
      })
    })

    describe('when a file is opened that contains mixed line endings', () => {
      it('displays "Mixed" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('mixed-endings.md').then(() => {
            expect(lineEndingTile.textContent).toBe('Mixed')
          })
        })
      })
    })

    describe('clicking the tile', () => {
      let lineEndingModal, lineEndingSelector

      beforeEach(() => {
        jasmine.attachToDOM(atom.views.getView(atom.workspace))

        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md')
        })

        runs(() => {
          lineEndingTile.dispatchEvent(new MouseEvent('click', {}))
          lineEndingModal = atom.workspace.getModalPanels()[0]
          lineEndingSelector = lineEndingModal.getItem()
        })
      })

      it('opens the line ending selector modal', () => {
        expect(lineEndingModal.isVisible()).toBe(true)
        expect(lineEndingSelector).toHaveFocus()
        let listItems = lineEndingSelector.list.find('li')
        expect(listItems[0].textContent).toBe('LF')
        expect(listItems[1].textContent).toBe('CRLF')

        lineEndingSelector.filterEditorView.getModel().setText('CR')

        advanceClock(100)

        atom.commands.dispatch(lineEndingSelector[0], 'core:confirm')
        expect(lineEndingModal.isVisible()).toBe(false)

        advanceClock(1)

        expect(lineEndingTile.textContent).toBe('CRLF')
        let editor = atom.workspace.getActiveTextEditor()
        expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nUnix\r\n')
        expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
      })

      describe('when modal is exited', () => {
        it('leaves the tile selection as-is', () => {
          atom.commands.dispatch(lineEndingSelector[0], 'core:cancel')
          expect(lineEndingTile.textContent).toBe('LF')
        })
      })
    })

    describe('clicking out of a text editor', () => {
      it('displays no line ending in the status bar', () => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then(() => {
            atom.workspace.getActivePane().destroy()
            expect(lineEndingTile.textContent).toBe('')
          })
        })
      })
    })

    describe('when the buffer\'s line endings change', () => {
      let editor, editorElement

      beforeEach(() => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then((e) => {
            editor = e
            editorElement = atom.views.getView(editor)
          })
        })
      })

      it('updates the line ending text in the tile', () => {
        jasmine.useRealClock()

        let tileText = lineEndingTile.textContent
        let tileUpdateCount = 0
        Object.defineProperty(lineEndingTile, 'textContent', {
          get () {
            return tileText
          },

          set (text) {
            tileUpdateCount++
            tileText = text
          }
        })

        expect(lineEndingTile.textContent).toBe('LF')

        runs(() => {
          editor.setTextInBufferRange([[0, 0], [0, 0]], '... ')
          editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', {normalizeLineEndings: false})
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(1)
          expect(lineEndingTile.textContent).toBe('Mixed')
        })

        runs(() => {
          atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-CRLF')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(2)
          expect(lineEndingTile.textContent).toBe('CRLF')
        })

        runs(() => {
          atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(3)
          expect(lineEndingTile.textContent).toBe('LF')
        })

        runs(() => {
          editor.setTextInBufferRange([[0, 0], [0, 0]], '\n')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(3)
        })
      })
    })
  })
})
<commit_msg>I guess this wasn't a thing yet.<commit_after>'use babel'

import helpers from '../lib/helpers'

function findStatusBar () {
  if (typeof atom.workspace.getFooterPanels === 'function') {
    return atom.workspace.getFooterPanels()[0].getItem()
  }

  return atom.workspace.getBottomPanels()[0].getItem()
}

describe('line ending selector', () => {
  let lineEndingTile

  beforeEach(() => {
    waitsForPromise(() => {
      return atom.packages.activatePackage('status-bar')
    })

    waitsForPromise(() => {
      return atom.packages.activatePackage('line-ending-selector')
    })

    waits(1)

    runs(() => {
      let statusBar = findStatusBar()

      lineEndingTile = statusBar.getRightTiles()[0].getItem()
      expect(lineEndingTile.className).toMatch(/line-ending-tile/)
      expect(lineEndingTile.textContent).toBe('')
    })
  })

  describe('Commands', () => {
    let editor, editorElement

    beforeEach(() => {
      waitsForPromise(() => {
        return atom.workspace.open('mixed-endings.md').then((e) => {
          editor = e
          editorElement = atom.views.getView(editor)
        })
      })
    })

    describe('When "line-ending-selector:convert-to-LF" is run', () => {
      it('converts the file to LF line endings', () => {
        atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
        expect(editor.getText()).toBe('Hello\nGoodbye\nMixed\n')
      })
    })

    describe('When "line-ending-selector:convert-to-LF" is run', () => {
      it('converts the file to CRLF line endings', () => {
        atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-CRLF')
        expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nMixed\r\n')
      })
    })
  })

  describe('Status bar tile', () => {
    describe('when an empty file is opened', () => {
      it('uses the default line endings for the platform', () => {
        waitsForPromise(() => {
          spyOn(helpers, 'getProcessPlatform').andReturn('win32')

          return atom.workspace.open('').then((editor) => {
            expect(lineEndingTile.textContent).toBe('CRLF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
          })
        })

        waitsForPromise(() => {
          helpers.getProcessPlatform.andReturn('darwin')

          return atom.workspace.open('').then((editor) => {
            expect(lineEndingTile.textContent).toBe('LF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
          })
        })
      })

      describe('when the "defaultLineEnding" setting is set to "LF"', () => {
        beforeEach(() => {
          atom.config.set('line-ending-selector.defaultLineEnding', 'LF')
        })

        it('uses LF line endings, regardless of the platform', () => {
          waitsForPromise(() => {
            spyOn(helpers, 'getProcessPlatform').andReturn('win32')

            return atom.workspace.open('').then((editor) => {
              expect(lineEndingTile.textContent).toBe('LF')
              expect(editor.getBuffer().getPreferredLineEnding()).toBe('\n')
            })
          })
        })
      })

      describe('when the "defaultLineEnding" setting is set to "CRLF"', () => {
        beforeEach(() => {
          atom.config.set('line-ending-selector.defaultLineEnding', 'CRLF')
        })

        it('uses CRLF line endings, regardless of the platform', () => {
          waitsForPromise(() => {
            return atom.workspace.open('').then((editor) => {
              expect(lineEndingTile.textContent).toBe('CRLF')
              expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
            })
          })
        })
      })
    })

    describe('when a file is opened that contains only CRLF line endings', () => {
      it('displays "CRLF" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('windows-endings.md').then(() => {
            expect(lineEndingTile.textContent).toBe('CRLF')
          })
        })
      })
    })

    describe('when a file is opened that contains only LF line endings', () => {
      it('displays "LF" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then((editor) => {
            expect(lineEndingTile.textContent).toBe('LF')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe(null)
          })
        })
      })
    })

    describe('when a file is opened that contains only legacy CR line endings', () => {
      it('displays "CR" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('old-endings.md').then((editor) => {
            expect(lineEndingTile.textContent).toBe('CR')
            expect(editor.getBuffer().getPreferredLineEnding()).toBe(null)
          })
        })
      })
    })

    describe('when a file is opened that contains mixed line endings', () => {
      it('displays "Mixed" as the line ending', () => {
        waitsForPromise(() => {
          return atom.workspace.open('mixed-endings.md').then(() => {
            expect(lineEndingTile.textContent).toBe('Mixed')
          })
        })
      })
    })

    describe('clicking the tile', () => {
      let lineEndingModal, lineEndingSelector

      beforeEach(() => {
        jasmine.attachToDOM(atom.views.getView(atom.workspace))

        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md')
        })

        runs(() => {
          lineEndingTile.dispatchEvent(new MouseEvent('click', {}))
          lineEndingModal = atom.workspace.getModalPanels()[0]
          lineEndingSelector = lineEndingModal.getItem()
        })
      })

      it('opens the line ending selector modal', () => {
        expect(lineEndingModal.isVisible()).toBe(true)
        expect(lineEndingSelector).toHaveFocus()
        let listItems = lineEndingSelector.list.find('li')
        expect(listItems[0].textContent).toBe('LF')
        expect(listItems[1].textContent).toBe('CRLF')

        lineEndingSelector.filterEditorView.getModel().setText('CR')

        advanceClock(100)

        atom.commands.dispatch(lineEndingSelector[0], 'core:confirm')
        expect(lineEndingModal.isVisible()).toBe(false)

        advanceClock(1)

        expect(lineEndingTile.textContent).toBe('CRLF')
        let editor = atom.workspace.getActiveTextEditor()
        expect(editor.getText()).toBe('Hello\r\nGoodbye\r\nUnix\r\n')
        expect(editor.getBuffer().getPreferredLineEnding()).toBe('\r\n')
      })

      describe('when modal is exited', () => {
        it('leaves the tile selection as-is', () => {
          atom.commands.dispatch(lineEndingSelector[0], 'core:cancel')
          expect(lineEndingTile.textContent).toBe('LF')
        })
      })
    })

    describe('clicking out of a text editor', () => {
      it('displays no line ending in the status bar', () => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then(() => {
            atom.workspace.getActivePane().destroy()
            expect(lineEndingTile.textContent).toBe('')
          })
        })
      })
    })

    describe('when the buffer\'s line endings change', () => {
      let editor, editorElement

      beforeEach(() => {
        waitsForPromise(() => {
          return atom.workspace.open('unix-endings.md').then((e) => {
            editor = e
            editorElement = atom.views.getView(editor)
          })
        })
      })

      it('updates the line ending text in the tile', () => {
        jasmine.useRealClock()

        let tileText = lineEndingTile.textContent
        let tileUpdateCount = 0
        Object.defineProperty(lineEndingTile, 'textContent', {
          get () {
            return tileText
          },

          set (text) {
            tileUpdateCount++
            tileText = text
          }
        })

        expect(lineEndingTile.textContent).toBe('LF')

        runs(() => {
          editor.setTextInBufferRange([[0, 0], [0, 0]], '... ')
          editor.setTextInBufferRange([[0, Infinity], [1, 0]], '\r\n', {normalizeLineEndings: false})
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(1)
          expect(lineEndingTile.textContent).toBe('Mixed')
        })

        runs(() => {
          atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-CRLF')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(2)
          expect(lineEndingTile.textContent).toBe('CRLF')
        })

        runs(() => {
          atom.commands.dispatch(editorElement, 'line-ending-selector:convert-to-LF')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(3)
          expect(lineEndingTile.textContent).toBe('LF')
        })

        runs(() => {
          editor.setTextInBufferRange([[0, 0], [0, 0]], '\n')
        })

        waits(1)

        runs(() => {
          expect(tileUpdateCount).toBe(3)
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import {
	WORKING_TITLE_API,
	CLEAR_WORKING_TITLE,
	WORKING_STRUCTURED_API,
	CLEAR_WORKING_STRUCTURED,
} from '../actions/metadata';
import { SAVE_RECORD_API, DELETE_RECORD_API } from '../actions/save';
import { CLEAR_DATA } from '../actions/dev';
import { UNAUTHORIZED, LOGOUT_API } from '../actions/auth';
import { SET_FIELD_VALUE } from '../actions/record';

function removeRecordMeta(state, tableId, recordId, lang) {
	if (!state[tableId] || !state[tableId][recordId]) return state;

	if (lang && !state[tableId][recordId][lang]) return state;
	const newState = {
		...state,
		[tableId]: {
			...state[tableId],
		},
	};
	if (lang) {
		newState[tableId][recordId] = {
			...newState[tableId][recordId],
		};
		delete newState[tableId][recordId][lang];
	} else {
		delete newState[tableId][recordId];
	}
	return newState;

}

function removeMetas(state, records) {
	if (!records) return state;
	return records.reduce((modifiedState, record) => {
		const { tableId, recordId } = record;
		return removeRecordMeta(modifiedState, tableId, recordId);
	}, state);
}

function receiveMetas(state, actionData) {
	const { tableId, recordId, data, lang } = actionData;
	const newState = {
		...state,
		[tableId]: {
			...state[tableId],
			[recordId]: {
				...(state[tableId] && state[tableId][recordId]),
				[lang]: data,
			},
		},
	};
	return newState;
}

const titles = (state = {}, action) => {
	switch (action.type) {
	case CLEAR_DATA:
	case UNAUTHORIZED:
	case LOGOUT_API.SUCCESS:
	case LOGOUT_API.REQUEST:
		return {};
	case WORKING_TITLE_API.SUCCESS:
		return receiveMetas(state, action.data);
	case SAVE_RECORD_API.SUCCESS:
	case DELETE_RECORD_API.SUCCESS:
		return removeMetas(state, action.data.records);
	case SET_FIELD_VALUE:
	case CLEAR_WORKING_TITLE: {
		const { tableId, recordId, lang } = action.data;
		return removeRecordMeta(state, tableId, recordId, lang);
	}
	default:
		return state;
	}
};

// structured data field
const structured = (state = {}, action) => {
	switch (action.type) {
	case CLEAR_DATA:
	case UNAUTHORIZED:
	case LOGOUT_API.SUCCESS:
	case LOGOUT_API.REQUEST:
		return {};
	case WORKING_STRUCTURED_API.SUCCESS:
		return receiveMetas(state, action.data);
	case SAVE_RECORD_API.SUCCESS:
	case DELETE_RECORD_API.SUCCESS:
		return removeMetas(state, action.data.records);
	case SET_FIELD_VALUE:
	case CLEAR_WORKING_STRUCTURED: {
		const { tableId, recordId, lang } = action.data;
		return removeRecordMeta(state, tableId, recordId, lang);
	}
	default:
		return state;
	}
};

export default combineReducers({
	titles,
	structured,
});
<commit_msg>fix tabs<commit_after>import { combineReducers } from 'redux';
import {
	WORKING_TITLE_API,
	CLEAR_WORKING_TITLE,
	WORKING_STRUCTURED_API,
	CLEAR_WORKING_STRUCTURED,
} from '../actions/metadata';
import { SAVE_RECORD_API, DELETE_RECORD_API } from '../actions/save';
import { CLEAR_DATA } from '../actions/dev';
import { UNAUTHORIZED, LOGOUT_API } from '../actions/auth';
import { SET_FIELD_VALUE } from '../actions/record';

function removeRecordMeta(state, tableId, recordId, lang) {
	if (!state[tableId] || !state[tableId][recordId]) return state;

	if (lang && !state[tableId][recordId][lang]) return state;
	const newState = {
		...state,
		[tableId]: {
			...state[tableId],
		},
	};
	if (lang) {
		newState[tableId][recordId] = {
			...newState[tableId][recordId],
		};
		delete newState[tableId][recordId][lang];
	} else {
		delete newState[tableId][recordId];
	}
	return newState;

}

function removeMetas(state, records) {
	if (!records) return state;
	return records.reduce((modifiedState, record) => {
		const { tableId, recordId } = record;
		return removeRecordMeta(modifiedState, tableId, recordId);
	}, state);
}

function receiveMetas(state, actionData) {
	const { tableId, recordId, data, lang } = actionData;
	const newState = {
		...state,
		[tableId]: {
			...state[tableId],
			[recordId]: {
				...(state[tableId] && state[tableId][recordId]),
				[lang]: data,
			},
		},
	};
	return newState;
}

const titles = (state = {}, action) => {
	switch (action.type) {
		case CLEAR_DATA:
		case UNAUTHORIZED:
		case LOGOUT_API.SUCCESS:
		case LOGOUT_API.REQUEST:
			return {};
		case WORKING_TITLE_API.SUCCESS:
			return receiveMetas(state, action.data);
		case SAVE_RECORD_API.SUCCESS:
		case DELETE_RECORD_API.SUCCESS:
			return removeMetas(state, action.data.records);
		case SET_FIELD_VALUE:
		case CLEAR_WORKING_TITLE: {
			const { tableId, recordId, lang } = action.data;
			return removeRecordMeta(state, tableId, recordId, lang);
		}
		default:
			return state;
	}
};

// structured data field
const structured = (state = {}, action) => {
	switch (action.type) {
		case CLEAR_DATA:
		case UNAUTHORIZED:
		case LOGOUT_API.SUCCESS:
		case LOGOUT_API.REQUEST:
			return {};
		case WORKING_STRUCTURED_API.SUCCESS:
			return receiveMetas(state, action.data);
		case SAVE_RECORD_API.SUCCESS:
		case DELETE_RECORD_API.SUCCESS:
			return removeMetas(state, action.data.records);
		case SET_FIELD_VALUE:
		case CLEAR_WORKING_STRUCTURED: {
			const { tableId, recordId, lang } = action.data;
			return removeRecordMeta(state, tableId, recordId, lang);
		}
		default:
			return state;
	}
};

export default combineReducers({
	titles,
	structured,
});
<|endoftext|>


<commit_before>module.exports = {
  config: {
    // default font size in pixels for all tabs
    fontSize: 16,

    // font family with optional fallbacks
    fontFamily: 'Hack, Menlo, "DejaVu Sans Mono", "Lucida Console", monospace',

    // terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)
    cursorColor: 'rgba(248,28,229,0.8)',

    // `BEAM` for |, `UNDERLINE` for _, `BLOCK` for █
    cursorShape: 'BEAM',

    // color of the text
    foregroundColor: '#fff',

    // terminal background color
    backgroundColor: '#000',

    // border color (window, tabs)
    borderColor: '#333',

    // custom css to embed in the main window
    css: '',

    // custom css to embed in the terminal window
    termCSS: ``,

    // custom padding (css format, i.e.: `top right bottom left`)
    padding: '17px',

    // the full list. if you're going to provide the full color palette,
    // including the 6 x 6 color cubes and the grayscale map, just provide
    // an array here instead of a color map object
    colors: {
      black: '#000000',
      red: '#ff0000',
      green: '#33ff00',
      yellow: '#ffff00',
      blue: '#0066ff',
      magenta: '#cc00ff',
      cyan: '#00ffff',
      white: '#d0d0d0',
      lightBlack: '#808080',
      lightRed: '#ff0000',
      lightGreen: '#33ff00',
      lightYellow: '#ffff00',
      lightBlue: '#0066ff',
      lightMagenta: '#cc00ff',
      lightCyan: '#00ffff',
      lightWhite: '#ffffff'
    },

    // the shell to run when spawning a new session (i.e. /usr/local/bin/fish)
    // if left empty, your system's login shell will be used by default
    shell: '',

    // for setting shell arguments (i.e. for using interactive shellArgs: ['-i'])
    // by default ['--login'] will be used
    shellArgs: ['--login'],

    // for environment variables
    env: {},

    // set to false for no bell
    bell: 'SOUND',

    // if true, selected text will automatically be copied to the clipboard
    copyOnSelect: false,

    // URL to custom bell
    // bellSoundURL: 'http://example.com/bell.mp3',

    // for advanced config flags please refer to https://hyper.is/#cfg
    hyperStatusLine: {
        fontSize: 13,
        fontFamily: 'Fira Code'
      }
  },

  // a list of plugins to fetch and install from npm
  // format: [@org/]project[#version]
  // examples:
  //   `hyperpower`
  //   `@company/project`
  //   `project#1.0.1`
  plugins: [
    'hyper-statusline',
    'hyperterm-close-on-left',
    'hyperlinks',
    'hyperterm-paste',
    // 'hyper-dark-nova-theme',
    'hyperterm-atom-dark'
  ],

  // in development, you can create a directory under
  // `~/.hyper_plugins/local/` and include it here
  // to load it and avoid it being `npm install`ed
  localPlugins: []
};
<commit_msg>Update hyper settings.<commit_after>module.exports = {
  config: {
    // default font size in pixels for all tabs
    fontSize: 16,

    // font family with optional fallbacks
    fontFamily: 'Hack, Menlo, "DejaVu Sans Mono", "Lucida Console", monospace',

    // terminal cursor background color and opacity (hex, rgb, hsl, hsv, hwb or cmyk)
    cursorColor: 'rgba(248,28,229,0.8)',

    // `BEAM` for |, `UNDERLINE` for _, `BLOCK` for █
    cursorShape: 'BEAM',

    // color of the text
    foregroundColor: '#fff',

    // terminal background color
    backgroundColor: '#000',

    // border color (window, tabs)
    borderColor: '#333',

    // custom css to embed in the main window
    css: '',

    // custom css to embed in the terminal window
    termCSS: ``,

    // custom padding (css format, i.e.: `top right bottom left`)
    padding: '17px',

    // the full list. if you're going to provide the full color palette,
    // including the 6 x 6 color cubes and the grayscale map, just provide
    // an array here instead of a color map object
    colors: {
      black: '#000000',
      red: '#ff0000',
      green: '#33ff00',
      yellow: '#ffff00',
      blue: '#0066ff',
      magenta: '#cc00ff',
      cyan: '#00ffff',
      white: '#d0d0d0',
      lightBlack: '#808080',
      lightRed: '#ff0000',
      lightGreen: '#33ff00',
      lightYellow: '#ffff00',
      lightBlue: '#0066ff',
      lightMagenta: '#cc00ff',
      lightCyan: '#00ffff',
      lightWhite: '#ffffff',
    },

    // the shell to run when spawning a new session (i.e. /usr/local/bin/fish)
    // if left empty, your system's login shell will be used by default
    shell: '',

    // for setting shell arguments (i.e. for using interactive shellArgs: ['-i'])
    // by default ['--login'] will be used
    shellArgs: ['--login'],

    // for environment variables
    env: {},

    // set to false for no bell
    bell: 'SOUND',

    // if true, selected text will automatically be copied to the clipboard
    copyOnSelect: false,

    // URL to custom bell
    // bellSoundURL: 'http://example.com/bell.mp3',

    // for advanced config flags please refer to https://hyper.is/#cfg
  },

  // a list of plugins to fetch and install from npm
  // format: [@org/]project[#version]
  // examples:
  //   `hyperpower`
  //   `@company/project`
  //   `project#1.0.1`
  plugins: [
    'hyperterm-close-on-left',
    'hyperlinks',
    'hyperterm-paste',
    // 'hyper-dark-nova-theme',
    'hyperterm-atom-dark',
  ],

  // in development, you can create a directory under
  // `~/.hyper_plugins/local/` and include it here
  // to load it and avoid it being `npm install`ed
  localPlugins: [],
};
<|endoftext|>


<commit_before>/* Magic Mirror Test calendar calendar
 *
 * This configuration is a wrong authentication
 *
 * By Rodrigo Ramírez Norambuena https://rodrigoramirez.com
 * MIT Licensed.
 */

var config = {
	port: 8080,
	ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],

	language: "en",
	timeFormat: 12,
	units: "metric",
	electronOptions: {
		webPreferences: {
			nodeIntegration: true,
		},
	},

	modules: [
		{
			module: "calendar",
			position: "bottom_bar",
			config: {
				calendars: [
					{
						maximumNumberOfDays: 10000,
						url: "http://localhost:8020/tests/configs/data/calendar_test.ics",
						auth: {
							user: "MagicMirror",
							pass: "StairwayToHeaven",
							method: "basic"
						}
					}
				]
			}
		}
	]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}
<commit_msg>Update fail-basic-auth.js<commit_after>/* Smart Mirror Test calendar calendar
 *
 * This configuration is a wrong authentication
 */

var config = {
	port: 8080,
	ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"],

	language: "en",
	timeFormat: 12,
	units: "metric",
	electronOptions: {
		webPreferences: {
			nodeIntegration: true,
		},
	},

	modules: [
		{
			module: "calendar",
			position: "bottom_bar",
			config: {
				calendars: [
					{
						maximumNumberOfDays: 10000,
						url: "http://localhost:8020/tests/configs/data/calendar_test.ics",
						auth: {
							user: "MagicMirror",
							pass: "StairwayToHeaven",
							method: "basic"
						}
					}
				]
			}
		}
	]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}
<|endoftext|>


<commit_before>'use strict';

function log(success, message, specification) {
	let text = success ? 'Success' : 'Fail';
	if(message) {
		text += ' : ';
		text += message;
		if(specification) {
			text += ' - ';
			text += specification;
		}
	}
	if(this.debug) {
		console.log(text);
	}
}

function check_has_begun() {
	if(!this.began) {
		throw new Error('Assert must be started before beginning testing');
	}
}

export class Assert {
	constructor(onsuccess, onfail, onbegin, onend, debug) {
		this.onsuccess = onsuccess;
		this.onfail = onfail;
		this.onbegin = onbegin;
		this.onend = onend;
		this.debug = debug || false;
		this.results = [];
		this.began = false;
		this.times = {};
		this.counters = {
			successes: 0,
			fails: 0
		};
	}
	begin() {
		this.times.start = new Date();
		this.began = true;
		//callback
		if(this.onbegin) {
			this.onbegin.call(this);
		}
	}
	end() {
		this.times.stop = new Date();
		//callback
		if(this.onend) {
			this.onend.call(this);
		}
	}
	getDuration() {
		return this.times.stop.getTime() - this.times.start.getTime();
	}
	getSuccessesNumber() {
		return this.counters.successes;
	}
	getFailsNumber() {
		return this.counters.fails;
	}
	getTotal() {
		return this.results.length;
	}
	globalize(object) {
		const hook = object || window;
		const methods = ['begin', 'end', 'success', 'fail', 'equal', 'notEqual', 'similar', 'notSimilar', 'defined', 'undefined', 'null', 'notNull', 'ok', 'notOk', 'doesThrow', 'doesNotThrow'];
		methods.forEach(method => hook[method] = this[method].bind(this));
	}
	success(message, specification) {
		check_has_begun.call(this);
		this.results.push({success: true, message: message, specification: specification});
		//increment counter
		this.counters.successes++;
		//log
		log.call(this, true, message, specification);
		//callback
		if(this.onsuccess) {
			this.onsuccess.call(undefined, message, specification);
		}
	}
	fail(message, specification) {
		check_has_begun.call(this);
		this.results.push({success: false, message: message, specification: specification});
		//increment counter
		this.counters.fails++;
		//log
		log.call(this, false, message, specification);
		//callback
		if(this.onfail) {
			this.onfail.call(undefined, message, specification);
		}
	}
	equal(actual, expected, message, specification) {
		actual === expected ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Expected [' + expected + ']', specification);
	}
	notEqual(actual, notExpected, message, specification) {
		actual !== notExpected ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Not expected [' + notExpected + ']', specification);
	}
	similar(actual, expected, message, specification) {
		Object.equals(actual, expected) ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Expected [' + expected + ']', specification);
	}
	notSimilar(actual, notExpected, message, specification) {
		!Object.equals(actual, notExpected) ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Not expected [' + notExpected + ']', specification);
	}
	defined(value, message, specification) {
		this.notEqual(value, undefined, message, specification);
	}
	undefined(value, message, specification) {
		this.equal(value, undefined, message, specification);
	}
	null(value, message, specification) {
		this.equal(value, null, message, specification);
	}
	notNull(value, message, specification) {
		this.notEqual(value, null, message, specification);
	}
	ok(assertion, message, specification) {
		this.equal(assertion, true, message, specification);
	}
	notOk(assertion, message, specification) {
		this.equal(assertion, false, message, specification);
	}
	doesThrow(block, exception_assert, message, specification) {
		try {
			block.call();
			this.fail(message || 'Code does not throw an exception', specification);
		}
		catch(exception) {
			if(!exception_assert) {
				this.success(message || 'Code throws an exception', specification);
			}
			//check exception matches criteria
			else {
				const check = exception_assert.call(exception);
				if(check === undefined) {
					this.fail(message + ': Exception assert must return a boolean', specification);
				}
				else if(check) {
					this.success(message || 'Code throws an exception matching criteria', specification);
				}
				else {
					this.fail(message + ': Code does not throw the good exception: Actual [' + exception + ']', specification);
				}
			}
		}
	}
	doesNotThrow(block, message, specification) {
		try {
			block.call();
			this.success(message || 'Code does not throw an exception', specification);
		}
		catch(exception) {
			this.fail(message + ': Code throws an exception: ' + exception, specification);
		}
	}
}
<commit_msg>Remove code used to globalize the assert object<commit_after>function log(success, message, specification) {
	let text = success ? 'Success' : 'Fail';
	if(message) {
		text += ' : ';
		text += message;
		if(specification) {
			text += ' - ';
			text += specification;
		}
	}
	if(this.debug) {
		console.log(text);
	}
}

function check_has_begun() {
	if(!this.began) {
		throw new Error('Assert must be started before beginning testing');
	}
}

export class Assert {
	constructor(onsuccess, onfail, onbegin, onend, debug) {
		this.onsuccess = onsuccess;
		this.onfail = onfail;
		this.onbegin = onbegin;
		this.onend = onend;
		this.debug = debug || false;
		this.results = [];
		this.began = false;
		this.times = {};
		this.counters = {
			successes: 0,
			fails: 0
		};
	}
	begin() {
		this.times.start = new Date();
		this.began = true;
		//callback
		if(this.onbegin) {
			this.onbegin.call(this);
		}
	}
	end() {
		this.times.stop = new Date();
		//callback
		if(this.onend) {
			this.onend.call(this);
		}
	}
	getDuration() {
		return this.times.stop.getTime() - this.times.start.getTime();
	}
	getSuccessesNumber() {
		return this.counters.successes;
	}
	getFailsNumber() {
		return this.counters.fails;
	}
	getTotal() {
		return this.results.length;
	}
	success(message, specification) {
		check_has_begun.call(this);
		this.results.push({success: true, message: message, specification: specification});
		//increment counter
		this.counters.successes++;
		//log
		log.call(this, true, message, specification);
		//callback
		if(this.onsuccess) {
			this.onsuccess.call(undefined, message, specification);
		}
	}
	fail(message, specification) {
		check_has_begun.call(this);
		this.results.push({success: false, message: message, specification: specification});
		//increment counter
		this.counters.fails++;
		//log
		log.call(this, false, message, specification);
		//callback
		if(this.onfail) {
			this.onfail.call(undefined, message, specification);
		}
	}
	equal(actual, expected, message, specification) {
		actual === expected ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Expected [' + expected + ']', specification);
	}
	notEqual(actual, notExpected, message, specification) {
		actual !== notExpected ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Not expected [' + notExpected + ']', specification);
	}
	similar(actual, expected, message, specification) {
		Object.equals(actual, expected) ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Expected [' + expected + ']', specification);
	}
	notSimilar(actual, notExpected, message, specification) {
		!Object.equals(actual, notExpected) ? this.success(message, specification) : this.fail(message + ': Actual [' + actual + '] - Not expected [' + notExpected + ']', specification);
	}
	defined(value, message, specification) {
		this.notEqual(value, undefined, message, specification);
	}
	undefined(value, message, specification) {
		this.equal(value, undefined, message, specification);
	}
	null(value, message, specification) {
		this.equal(value, null, message, specification);
	}
	notNull(value, message, specification) {
		this.notEqual(value, null, message, specification);
	}
	ok(assertion, message, specification) {
		this.equal(assertion, true, message, specification);
	}
	notOk(assertion, message, specification) {
		this.equal(assertion, false, message, specification);
	}
	doesThrow(block, exception_assert, message, specification) {
		try {
			block.call();
			this.fail(message || 'Code does not throw an exception', specification);
		}
		catch(exception) {
			if(!exception_assert) {
				this.success(message || 'Code throws an exception', specification);
			}
			//check exception matches criteria
			else {
				const check = exception_assert.call(exception);
				if(check === undefined) {
					this.fail(message + ': Exception assert must return a boolean', specification);
				}
				else if(check) {
					this.success(message || 'Code throws an exception matching criteria', specification);
				}
				else {
					this.fail(message + ': Code does not throw the good exception: Actual [' + exception + ']', specification);
				}
			}
		}
	}
	doesNotThrow(block, message, specification) {
		try {
			block.call();
			this.success(message || 'Code does not throw an exception', specification);
		}
		catch(exception) {
			this.fail(message + ': Code throws an exception: ' + exception, specification);
		}
	}
}
<|endoftext|>


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

selectowl.ontology.feedMee = function () {
  // index is a hashmap from URI to jOWL Ontology object representing the resource
  // sometimes the URI is shortened when base-namespace is present
  //
  // we use full URI to reference everything here.. in our inner repre this.model
  var index = jOWL.index('ID');
  var m = selectowl.model;
  var r, el, my;

  // Fill prefixes
  var ns = jOWL.NS;
  for ( p in ns ) {
    p = ns[p];
    // Check if it's valid prefix and if we don't have it already
    if (p.URI && p.prefix &&
          !this.prefixes.contains(p.uri) &&
          !this.prefixes.contains(p.prefix)) {
      this.prefixes.add(p.prefix, p.URI);
    }
  }

  // Fill Resources
  for ( r in index ) {
    el = index[r];
    my = {};
    my.baseURI = el.baseURI
    my.URI = el.URI;
    my.type = el.type;
    my.name = el.name;
    my.prefix = this.prefixes.uri2prefix(el.baseURI) || this.prefixes.guessPrefix(el.baseURI);

    if ( el.isProperty ) {
      my.domain = el.domain;
      my.range = el.range;
      this.properties.add(my);
    } else {
      this.classes.add(my);
    }
  }
}

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

selectowl.ontology.prefixes._byIdx = [];
selectowl.ontology.prefixes._byPfx = {};
selectowl.ontology.prefixes._byUri = {};

selectowl.ontology.prefixes.add = function ( prefix, uri ) {
  var o = {};
  o.prefix = prefix;
  o.uri = uri;
  this._byIdx.push(o);
  this._byUri[uri] = o;
  this._byPfx[prefix] = o;
}

selectowl.ontology.prefixes.get = function ( idx ) {
  return this._byIdx[idx];
}

selectowl.ontology.prefixes.getByPrefix = function ( prefix ) {
  return this._byPfx[prefix];
}

selectowl.ontology.prefixes.getByUri = function ( uri ) {
  return this._byUri[uri];
}

selectowl.ontology.prefixes.prefix2uri = function ( prefix ) {
  return this._byPfx[prefix].uri;
}

selectowl.ontology.prefixes.uri2prefix = function ( uri ) {
  return this._byUri[uri].prefix;
}

selectowl.ontology.prefixes.findIndex = function ( str ) {
  var i, o;
  for (i = 0; i <  this._byIdx.length; i++) {
    o = this._byIdx[i];
    if (o.prefix == str || o.uri == str) return i;
  }
  return -1;
}

selectowl.ontology.prefixes.contains = function ( str ) {
  return (this._byUri[str] != null || this._byPfx[str] != null);
}

selectowl.ontology.prefixes.guessPrefix = function ( uri ) {
  rg_last_word = /[a-zA-Z-_]+(?=[^a-zA-Z-_]*$)/;
  prefix = uri.match(rg_last_word);
  if(prefix) {
    this.add(prefix, uri);
    return prefix;
  }
  throw 'Can not guess prefix of URI: ' + uri;
}

selectowl.ontology.prefixes.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.prefixes.delete = function ( prefix ) {
  uri = this.prefix2uri(prefix);
  idx = this.findIndex(prefix);
  delete this._byIdx[idx];
  delete this._byUri[uri];
  delete this._byPfx[prefix];
}

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

selectowl.ontology.classes._byIdx = [];
selectowl.ontology.classes._byUri = {};
selectowl.ontology.classes.add = function ( obj ) {
  this._byIdx.push(obj);
  this._byUri[obj.baseURI] = obj;
}

selectowl.ontology.classes.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.classes.get = function ( idx ) {
  return this._byIdx[idx];
}

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

selectowl.ontology.properties._byIdx = [];
selectowl.ontology.properties._byUri = {};
selectowl.ontology.properties.add = function ( obj ) {
  this._byIdx.push(obj);
  this._byUri[obj.baseURI] = obj;
}

selectowl.ontology.properties.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.properties.get = function ( idx ) {
  return this._byIdx[idx];
}

<commit_msg>corrected typo<commit_after>
/************************************************************
 *                                                          *
 ************************************************************/

selectowl.ontology.feedMee = function () {
  // index is a hashmap from URI to jOWL Ontology object representing the resource
  // sometimes the URI is shortened when base-namespace is present
  //
  // we use full URI to reference everything here.. in our inner repre this.model
  var index = jOWL.index('ID');
  var m = selectowl.model;
  var r, el, my;

  // Fill prefixes
  var ns = jOWL.NS;
  for ( p in ns ) {
    p = ns[p];
    // Check if it's valid prefix and if we don't have it already
    if (p.URI && p.prefix &&
          !this.prefixes.contains(p.URI) &&
          !this.prefixes.contains(p.prefix)) {
      this.prefixes.add(p.prefix, p.URI);
    }
  }

  // Fill Resources
  for ( r in index ) {
    el = index[r];
    my = {};
    my.baseURI = el.baseURI
    my.uri = el.URI; //XXX EVEN FOR URI, I USE ALL LOWERCASE IF I CAN...
    my.type = el.type;
    my.name = el.name;
    my.prefix = this.prefixes.uri2prefix(el.baseURI) || this.prefixes.guessPrefix(el.baseURI);

    if ( el.isProperty ) {
      my.domain = el.domain;
      my.range = el.range;
      this.properties.add(my);
    } else {
      this.classes.add(my);
    }
  }
}

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

selectowl.ontology.prefixes._byIdx = [];
selectowl.ontology.prefixes._byPfx = {};
selectowl.ontology.prefixes._byUri = {};

selectowl.ontology.prefixes.add = function ( prefix, uri ) {
  var o = {};
  o.prefix = prefix;
  o.uri = uri;
  this._byIdx.push(o);
  this._byUri[uri] = o;
  this._byPfx[prefix] = o;
}

selectowl.ontology.prefixes.get = function ( idx ) {
  return this._byIdx[idx];
}

selectowl.ontology.prefixes.getByPrefix = function ( prefix ) {
  return this._byPfx[prefix];
}

selectowl.ontology.prefixes.getByUri = function ( uri ) {
  return this._byUri[uri];
}

selectowl.ontology.prefixes.prefix2uri = function ( prefix ) {
  return this._byPfx[prefix].uri;
}

selectowl.ontology.prefixes.uri2prefix = function ( uri ) {
  return this._byUri[uri].prefix;
}

selectowl.ontology.prefixes.findIndex = function ( str ) {
  var i, o;
  for (i = 0; i <  this._byIdx.length; i++) {
    o = this._byIdx[i];
    if (o.prefix == str || o.uri == str) return i;
  }
  return -1;
}

selectowl.ontology.prefixes.contains = function ( str ) {
  return (this._byUri[str] != null || this._byPfx[str] != null);
}

selectowl.ontology.prefixes.guessPrefix = function ( uri ) {
  rg_last_word = /[a-zA-Z-_]+(?=[^a-zA-Z-_]*$)/;
  prefix = uri.match(rg_last_word);
  if(prefix) {
    this.add(prefix, uri);
    return prefix;
  }
  throw 'Can not guess prefix of URI: ' + uri;
}

selectowl.ontology.prefixes.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.prefixes.delete = function ( prefix ) {
  uri = this.prefix2uri(prefix);
  idx = this.findIndex(prefix);
  delete this._byIdx[idx];
  delete this._byUri[uri];
  delete this._byPfx[prefix];
}

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

selectowl.ontology.classes._byIdx = [];
selectowl.ontology.classes._byUri = {};
selectowl.ontology.classes.add = function ( obj ) {
  this._byIdx.push(obj);
  this._byUri[obj.baseURI] = obj;
}

selectowl.ontology.classes.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.classes.get = function ( idx ) {
  return this._byIdx[idx];
}

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

selectowl.ontology.properties._byIdx = [];
selectowl.ontology.properties._byUri = {};
selectowl.ontology.properties.add = function ( obj ) {
  this._byIdx.push(obj);
  this._byUri[obj.baseURI] = obj;
}

selectowl.ontology.properties.getLength = function () {
  return this._byIdx.length;
}

selectowl.ontology.properties.get = function ( idx ) {
  return this._byIdx[idx];
}

<|endoftext|>


<commit_before>/*
    ***** BEGIN LICENSE BLOCK *****

	Copyright (c) 2009  Zotero
	                    Center for History and New Media
						George Mason University, Fairfax, Virginia, USA
						http://zotero.org

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

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

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

    ***** END LICENSE BLOCK *****
*/

var EXPORTED_SYMBOLS = ["ZoteroPluginInstaller"];
var Zotero = Components.classes["@zotero.org/Zotero;1"]
				// Currently uses only nsISupports
				//.getService(Components.interfaces.chnmIZoteroService).
				.getService(Components.interfaces.nsISupports)
				.wrappedJSObject;

var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
	.getService(Components.interfaces.nsIXULAppInfo);
var versionComparator = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
	.getService(Components.interfaces.nsIVersionComparator);
if(versionComparator.compare(appInfo.platformVersion, "2.0a1") >= 0) {
	Components.utils.import("resource://gre/modules/AddonManager.jsm");
} else {
	var AddonManager = false;
}

var installationInProgress = false;
var _runningTimers = [];
function setTimeout(func, ms) {
	var timer = Components.classes["@mozilla.org/timer;1"].
		createInstance(Components.interfaces.nsITimer);
	var timerCallback = {"notify":function() {
		_runningTimers.splice(_runningTimers.indexOf(timer), 1);
		func();
	}};
	timer.initWithCallback(timerCallback, ms, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
	// add timer to global scope so that it doesn't get garbage collected before it completes
	_runningTimers.push(timer);
}

var ZoteroPluginInstaller = function(addon, failSilently, force) {
	this._addon = addon;
	this.failSilently = failSilently;
	this.force = force;

	var prefService = Components.classes["@mozilla.org/preferences-service;1"].
			getService(Components.interfaces.nsIPrefService);
	this.prefBranch = prefService.getBranch(this._addon.EXTENSION_PREF_BRANCH);

	var me = this;
	var extensionIDs = [this._addon.EXTENSION_ID].concat([req.id for each(req in this._addon.REQUIRED_ADDONS)]);
	if(AddonManager) {
		AddonManager.getAddonsByIDs(extensionIDs, function(addons) {
			me._addons = addons;
			me._addonInfoAvailable();
		});
	} else {
		var extMan = Components.classes['@mozilla.org/extensions/manager;1'].
								getService(Components.interfaces.nsIExtensionManager)
		this._addons = [extMan.getItemForID(id) for each(id in extensionIDs)];
		this._addonInfoAvailable();
	}
}

ZoteroPluginInstaller.prototype = {
	"_errorDisplayed":false,

	"_addonInfoAvailable":function() {
		try {
			this._version = this._addons[0].version;
			if(!this._checkVersions()) return;

			try {
				this._addon.verifyNotCorrupt(this);
			} catch(e) {
				Zotero.debug("Not installing "+this._addon.EXTENSION_STRING+": "+e.toString());
				return;
			}

			if(this.force || (
					(
						this.prefBranch.getCharPref("version") != this._version
						|| (!Zotero.isStandalone && !this.prefBranch.getBoolPref("installed"))
					)
					&& !this.prefBranch.getBoolPref("skipInstallation")
				)) {

				var me = this;
				if(installationInProgress) return;

				installationInProgress = true;
				if(!this._addon.DISABLE_PROGRESS_WINDOW) {
					this._progressWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
						.getService(Components.interfaces.nsIWindowWatcher)
						.openWindow(null, "chrome://"+this._addon.EXTENSION_DIR+"/content/progress.xul", '',
							"chrome,resizable=no,close=no,centerscreen", null);
					this._progressWindow.addEventListener("load", function() { me._firstRunListener() }, false);
				} else {
					this._addon.install(this);
				}
			}
		} catch(e) {
			Zotero.logError(e);
		}
	},

	"isInstalled":function() {
		while(!this._version) Zotero.mainThread.processNextEvent(true);
		return this.prefBranch.getCharPref("version") == this._version &&
			this.prefBranch.getBoolPref("installed");
	},

	"getAddonPath":function(addonID) {
		if(AddonManager) {
			for each(var addon in this._addons) {
				if(addon && addon.id == addonID) {
					return addon.getResourceURI().
						QueryInterface(Components.interfaces.nsIFileURL).file;
				}
			}
		} else {
			return Components.classes["@mozilla.org/extensions/manager;1"].
				getService(Components.interfaces.nsIExtensionManager).
				getInstallLocation(addonID).
				getItemLocation(addonID);
		}
	},

	"setProgressWindowLabel":function(value) {
		if(this._progressWindow) this._progressWindowLabel.value = value;
	},

	"closeProgressWindow":function(value) {
		if(this._progressWindow) this._progressWindow.close();
	},

	"success":function() {
		installationInProgress = false;
		this.closeProgressWindow();
		this.prefBranch.setCharPref("version", this._version);
		this.prefBranch.setBoolPref("installed", true);
		this.prefBranch.setBoolPref("skipInstallation", false);
		if(this.force && !this._addon.DISABLE_PROGRESS_WINDOW) {
			Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
				.getService(Components.interfaces.nsIPromptService)
				.alert(null, this._addon.EXTENSION_STRING,
				'Installation was successful.');
		}
	},

	"error":function(error, notFailure) {
		installationInProgress = false;
		this.closeProgressWindow();
		if(!notFailure) {
			this.prefBranch.setCharPref("version", this._version);
			this.prefBranch.setBoolPref("installed", false);
		}
		if(this.failSilently) return;
		if(this._errorDisplayed) return;
		this._errorDisplayed = true;
		Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
			.getService(Components.interfaces.nsIPromptService)
			.alert(null, this._addon.EXTENSION_STRING,
			(error ? error : 'Installation could not be completed because an error occurred. Please ensure that '+this._addon.APP+' is closed, and then restart Firefox.'));
	},

	"cancelled":function(error) {
		installationInProgress = false;
		this.closeProgressWindow();
		if(!this.force) this.prefBranch.setBoolPref("skipInstallation", true);
	},

	"_firstRunListener":function() {
		this._progressWindowLabel = this._progressWindow.document.getElementById("progress-label");
		this._progressWindowLabel.value = "Installing "+this._addon.EXTENSION_STRING+"...";
		var me = this;
		setTimeout(function() {
			me._progressWindow.focus();
			setTimeout(function() {
				me._progressWindow.focus();
				try {
					me._addon.install(me);
				} catch(e) {
					me.error();
					throw e;
				}
			}, 500);
		}, 100);
	},

	"_checkVersions":function() {
		for(var i=0; i<this._addon.REQUIRED_ADDONS.length; i++) {
			var checkAddon = this._addon.REQUIRED_ADDONS[i];

			// check versions
			try {
				var comp = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
					.getService(Components.interfaces.nsIVersionComparator)
					.compare((checkAddon.id == "zotero@chnm.gmu.edu" ? Zotero.version : this._addons[i+1].version), checkAddon.minVersion);
			} catch(e) {
				var comp = null;
			}

			if((comp === null && checkAddon.required) || comp < 0) {
				if(checkAddon.required === false) {
					var err = this._addon.EXTENSION_STRING+' '+this._addons[0].version+' is incompatible with versions of '+checkAddon.name+
						' before '+checkAddon.minVersion+'. Please remove '+checkAddon.name+', or download the latest version from '+checkAddon.url+'.';
				} else {
					var err = this._addon.EXTENSION_STRING+' '+this._addons[0].version+' requires '+checkAddon.name+' '+checkAddon.minVersion+
						' or later to run. Please download the latest version of '+checkAddon.name+' from '+checkAddon.url+'.';
				}
				this.error(err, true);
				if(this.failSilently) {
					throw err;
				} else {
					Zotero.debug("Not installing "+this._addon.EXTENSION_STRING+": requires "+checkAddon.name+" "+checkAddon.minVersion);
					return false;
				}
			}
		}

		return true;
	}
}<commit_msg>  "Firefox" -> "Firefox"/"Zotero" as appropriate<commit_after>/*
    ***** BEGIN LICENSE BLOCK *****

	Copyright (c) 2009  Zotero
	                    Center for History and New Media
						George Mason University, Fairfax, Virginia, USA
						http://zotero.org

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

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

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

    ***** END LICENSE BLOCK *****
*/

var EXPORTED_SYMBOLS = ["ZoteroPluginInstaller"];
var Zotero = Components.classes["@zotero.org/Zotero;1"]
				// Currently uses only nsISupports
				//.getService(Components.interfaces.chnmIZoteroService).
				.getService(Components.interfaces.nsISupports)
				.wrappedJSObject;

var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
	.getService(Components.interfaces.nsIXULAppInfo);
var versionComparator = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
	.getService(Components.interfaces.nsIVersionComparator);
if(versionComparator.compare(appInfo.platformVersion, "2.0a1") >= 0) {
	Components.utils.import("resource://gre/modules/AddonManager.jsm");
} else {
	var AddonManager = false;
}

var installationInProgress = false;
var _runningTimers = [];
function setTimeout(func, ms) {
	var timer = Components.classes["@mozilla.org/timer;1"].
		createInstance(Components.interfaces.nsITimer);
	var timerCallback = {"notify":function() {
		_runningTimers.splice(_runningTimers.indexOf(timer), 1);
		func();
	}};
	timer.initWithCallback(timerCallback, ms, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
	// add timer to global scope so that it doesn't get garbage collected before it completes
	_runningTimers.push(timer);
}

var ZoteroPluginInstaller = function(addon, failSilently, force) {
	this._addon = addon;
	this.failSilently = failSilently;
	this.force = force;

	var prefService = Components.classes["@mozilla.org/preferences-service;1"].
			getService(Components.interfaces.nsIPrefService);
	this.prefBranch = prefService.getBranch(this._addon.EXTENSION_PREF_BRANCH);

	var me = this;
	var extensionIDs = [this._addon.EXTENSION_ID].concat([req.id for each(req in this._addon.REQUIRED_ADDONS)]);
	if(AddonManager) {
		AddonManager.getAddonsByIDs(extensionIDs, function(addons) {
			me._addons = addons;
			me._addonInfoAvailable();
		});
	} else {
		var extMan = Components.classes['@mozilla.org/extensions/manager;1'].
								getService(Components.interfaces.nsIExtensionManager)
		this._addons = [extMan.getItemForID(id) for each(id in extensionIDs)];
		this._addonInfoAvailable();
	}
}

ZoteroPluginInstaller.prototype = {
	"_errorDisplayed":false,

	"_addonInfoAvailable":function() {
		try {
			this._version = this._addons[0].version;
			if(!this._checkVersions()) return;

			try {
				this._addon.verifyNotCorrupt(this);
			} catch(e) {
				Zotero.debug("Not installing "+this._addon.EXTENSION_STRING+": "+e.toString());
				return;
			}

			if(this.force || (
					(
						this.prefBranch.getCharPref("version") != this._version
						|| (!Zotero.isStandalone && !this.prefBranch.getBoolPref("installed"))
					)
					&& !this.prefBranch.getBoolPref("skipInstallation")
				)) {

				var me = this;
				if(installationInProgress) return;

				installationInProgress = true;
				if(!this._addon.DISABLE_PROGRESS_WINDOW) {
					this._progressWindow = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
						.getService(Components.interfaces.nsIWindowWatcher)
						.openWindow(null, "chrome://"+this._addon.EXTENSION_DIR+"/content/progress.xul", '',
							"chrome,resizable=no,close=no,centerscreen", null);
					this._progressWindow.addEventListener("load", function() { me._firstRunListener() }, false);
				} else {
					this._addon.install(this);
				}
			}
		} catch(e) {
			Zotero.logError(e);
		}
	},

	"isInstalled":function() {
		while(!this._version) Zotero.mainThread.processNextEvent(true);
		return this.prefBranch.getCharPref("version") == this._version &&
			this.prefBranch.getBoolPref("installed");
	},

	"getAddonPath":function(addonID) {
		if(AddonManager) {
			for each(var addon in this._addons) {
				if(addon && addon.id == addonID) {
					return addon.getResourceURI().
						QueryInterface(Components.interfaces.nsIFileURL).file;
				}
			}
		} else {
			return Components.classes["@mozilla.org/extensions/manager;1"].
				getService(Components.interfaces.nsIExtensionManager).
				getInstallLocation(addonID).
				getItemLocation(addonID);
		}
	},

	"setProgressWindowLabel":function(value) {
		if(this._progressWindow) this._progressWindowLabel.value = value;
	},

	"closeProgressWindow":function(value) {
		if(this._progressWindow) this._progressWindow.close();
	},

	"success":function() {
		installationInProgress = false;
		this.closeProgressWindow();
		this.prefBranch.setCharPref("version", this._version);
		this.prefBranch.setBoolPref("installed", true);
		this.prefBranch.setBoolPref("skipInstallation", false);
		if(this.force && !this._addon.DISABLE_PROGRESS_WINDOW) {
			Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
				.getService(Components.interfaces.nsIPromptService)
				.alert(null, this._addon.EXTENSION_STRING,
				'Installation was successful.');
		}
	},

	"error":function(error, notFailure) {
		installationInProgress = false;
		this.closeProgressWindow();
		if(!notFailure) {
			this.prefBranch.setCharPref("version", this._version);
			this.prefBranch.setBoolPref("installed", false);
		}
		if(this.failSilently) return;
		if(this._errorDisplayed) return;
		this._errorDisplayed = true;
		Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
			.getService(Components.interfaces.nsIPromptService)
			.alert(null, this._addon.EXTENSION_STRING,
			(error ? error : 'Installation could not be completed because an error occurred. Please ensure that '+this._addon.APP+' is closed, and then restart '+Zotero.appName+'.'));
	},

	"cancelled":function(error) {
		installationInProgress = false;
		this.closeProgressWindow();
		if(!this.force) this.prefBranch.setBoolPref("skipInstallation", true);
	},

	"_firstRunListener":function() {
		this._progressWindowLabel = this._progressWindow.document.getElementById("progress-label");
		this._progressWindowLabel.value = "Installing "+this._addon.EXTENSION_STRING+"...";
		var me = this;
		setTimeout(function() {
			me._progressWindow.focus();
			setTimeout(function() {
				me._progressWindow.focus();
				try {
					me._addon.install(me);
				} catch(e) {
					me.error();
					throw e;
				}
			}, 500);
		}, 100);
	},

	"_checkVersions":function() {
		for(var i=0; i<this._addon.REQUIRED_ADDONS.length; i++) {
			var checkAddon = this._addon.REQUIRED_ADDONS[i];

			// check versions
			try {
				var comp = Components.classes["@mozilla.org/xpcom/version-comparator;1"]
					.getService(Components.interfaces.nsIVersionComparator)
					.compare((checkAddon.id == "zotero@chnm.gmu.edu" ? Zotero.version : this._addons[i+1].version), checkAddon.minVersion);
			} catch(e) {
				var comp = null;
			}

			if((comp === null && checkAddon.required) || comp < 0) {
				if(checkAddon.required === false) {
					var err = this._addon.EXTENSION_STRING+' '+this._addons[0].version+' is incompatible with versions of '+checkAddon.name+
						' before '+checkAddon.minVersion+'. Please remove '+checkAddon.name+', or download the latest version from '+checkAddon.url+'.';
				} else {
					var err = this._addon.EXTENSION_STRING+' '+this._addons[0].version+' requires '+checkAddon.name+' '+checkAddon.minVersion+
						' or later to run. Please download the latest version of '+checkAddon.name+' from '+checkAddon.url+'.';
				}
				this.error(err, true);
				if(this.failSilently) {
					throw err;
				} else {
					Zotero.debug("Not installing "+this._addon.EXTENSION_STRING+": requires "+checkAddon.name+" "+checkAddon.minVersion);
					return false;
				}
			}
		}

		return true;
	}
}<|endoftext|>


<commit_before>#!/usr/bin/env node
/*
 * Pulley: Easy Github Pull Request Lander
 * Copyright 2011 John Resig
 * MIT Licensed
 */
(function() {
	"use strict";

	var child = require("child_process"),
		http = require("https"),
		fs = require("fs"),
		prompt = require("prompt"),
		request = require("request"),
		colors = require("colors"),
		pkg = require("./package"),

		// Process references
		exec = child.exec,
		spawn = child.spawn,

		// Process arguments
		id = process.argv[ 2 ],
		done = process.argv[ 3 ],

		// Localized application references
		user_repo = "",
		tracker = "",
		token = "",

		// Initialize config file
		config = JSON.parse( fs.readFileSync( __dirname + "/config.json" ) );

	// We don't want the default prompt message
	prompt.message = "";

	process.stdout.write( "Initializing... ".blue );

	exec( "git config --global --get pulley.token", function( error, stdout, stderr ) {
		token = stdout.trim();

		if ( token ) {
			init();
		} else {
			login();
		}
	});

	function login() {
		console.log("\nPlease login with your GitHub credentials.");
		console.log("Your credentials are only needed this one time to get a token from GitHub.");
		prompt.start();
		prompt.get([{
			name: "username",
			message: "Username",
			empty: false
		}, {
			name: "password",
			message: "Password",
			empty: false,
			hidden: true
		}], function( err, result ) {
			var auth = encodeURIComponent( result.username ) + ":" + encodeURIComponent( result.password );
			request.post("https://" + auth + "@api.github.com/authorizations", {
				json: true,
				body: {
					scopes: ["repo"],
					note: "Pulley",
					note_url: "https://github.com/jeresig/pulley"
				}
			}, function( err, res, body ) {
				token = body.token;
				if ( token ) {
					exec( "git config --global --add pulley.token " + token, function( error, stdout, stderr ) {
						console.log( "Success!".green );
						init();
					});
				} else {
					console.log( ( body.message + ". Try again." ).red );
					login();
				}
			});
		});
	}

	function init() {
		if ( !id ) {
			exit("No pull request ID specified, please provide one.");
		}
		exec( "git remote -v show " + config.remote, function( error, stdout, stderr ) {
			user_repo = ( /URL:.*?([\w\-]+\/[\w\-]+)/.exec( stdout ) || [] )[ 1 ];
			tracker = config.repos[ user_repo ];

			if ( user_repo ) {
				getStatus();
			} else {
				exit("External repository not found.");
			}
		});
	}

	function getStatus() {
		exec( "git status", function( error, stdout, stderr ) {
			if ( /Changes to be committed/i.test( stdout ) ) {
				if ( done ) {
					getPullData();
				} else {
					exit("Please commit changed files before attemping a pull/merge.");
				}
			} else if ( /Changes not staged for commit/i.test( stdout ) ) {
				if ( done ) {
					exit("Please add files that you wish to commit.");

				} else {
					exit("Please stash files before attempting a pull/merge.");
				}
			} else {
				if ( done ) {
					exit("It looks like you've broken your merge attempt.");
				} else {
					getPullData();
				}
			}
		});
	}

	function getPullData() {
		var path = "/repos/" + user_repo + "/pulls/" + id;

		console.log( "done.".green );
		process.stdout.write( "Getting pull request details... ".blue );

		callAPI( path, function( data ) {
			try {
				var pull = JSON.parse( data );

				console.log( "done.".green );

				if ( done ) {
					commit( pull );
				} else {
					mergePull( pull );
				}
			} catch( e ) {
				exit("Error retrieving pull request from Github.");
			}
		});
	}

	function mergePull( pull ) {
		var repo = pull.head.repo.ssh_url,
			head_branch = pull.head.ref,
			base_branch = pull.base.ref,
			branch = "pull-" + id,
			checkout = "git checkout " + base_branch,
			checkout_cmds = [
				checkout,
				"git pull " + config.remote + " " + base_branch,
				"git submodule update --init",
				"git checkout -b " + branch
			];

		process.stdout.write( "Pulling and merging results... ".blue );

		if ( pull.state === "closed" ) {
			exit("Can not merge closed Pull Requests.");
		}

		if ( pull.merged ) {
			exit("This Pull Request has already been merged.");
		}

		// TODO: give user the option to resolve the merge by themselves
		if ( !pull.mergeable ) {
			exit("This Pull Request is not automatically mergeable.");
		}

		exec( checkout_cmds.join( " && " ), function( error, stdout, stderr ) {
			if ( /toplevel/i.test( stderr ) ) {
				exit("Please call pulley from the toplevel directory of this repo.");
			} else if ( /fatal/i.test( stderr ) ) {
				exec( "git branch -D " + branch + " && " + checkout, doPull );
			} else {
				doPull();
			}
		});

		function doPull( error, stdout, stderr ) {
			var pull_cmds = [
				"git pull " + repo + " " + head_branch,
				checkout,
				"git merge --no-commit --squash " + branch
			];

			exec( pull_cmds.join( " && " ), function( error, stdout, stderr ) {
				if ( /Merge conflict/i.test( stdout ) ) {
					exit("Merge conflict. Please resolve then run: " +
						process.argv.join(" ") + " done");
				} else if ( /error/.test( stderr ) ) {
					exit("Unable to merge.  Please resolve then retry:\n" + stderr);
				} else {
					console.log( "done.".green );
					commit( pull );
				}
			});
		}
	}

	function commit( pull ) {
		var path = "/repos/" + user_repo + "/pulls/" + id + "/commits";

		process.stdout.write( "Getting author and committing changes... ".blue );

		callAPI( path, function( data ) {
			var match,
				msg = "Close GH-" + id + ": " + pull.title + ".",
				author = JSON.parse( data )[ 0 ].commit.author.name,
				base_branch = pull.base.ref,
				issues = [],
				urls = [],
				findBug = /#(\d+)/g;

			// Search title and body for issues for issues to link to
			if ( tracker ) {
				while ( ( match = findBug.exec( pull.title + pull.body ) ) ) {
					urls.push( tracker + match[ 1 ] );
				}
			}

			// Search just body for issues to add to the commit message
			while ( ( match = findBug.exec( pull.body ) ) ) {
				issues.push( " Fixes #" + match[ 1 ] );
			}

			// Add issues to the commit message
			msg += issues.join(",");

			if ( urls.length ) {
				msg += "\n\nMore Details:" + urls.map(function( url ) {
					return "\n - " + url;
				}).join("");
			}

			var commit = [ "commit", "-a", "--message=" + msg ];

			if ( config.interactive ) {
				commit.push("-e");
			}

			if ( author ) {
				commit.push( "--author=" + author );
			}

			getHEAD(function( oldCommit ) {
				// Thanks to: https://gist.github.com/927052
				spawn( "git", commit, {
					customFds: [ process.stdin, process.stdout, process.stderr ]
				}).on( "exit", function() {
					getHEAD(function( newCommit ) {
						if ( oldCommit === newCommit ) {
							reset("No commit, aborting push.");
						} else {
							exec( "git push " + config.remote + " " + base_branch, function( error, stdout, stderr ) {
								console.log( "done.".green );
								exit();
							});
						}
					});
				});
			});
		});
	}

	function callAPI( path, callback ) {
		request.get( "https://api.github.com" + path, {
			headers: {
				Authorization: "token " + token,
				"User-Agent": "Pulley " + pkg.version
			}
		}, function( err, res, body ) {
			var statusCode = res.socket._httpMessage.res.statusCode;

			if ( err ) {
				exit( err );
			}

			if ( statusCode === 404 ) {
				exit("Pull request doesn't exist");
			}

			if ( statusCode === 401 ) {
				login();
				return;
			}

			callback( body );
		});
	}

	function getHEAD( fn ) {
		exec( "git log | head -1", function( error, stdout, stderr ) {
			var commit = ( /commit (.*)/.exec( stdout ) || [] )[ 1 ];

			fn( commit );
		});
	}

	function reset( msg ) {
		console.error( ( "\n" + msg ).red );
		process.stderr.write( "Resetting files... ".red );

		exec( "git reset --hard ORIG_HEAD", function() {
			console.log( "done.".green );
			exit();
		});
	}

	function exit( msg ) {
		if ( msg ) {
			console.error( ( "\nError: " + msg ).red );
		}

		process.exit( 1 );
	}

})();
<commit_msg>Add user-agent for login API call<commit_after>#!/usr/bin/env node
/*
 * Pulley: Easy Github Pull Request Lander
 * Copyright 2011 John Resig
 * MIT Licensed
 */
(function() {
	"use strict";

	var child = require("child_process"),
		http = require("https"),
		fs = require("fs"),
		prompt = require("prompt"),
		request = require("request"),
		colors = require("colors"),
		pkg = require("./package"),

		// Process references
		exec = child.exec,
		spawn = child.spawn,

		// Process arguments
		id = process.argv[ 2 ],
		done = process.argv[ 3 ],

		// Localized application references
		user_repo = "",
		tracker = "",
		token = "",

		// Initialize config file
		config = JSON.parse( fs.readFileSync( __dirname + "/config.json" ) );

	// We don't want the default prompt message
	prompt.message = "";

	process.stdout.write( "Initializing... ".blue );

	exec( "git config --global --get pulley.token", function( error, stdout, stderr ) {
		token = stdout.trim();

		if ( token ) {
			init();
		} else {
			login();
		}
	});

	function login() {
		console.log("\nPlease login with your GitHub credentials.");
		console.log("Your credentials are only needed this one time to get a token from GitHub.");
		prompt.start();
		prompt.get([{
			name: "username",
			message: "Username",
			empty: false
		}, {
			name: "password",
			message: "Password",
			empty: false,
			hidden: true
		}], function( err, result ) {
			var auth = encodeURIComponent( result.username ) + ":" + encodeURIComponent( result.password );
			request.post("https://" + auth + "@api.github.com/authorizations", {
				json: true,
				headers: {
					"User-Agent": "Pulley " + pkg.version
				},
				body: {
					scopes: ["repo"],
					note: "Pulley",
					note_url: "https://github.com/jeresig/pulley"
				}
			}, function( err, res, body ) {
				token = body.token;
				if ( token ) {
					exec( "git config --global --add pulley.token " + token, function( error, stdout, stderr ) {
						console.log( "Success!".green );
						init();
					});
				} else {
					console.log( ( body.message + ". Try again." ).red );
					login();
				}
			});
		});
	}

	function init() {
		if ( !id ) {
			exit("No pull request ID specified, please provide one.");
		}
		exec( "git remote -v show " + config.remote, function( error, stdout, stderr ) {
			user_repo = ( /URL:.*?([\w\-]+\/[\w\-]+)/.exec( stdout ) || [] )[ 1 ];
			tracker = config.repos[ user_repo ];

			if ( user_repo ) {
				getStatus();
			} else {
				exit("External repository not found.");
			}
		});
	}

	function getStatus() {
		exec( "git status", function( error, stdout, stderr ) {
			if ( /Changes to be committed/i.test( stdout ) ) {
				if ( done ) {
					getPullData();
				} else {
					exit("Please commit changed files before attemping a pull/merge.");
				}
			} else if ( /Changes not staged for commit/i.test( stdout ) ) {
				if ( done ) {
					exit("Please add files that you wish to commit.");

				} else {
					exit("Please stash files before attempting a pull/merge.");
				}
			} else {
				if ( done ) {
					exit("It looks like you've broken your merge attempt.");
				} else {
					getPullData();
				}
			}
		});
	}

	function getPullData() {
		var path = "/repos/" + user_repo + "/pulls/" + id;

		console.log( "done.".green );
		process.stdout.write( "Getting pull request details... ".blue );

		callAPI( path, function( data ) {
			try {
				var pull = JSON.parse( data );

				console.log( "done.".green );

				if ( done ) {
					commit( pull );
				} else {
					mergePull( pull );
				}
			} catch( e ) {
				exit("Error retrieving pull request from Github.");
			}
		});
	}

	function mergePull( pull ) {
		var repo = pull.head.repo.ssh_url,
			head_branch = pull.head.ref,
			base_branch = pull.base.ref,
			branch = "pull-" + id,
			checkout = "git checkout " + base_branch,
			checkout_cmds = [
				checkout,
				"git pull " + config.remote + " " + base_branch,
				"git submodule update --init",
				"git checkout -b " + branch
			];

		process.stdout.write( "Pulling and merging results... ".blue );

		if ( pull.state === "closed" ) {
			exit("Can not merge closed Pull Requests.");
		}

		if ( pull.merged ) {
			exit("This Pull Request has already been merged.");
		}

		// TODO: give user the option to resolve the merge by themselves
		if ( !pull.mergeable ) {
			exit("This Pull Request is not automatically mergeable.");
		}

		exec( checkout_cmds.join( " && " ), function( error, stdout, stderr ) {
			if ( /toplevel/i.test( stderr ) ) {
				exit("Please call pulley from the toplevel directory of this repo.");
			} else if ( /fatal/i.test( stderr ) ) {
				exec( "git branch -D " + branch + " && " + checkout, doPull );
			} else {
				doPull();
			}
		});

		function doPull( error, stdout, stderr ) {
			var pull_cmds = [
				"git pull " + repo + " " + head_branch,
				checkout,
				"git merge --no-commit --squash " + branch
			];

			exec( pull_cmds.join( " && " ), function( error, stdout, stderr ) {
				if ( /Merge conflict/i.test( stdout ) ) {
					exit("Merge conflict. Please resolve then run: " +
						process.argv.join(" ") + " done");
				} else if ( /error/.test( stderr ) ) {
					exit("Unable to merge.  Please resolve then retry:\n" + stderr);
				} else {
					console.log( "done.".green );
					commit( pull );
				}
			});
		}
	}

	function commit( pull ) {
		var path = "/repos/" + user_repo + "/pulls/" + id + "/commits";

		process.stdout.write( "Getting author and committing changes... ".blue );

		callAPI( path, function( data ) {
			var match,
				msg = "Close GH-" + id + ": " + pull.title + ".",
				author = JSON.parse( data )[ 0 ].commit.author.name,
				base_branch = pull.base.ref,
				issues = [],
				urls = [],
				findBug = /#(\d+)/g;

			// Search title and body for issues for issues to link to
			if ( tracker ) {
				while ( ( match = findBug.exec( pull.title + pull.body ) ) ) {
					urls.push( tracker + match[ 1 ] );
				}
			}

			// Search just body for issues to add to the commit message
			while ( ( match = findBug.exec( pull.body ) ) ) {
				issues.push( " Fixes #" + match[ 1 ] );
			}

			// Add issues to the commit message
			msg += issues.join(",");

			if ( urls.length ) {
				msg += "\n\nMore Details:" + urls.map(function( url ) {
					return "\n - " + url;
				}).join("");
			}

			var commit = [ "commit", "-a", "--message=" + msg ];

			if ( config.interactive ) {
				commit.push("-e");
			}

			if ( author ) {
				commit.push( "--author=" + author );
			}

			getHEAD(function( oldCommit ) {
				// Thanks to: https://gist.github.com/927052
				spawn( "git", commit, {
					customFds: [ process.stdin, process.stdout, process.stderr ]
				}).on( "exit", function() {
					getHEAD(function( newCommit ) {
						if ( oldCommit === newCommit ) {
							reset("No commit, aborting push.");
						} else {
							exec( "git push " + config.remote + " " + base_branch, function( error, stdout, stderr ) {
								console.log( "done.".green );
								exit();
							});
						}
					});
				});
			});
		});
	}

	function callAPI( path, callback ) {
		request.get( "https://api.github.com" + path, {
			headers: {
				Authorization: "token " + token,
				"User-Agent": "Pulley " + pkg.version
			}
		}, function( err, res, body ) {
			var statusCode = res.socket._httpMessage.res.statusCode;

			if ( err ) {
				exit( err );
			}

			if ( statusCode === 404 ) {
				exit("Pull request doesn't exist");
			}

			if ( statusCode === 401 ) {
				login();
				return;
			}

			callback( body );
		});
	}

	function getHEAD( fn ) {
		exec( "git log | head -1", function( error, stdout, stderr ) {
			var commit = ( /commit (.*)/.exec( stdout ) || [] )[ 1 ];

			fn( commit );
		});
	}

	function reset( msg ) {
		console.error( ( "\n" + msg ).red );
		process.stderr.write( "Resetting files... ".red );

		exec( "git reset --hard ORIG_HEAD", function() {
			console.log( "done.".green );
			exit();
		});
	}

	function exit( msg ) {
		if ( msg ) {
			console.error( ( "\nError: " + msg ).red );
		}

		process.exit( 1 );
	}

})();
<|endoftext|>