<commit_before>var colors = require('colors')
var shell = require('shelljs')
var prompt = require('prompt')
var prompts = require('./prompts')
var spinner = require('../cli-helpers/spinner')
var clientFiles = require('./clientFiles')

/**
 * Handle errors from the cli prompts.
 * @param err
 * @returns {number}
 */
function onErr(err) {
  console.log(err)
  return 1
}

/**
 * Runs the spinner for a given amount of time. Currently this isn't being used for any kind of asynchronous action
 * but rather is there to improve the UX of the CLI too. After postinstall the terminal get's flooded with a list of
 * install modules, which hides the postinstall instructions.
 * @param {number} time - How long you want the spinner to run.
 */
function preventFinishFor(time) {
  spinner.start()
  setTimeout(function () {
    spinner.stop()
  }, time)
}

/**
 * An object of strings used for inserting the start script into the user's package.json file with
 * the `shell.sed` command.
 * @type {{flag: string, insertionPoint: string, addition: string, file: string}}
 */
var insertScript = {
  flag: '-i',
  insertionPoint: '"scripts": {',
  addition: "\"scripts\": { \n    \"start\": \"node node_modules/enclave/src/index.js\",",
  file: clientFiles.package
}

/**
 * Start the CLI prompt.
 */
prompt.start()

/**
 * Throw down that postinstall logo.
 */
shell.exec('bash ./node_modules/enclave/src/cli-helpers/postinstall-logo')

/**
 * Clean out any currently existing config file for a fresh one.
 */
shell.exec('rm ' + clientFiles.config)
shell.exec('touch ' + clientFiles.config)

function configureConfigFile(err, result) {
  for (var key in result) {
    if (key === 'port' && !result[key] || key === 'port' && result[key] !== result[key]) {
      shell.echo("exports." + key + " = 8080" + '\n').toEnd(clientFiles.config)
    } else {
      shell.echo("exports." + key + " = " + JSON.stringify(result[key]) + '\n').toEnd(clientFiles.config)
    }
  }
  if (err) {
    return onErr(err)
  }
  console.log('Here\'s what I\'ve got down, if something is wrong you can edit this in your enclave.js file.:'.yellow)
  console.log('  entry: '.red + result.entry.magenta)
  console.log('  output: '.red + result.output.magenta)
  console.log(!result.port ? '  port: '.red + '8080' : '  port: '.red + result.port)
  console.log('  index: '.red + result.index.magenta)
  console.log('  live: '.red + result.live)
  console.log('To run your app, just type'.green, '$ npm start'.bold.green)
  shell.sed(insertScript.flag, insertScript.insertionPoint, insertScript.addition, insertScript.file)
  preventFinishFor(5000)
}

prompt.get(prompts, configureConfigFile)
<commit_msg>fix cli-helper path<commit_after>var colors = require('colors')
var shell = require('shelljs')
var prompt = require('prompt')
var prompts = require('./prompts')
var spinner = require('../cli-helpers/spinner')
var clientFiles = require('./clientFiles')

/**
 * Handle errors from the cli prompts.
 * @param err
 * @returns {number}
 */
function onErr(err) {
  console.log(err)
  return 1
}

/**
 * Runs the spinner for a given amount of time. Currently this isn't being used for any kind of asynchronous action
 * but rather is there to improve the UX of the CLI too. After postinstall the terminal get's flooded with a list of
 * install modules, which hides the postinstall instructions.
 * @param {number} time - How long you want the spinner to run.
 */
function preventFinishFor(time) {
  spinner.start()
  setTimeout(function () {
    spinner.stop()
  }, time)
}

/**
 * An object of strings used for inserting the start script into the user's package.json file with
 * the `shell.sed` command.
 * @type {{flag: string, insertionPoint: string, addition: string, file: string}}
 */
var insertScript = {
  flag: '-i',
  insertionPoint: '"scripts": {',
  addition: "\"scripts\": { \n    \"start\": \"node node_modules/enclave/src/index.js\",",
  file: clientFiles.package
}

/**
 * Start the CLI prompt.
 */
prompt.start()

/**
 * Throw down that postinstall logo.
 */
shell.exec('bash ./src/cli-helpers/postinstall-logo')

/**
 * Clean out any currently existing config file for a fresh one.
 */
shell.exec('rm ' + clientFiles.config)
shell.exec('touch ' + clientFiles.config)

function configureConfigFile(err, result) {
  for (var key in result) {
    if (key === 'port' && !result[key] || key === 'port' && result[key] !== result[key]) {
      shell.echo("exports." + key + " = 8080" + '\n').toEnd(clientFiles.config)
    } else {
      shell.echo("exports." + key + " = " + JSON.stringify(result[key]) + '\n').toEnd(clientFiles.config)
    }
  }
  if (err) {
    return onErr(err)
  }
  console.log('Here\'s what I\'ve got down, if something is wrong you can edit this in your enclave.js file.:'.yellow)
  console.log('  entry: '.red + result.entry.magenta)
  console.log('  output: '.red + result.output.magenta)
  console.log(!result.port ? '  port: '.red + '8080' : '  port: '.red + result.port)
  console.log('  index: '.red + result.index.magenta)
  console.log('  live: '.red + result.live)
  console.log('To run your app, just type'.green, '$ npm start'.bold.green)
  shell.sed(insertScript.flag, insertScript.insertionPoint, insertScript.addition, insertScript.file)
  preventFinishFor(5000)
}

prompt.get(prompts, configureConfigFile)
<|endoftext|>


<commit_before>/**
*
* Wechaty - Wechat for Bot, and human who talk to bot.
*
* Inject this js code to browser,
* in order to interactive with wechat web program.
*
* Licenst: MIT
* https://github.com/zixia/wechaty-lib
*
*/
const log = require('npmlog')

class Bridge {
  constructor(options) {
    if (!options || !options.browser) { throw new Error('Bridge need a browser')}
    log.verbose('Bridge', `new Bridge({browser: ${options.browser}, port: ${options.port}})`)

    this.browser  = options.browser
    this.port     = options.port || 8788 // W(87) X(88), ascii char code ;-]
  }
  toString() { return `Class Bridge({browser: ${options.browser}, port: ${options.port}})` }

  init() {
    log.verbose('Bridge', 'init()')
    return this.inject()
  }

  logout()                  { return this.proxyWechaty('logout') }
  quit()                    { return this.proxyWechaty('quit') }
  getLoginStatusCode()      { return this.proxyWechaty('getLoginStatusCode') }
  getLoginQrImgUrl()        { return this.proxyWechaty('getLoginQrImgUrl') }
  getUserName()             { return this.proxyWechaty('getUserName') }

  getContact(id)            {
    return this.waitData(r => {
      return this.proxyWechaty('getContact', id)
    }, 3000)
  }

  /**
  * Call a function repeatly untill it return a value
  *
  * @param <function> pfunc
  * @param <number>   timeout
  *
  */
  waitData(pfunc, timeout) {
    log.silly('Bridge', 'waitData()')
    const waitTime  = 50
    let totalTime   = 0
    return new Promise((resolve, reject) => {
      function retry() {
        log.silly('Bridge', 'retry()@waitData()')
        try {
          pfunc().then(data => {
            if (data) {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) succ`)
              return resolve(data)
            } else if (totalTime > timeout) {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) timeout`)
              return resolve()
            } else {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) retry`)
              totalTime += waitTime
              return setTimeout(retry, waitTime)
            }
            throw new Error('should not run to here')
          })
        } catch (e) {
          log.silly('Bridge', `waitData(${totalTime}/${timeout}) exception: %s`, e)
          return reject(e)
        }
      }
      return retry()
    })
  }

  send(toUserName, content) { return this.proxyWechaty('send', toUserName, content) }

  getInjectio() {
    const fs = require('fs')
    const path = require('path')
    return fs.readFileSync(
      path.join(path.dirname(__filename), 'puppet-web-injectio.js')
      , 'utf8'
    )
  }
  inject() {
    log.verbose('Bridge', 'inject()')
    const injectio = this.getInjectio()
    try {
      return this.execute(injectio, this.port)
      .then(r => {
        log.verbose('Bridge', 'injected. initing...')
        return this.proxyWechaty('init')
      })
      .then(r => {
        if (true===r) { log.verbose('Bridge', 'Wechaty.init() return: ' + r) }
        else          { throw new Error('Wechaty.init() return not true： ' + r) }
        return r
      })
    } catch (e) {
      log.error('Bridge', 'inject() exception: %s', e)
      return Promise.reject('inject exception: ' + e)
    }
    throw new Error('should not run to here')
  }

  /**
   * Proxy Call to Wechaty in Bridge
   */
  proxyWechaty(wechatyFunc, ...args) {
    //const args      = Array.prototype.slice.call(arguments, 1)
    const argsJson  = JSON.stringify(args)
    const wechatyScript = `return (Wechaty && Wechaty.${wechatyFunc}.apply(undefined, JSON.parse('${argsJson}')))`

    log.silly('Bridge', 'proxyWechaty: ' + wechatyScript)
    return this.execute(wechatyScript)
  }
  execute(script, ...args) { return this.browser.execute(script, ...args) }
}

module.exports = Bridge
<commit_msg>add escape to json string before send to server<commit_after>/**
*
* Wechaty - Wechat for Bot, and human who talk to bot.
*
* Inject this js code to browser,
* in order to interactive with wechat web program.
*
* Licenst: MIT
* https://github.com/zixia/wechaty-lib
*
*/
const log = require('npmlog')

class Bridge {
  constructor(options) {
    if (!options || !options.browser) { throw new Error('Bridge need a browser')}
    log.verbose('Bridge', `new Bridge({browser: ${options.browser}, port: ${options.port}})`)

    this.browser  = options.browser
    this.port     = options.port || 8788 // W(87) X(88), ascii char code ;-]
  }
  toString() { return `Class Bridge({browser: ${options.browser}, port: ${options.port}})` }

  init() {
    log.verbose('Bridge', 'init()')
    return this.inject()
  }

  logout()                  { return this.proxyWechaty('logout') }
  quit()                    { return this.proxyWechaty('quit') }
  getLoginStatusCode()      { return this.proxyWechaty('getLoginStatusCode') }
  getLoginQrImgUrl()        { return this.proxyWechaty('getLoginQrImgUrl') }
  getUserName()             { return this.proxyWechaty('getUserName') }

  getContact(id)            {
    return this.waitData(r => {
      return this.proxyWechaty('getContact', id)
    }, 3000)
  }

  /**
  * Call a function repeatly untill it return a value
  *
  * @param <function> pfunc
  * @param <number>   timeout
  *
  */
  waitData(pfunc, timeout) {
    log.silly('Bridge', 'waitData()')
    const waitTime  = 50
    let totalTime   = 0
    return new Promise((resolve, reject) => {
      function retry() {
        log.silly('Bridge', 'retry()@waitData()')
        try {
          pfunc().then(data => {
            if (data) {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) succ`)
              return resolve(data)
            } else if (totalTime > timeout) {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) timeout`)
              return resolve()
            } else {
              log.silly('Bridge', `waitData(${totalTime}/${timeout}) retry`)
              totalTime += waitTime
              return setTimeout(retry, waitTime)
            }
            throw new Error('should not run to here')
          })
        } catch (e) {
          log.silly('Bridge', `waitData(${totalTime}/${timeout}) exception: %s`, e)
          return reject(e)
        }
      }
      return retry()
    })
  }

  send(toUserName, content) { return this.proxyWechaty('send', toUserName, content) }

  getInjectio() {
    const fs = require('fs')
    const path = require('path')
    return fs.readFileSync(
      path.join(path.dirname(__filename), 'puppet-web-injectio.js')
      , 'utf8'
    )
  }
  inject() {
    log.verbose('Bridge', 'inject()')
    const injectio = this.getInjectio()
    try {
      return this.execute(injectio, this.port)
      .then(r => {
        log.verbose('Bridge', 'injected. initing...')
        return this.proxyWechaty('init')
      })
      .then(r => {
        if (true===r) { log.verbose('Bridge', 'Wechaty.init() return: ' + r) }
        else          { throw new Error('Wechaty.init() return not true： ' + r) }
        return r
      })
    } catch (e) {
      log.error('Bridge', 'inject() exception: %s', e)
      return Promise.reject('inject exception: ' + e)
    }
    throw new Error('should not run to here')
  }

  /**
   * Proxy Call to Wechaty in Bridge
   */
  proxyWechaty(wechatyFunc, ...args) {
    function escape (key, val) {
     if (typeof(val)!="string") return val;
      return val
        .replace(/[\\]/g, '\\\\')
        .replace(/[\/]/g, '\\/')
        .replace(/[\b]/g, '\\b')
        .replace(/[\f]/g, '\\f')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r')
        .replace(/[\t]/g, '\\t')
        .replace(/[\"]/g, '\\"')
        .replace(/\\'/g, "\\'")
    } // http://stackoverflow.com/a/14137856/1123955
    const argsJson  = JSON.stringify(args, escape)

    const wechatyScript = `return (Wechaty && Wechaty.${wechatyFunc}.apply(undefined, JSON.parse('${argsJson}')))`

    log.silly('Bridge', 'proxyWechaty: ' + wechatyScript)
    return this.execute(wechatyScript)
  }
  execute(script, ...args) { return this.browser.execute(script, ...args) }
}

module.exports = Bridge
<|endoftext|>


<commit_before>import { md5Hash, newObjectId } from './cryptoUtils';
import { logger } from './logger';

export function flatten(array) {
  return array.reduce((memo, element) => {
    if (Array.isArray(element)) {
      memo = memo.concat(flatten(element));
    } else {
      memo = memo.concat(element);
    }
    return memo;
  }, []);
}

export default function pushStatusHandler(config) {

  let initialPromise;
  let pushStatus;
  let objectId = newObjectId();
  let collection = function() {
    return config.database.adaptiveCollection('_PushStatus');
  }

  let setInitial = function(body = {}, where, options = {source: 'rest'}) {
    let now = new Date();
    let data =  body.data || {};
    let object = {
      _id: objectId,
      objectId,
      pushTime: now.toISOString(),
      _created_at: now,
      query: JSON.stringify(where),
      payload: body.data,
      source: options.source,
      title: options.title,
      expiry: body.expiration_time,
      status: "pending",
      numSent: 0,
      pushHash: md5Hash(JSON.stringify(data)),
      // lockdown!
      _wperm: [],
      _rperm: []
    }
    initialPromise = collection().then((collection) => {
      return collection.insertOne(object);
    }).then((res) => {
      pushStatus = {
        objectId: object.objectId
      };
      return Promise.resolve(pushStatus);
    })
    return initialPromise;
  }

  let setRunning = function(installations) {
    logger.verbose('sending push to %d installations', installations.length);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({status:"pending", objectId: pushStatus.objectId}, {$set: {status: "running"}});
   });
  }

  let complete = function(results) {
    let update = {
      status: 'succeeded',
      numSent: 0,
      numFailed: 0,
    };
    if (Array.isArray(results)) {
      results = flatten(results);
      results.reduce((memo, result) => {
        // Cannot handle that
        if (!result.device || !result.device.deviceType) {
          return memo;
        }
        let deviceType = result.device.deviceType;
        if (result.transmitted)
        {
          memo.numSent++;
          memo.sentPerType = memo.sentPerType || {};
          memo.sentPerType[deviceType] = memo.sentPerType[deviceType] || 0;
          memo.sentPerType[deviceType]++;
        } else {
          memo.numFailed++;
          memo.failedPerType = memo.failedPerType || {};
          memo.failedPerType[deviceType] = memo.failedPerType[deviceType] || 0;
          memo.failedPerType[deviceType]++;
        }
        return memo;
      }, update);
    }
    logger.verbose('sent push! %d success, %d failures', update.numSent, update.numFailed);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({status:"running", objectId: pushStatus.objectId}, {$set: update});
    });
  }

  let fail = function(err) {
    let update = {
      errorMessage: JSON.stringify(err),
      status: 'failed'
    }
    logger.error('error while sending push', err);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({objectId: pushStatus.objectId}, {$set: update});
    });
  }

  return Object.freeze({
    objectId,
    setInitial,
    setRunning,
    complete,
    fail
  })
}
<commit_msg>stringify push payload in event that payload contains JSON<commit_after>import { md5Hash, newObjectId } from './cryptoUtils';
import { logger } from './logger';

export function flatten(array) {
  return array.reduce((memo, element) => {
    if (Array.isArray(element)) {
      memo = memo.concat(flatten(element));
    } else {
      memo = memo.concat(element);
    }
    return memo;
  }, []);
}

export default function pushStatusHandler(config) {

  let initialPromise;
  let pushStatus;
  let objectId = newObjectId();
  let collection = function() {
    return config.database.adaptiveCollection('_PushStatus');
  }

  let setInitial = function(body = {}, where, options = {source: 'rest'}) {
    let now = new Date();
    let data =  body.data || {};
    let object = {
      _id: objectId,
      objectId,
      pushTime: now.toISOString(),
      _created_at: now,
      query: JSON.stringify(where),
      payload: JSON.stringify(body.data),
      source: options.source,
      title: options.title,
      expiry: body.expiration_time,
      status: "pending",
      numSent: 0,
      pushHash: md5Hash(JSON.stringify(data)),
      // lockdown!
      _wperm: [],
      _rperm: []
    }
    initialPromise = collection().then((collection) => {
      return collection.insertOne(object);
    }).then((res) => {
      pushStatus = {
        objectId: object.objectId
      };
      return Promise.resolve(pushStatus);
    })
    return initialPromise;
  }

  let setRunning = function(installations) {
    logger.verbose('sending push to %d installations', installations.length);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({status:"pending", objectId: pushStatus.objectId}, {$set: {status: "running"}});
   });
  }

  let complete = function(results) {
    let update = {
      status: 'succeeded',
      numSent: 0,
      numFailed: 0,
    };
    if (Array.isArray(results)) {
      results = flatten(results);
      results.reduce((memo, result) => {
        // Cannot handle that
        if (!result.device || !result.device.deviceType) {
          return memo;
        }
        let deviceType = result.device.deviceType;
        if (result.transmitted)
        {
          memo.numSent++;
          memo.sentPerType = memo.sentPerType || {};
          memo.sentPerType[deviceType] = memo.sentPerType[deviceType] || 0;
          memo.sentPerType[deviceType]++;
        } else {
          memo.numFailed++;
          memo.failedPerType = memo.failedPerType || {};
          memo.failedPerType[deviceType] = memo.failedPerType[deviceType] || 0;
          memo.failedPerType[deviceType]++;
        }
        return memo;
      }, update);
    }
    logger.verbose('sent push! %d success, %d failures', update.numSent, update.numFailed);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({status:"running", objectId: pushStatus.objectId}, {$set: update});
    });
  }

  let fail = function(err) {
    let update = {
      errorMessage: JSON.stringify(err),
      status: 'failed'
    }
    logger.error('error while sending push', err);
    return initialPromise.then(() => {
      return collection();
    }).then((collection) => {
      return collection.updateOne({objectId: pushStatus.objectId}, {$set: update});
    });
  }

  return Object.freeze({
    objectId,
    setInitial,
    setRunning,
    complete,
    fail
  })
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Atom will evaluate this file each time a new window is opened. It is run
// after packages are loaded/activated and after the previous editor state
// has been restored.
////////////////////////////////////////////////////////////////////////////////

// Toggle between light and dark theme.
atom.commands.add('atom-workspace', 'me:toggle-theme', function () {
  const activeThemes = atom.themes.getActiveThemeNames()

  if (activeThemes[0].indexOf('light') > 0) {
    atom.config.set('core.themes', ['one-dark-ui', 'one-dark-syntax'])
  } else {
    atom.config.set('core.themes', ['one-light-ui', 'one-light-syntax'])
  }
})

// Close all panes.
//
// Atom's built-in 'pane:close-other-items' is super handy. But sometimes you
// just really want a completely clean slate.
//
// "Shut down all the [panes] on the detention level! ... Shut them all down.
// Hurry!"
atom.commands.add('atom-workspace', 'me:close-all-panes', () =>
  atom.workspace.getPanes().forEach(pane => pane.destroy())
)

// Share a Teletype portal to the workspace and copy the portal URL to the
// clipboard for easy sharing.
//
// Overrides Teletype's default 'Share Portal' command.
atom.commands.add('atom-workspace', 'teletype:share-portal', async () => {
  const teletype = atom.packages.getLoadedPackage('teletype').mainModule
  await teletype.sharePortal()
  atom.commands.dispatch(atom.workspace.getElement(), 'teletype:copy-portal-url')
  atom.notifications.addSuccess('Your portal is open for business', {
    description: "Invite people to collaborate with you using your portal URL. It's already on your clipboard. 👌"
  })
})

// Open the Settings View for Teletype.
atom.commands.add('atom-workspace', 'teletype:open-settings', async () => {
  atom.workspace.open(`atom://config/packages/teletype`)
})

// If the right dock is focused, close it. If the right dock is not focused,
// display the right dock and focus it.
//
// I bind this command a keyboard shortcut so that I can use a single keyboard
// shortcut to go straight to the Git tab from anywhere inside Atom, and then use
// that same keyboard shortcut to close the Git tab when I'm done committing my
// changes.
atom.commands.add('atom-workspace', 'me:focus-or-close-right-dock', function () {
  const workspaceActivePaneItem = atom.workspace.getActivePaneItem()
  const rightDockActivePaneItem = atom.workspace.getRightDock().getActivePaneItem()
  const isRightDockFocused = workspaceActivePaneItem === rightDockActivePaneItem

  if (isRightDockFocused) {
    atom.commands.dispatch(atom.workspace.getElement(), 'window:toggle-right-dock')
  } else {
    atom.workspace.getRightDock().activate()
  }
})

// Save all open buffers, even if they're not modified. This behavior is useful
// when you're using a file watcher to trigger scripts (e.g., using guard to
// run your Ruby tests).
//
// Atom's built-in "window:save-all" command previously provided this behavior,
// but it was removed in https://github.com/atom/atom/pull/9249.
atom.commands.add('atom-workspace', 'me:save-all', () =>
  atom.workspace.getPanes().forEach(pane => pane.getItems().map((item) => pane.saveItem(item)))
)

// Generate ctags.
atom.commands.add('atom-text-editor', 'me:generate-ctags', () => {
  let child
  const sys = require('sys')
  const { exec } = require('child_process')

  const tag_command = `    \
    /usr/local/bin/ctags   \
    --exclude=log          \
    --exclude=node_modules \
    --exclude=tmp          \
    --exclude=vendor       \
    --tag-relative -R      \
    `

  atom.notifications.addInfo('Generating ctags')
  atom.project.getPaths().map((path) =>
    (child = exec(tag_command, {cwd: path}, function (error, stdout, stderr) {
      if (error) {
        atom.notifications.addError('ctags exited with error',
          {detail: error, dismissable: true}
        )
      } else if (stderr) {
        atom.notifications.addWarning('Generated ctags (with warnings)',
          {detail: stderr, dismissable: true}
        )
      } else {
        atom.notifications.addSuccess('Finished generating ctags',
          {detail: stdout}
        )
      }
    })
  ))
})

// Define commands that should only be available in an editor view (and should
// not be available in a tree view, for example).
atom.commands.add('atom-text-editor', {
  // Approximate Vim's "H" motion: Move the cursor to the topmost line that is
  // currently visible.
  'me:move-to-top-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    let topRow = editor.getFirstVisibleScreenRow()
    if (topRow !== 0) { topRow = topRow + 1 }
    editor.setCursorBufferPosition([topRow, 0], {autoscroll: false})
  },

  // Approximate Vim's "M" motion: Move the cursor to the middle line that is
  // currently visible.
  'me:move-to-middle-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    const topRow = editor.getFirstVisibleScreenRow()
    const bottomRow = editor.getLastVisibleScreenRow()
    const middleRow = Math.floor((bottomRow - topRow) / 2) + topRow
    editor.setCursorBufferPosition([middleRow, 0])
  },

  // Approximate Vim's "L" motion: Move the cursor to the bottommost line that
  // is currently visible.
  'me:move-to-bottom-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    let bottomRow = editor.getLastVisibleScreenRow()
    if (bottomRow !== editor.getLastScreenRow()) { bottomRow = bottomRow - 1 }
    editor.setCursorBufferPosition([bottomRow, 0], {autoscroll: false})
  },

  // Approximate Vim's "zt" motion: Scroll the view such that the line containing
  // the cursor is at the top of the view.
  'me:scroll-cursor-to-top' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const pixelPositionForCursorPosition =
      textEditorElement.pixelPositionForScreenPosition(cursorPosition)
    textEditorElement.setScrollTop(pixelPositionForCursorPosition.top)
  },

  // Approximate Vim's "zz" motion: Scroll the view such that the line containing
  // the cursor is vertically centered in the view.
  'me:scroll-cursor-to-center' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const pixelPositionForCursorPosition =
      textEditorElement.pixelPositionForScreenPosition(cursorPosition)
    const halfScreenHeight = textEditorElement.getHeight() / 2
    const scrollTop = pixelPositionForCursorPosition.top - halfScreenHeight
    textEditorElement.setScrollTop(scrollTop)
  },

  // Approximate Vim's "zb" motion: Scroll the view such that the line containing
  // the cursor is at the bottom of the view.
  'me:scroll-cursor-to-bottom' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const scrollBottomPosition = [cursorPosition.row + 1, cursorPosition.column]
    const pixelPositionForScrollBottomPosition =
      textEditorElement.pixelPositionForScreenPosition(scrollBottomPosition)
    textEditorElement.setScrollBottom(pixelPositionForScrollBottomPosition.top)
  },

  // Move the cursor to the next diff in the editor, and then scroll the view
  // such that the line containing the cursor is vertically centered in the view.
  'me:center-next-diff' () {
    const editor = atom.workspace.getActiveTextEditor()
    const editorView = atom.views.getView(editor)
    atom.commands.dispatch(editorView, 'git-diff:move-to-next-diff')
    atom.commands.dispatch(editorView, 'me:scroll-cursor-to-center')
  },

  // Move the cursor to the previous diff in the editor, and then scroll the view
  // such that the line containing the cursor is vertically centered in the view.
  'me:center-previous-diff' () {
    const editor = atom.workspace.getActiveTextEditor()
    const editorView = atom.views.getView(editor)
    atom.commands.dispatch(editorView, 'git-diff:move-to-previous-diff')
    atom.commands.dispatch(editorView, 'me:scroll-cursor-to-center')
  }
})

function focusTracer (event) {
  console.log('window.focus =', event.target)
}

atom.commands.add('atom-workspace', {
  // Log each time focus changes to a new element in Atom's UI.
  'me:trace-focus' (event) {
    window.addEventListener('focusin', focusTracer)
  },

  // Stop logging each time focus changes to a new element in Atom's UI.
  'me:untrace-focus' (event) {
    window.removeEventListener('focusin', focusTracer)
  }
})
<commit_msg>:atom: Use monokai as my dark syntax theme<commit_after>////////////////////////////////////////////////////////////////////////////////
// Atom will evaluate this file each time a new window is opened. It is run
// after packages are loaded/activated and after the previous editor state
// has been restored.
////////////////////////////////////////////////////////////////////////////////

// Toggle between light and dark theme.
atom.commands.add('atom-workspace', 'me:toggle-theme', function () {
  const activeThemes = atom.themes.getActiveThemeNames()

  if (activeThemes[0].indexOf('light') > 0) {
    atom.config.set('core.themes', ['one-dark-ui', 'monokai'])
  } else {
    atom.config.set('core.themes', ['one-light-ui', 'one-light-syntax'])
  }
})

// Close all panes.
//
// Atom's built-in 'pane:close-other-items' is super handy. But sometimes you
// just really want a completely clean slate.
//
// "Shut down all the [panes] on the detention level! ... Shut them all down.
// Hurry!"
atom.commands.add('atom-workspace', 'me:close-all-panes', () =>
  atom.workspace.getPanes().forEach(pane => pane.destroy())
)

// Share a Teletype portal to the workspace and copy the portal URL to the
// clipboard for easy sharing.
//
// Overrides Teletype's default 'Share Portal' command.
atom.commands.add('atom-workspace', 'teletype:share-portal', async () => {
  const teletype = atom.packages.getLoadedPackage('teletype').mainModule
  await teletype.sharePortal()
  atom.commands.dispatch(atom.workspace.getElement(), 'teletype:copy-portal-url')
  atom.notifications.addSuccess('Your portal is open for business', {
    description: "Invite people to collaborate with you using your portal URL. It's already on your clipboard. 👌"
  })
})

// Open the Settings View for Teletype.
atom.commands.add('atom-workspace', 'teletype:open-settings', async () => {
  atom.workspace.open(`atom://config/packages/teletype`)
})

// If the right dock is focused, close it. If the right dock is not focused,
// display the right dock and focus it.
//
// I bind this command a keyboard shortcut so that I can use a single keyboard
// shortcut to go straight to the Git tab from anywhere inside Atom, and then use
// that same keyboard shortcut to close the Git tab when I'm done committing my
// changes.
atom.commands.add('atom-workspace', 'me:focus-or-close-right-dock', function () {
  const workspaceActivePaneItem = atom.workspace.getActivePaneItem()
  const rightDockActivePaneItem = atom.workspace.getRightDock().getActivePaneItem()
  const isRightDockFocused = workspaceActivePaneItem === rightDockActivePaneItem

  if (isRightDockFocused) {
    atom.commands.dispatch(atom.workspace.getElement(), 'window:toggle-right-dock')
  } else {
    atom.workspace.getRightDock().activate()
  }
})

// Save all open buffers, even if they're not modified. This behavior is useful
// when you're using a file watcher to trigger scripts (e.g., using guard to
// run your Ruby tests).
//
// Atom's built-in "window:save-all" command previously provided this behavior,
// but it was removed in https://github.com/atom/atom/pull/9249.
atom.commands.add('atom-workspace', 'me:save-all', () =>
  atom.workspace.getPanes().forEach(pane => pane.getItems().map((item) => pane.saveItem(item)))
)

// Generate ctags.
atom.commands.add('atom-text-editor', 'me:generate-ctags', () => {
  let child
  const sys = require('sys')
  const { exec } = require('child_process')

  const tag_command = `    \
    /usr/local/bin/ctags   \
    --exclude=log          \
    --exclude=node_modules \
    --exclude=tmp          \
    --exclude=vendor       \
    --tag-relative -R      \
    `

  atom.notifications.addInfo('Generating ctags')
  atom.project.getPaths().map((path) =>
    (child = exec(tag_command, {cwd: path}, function (error, stdout, stderr) {
      if (error) {
        atom.notifications.addError('ctags exited with error',
          {detail: error, dismissable: true}
        )
      } else if (stderr) {
        atom.notifications.addWarning('Generated ctags (with warnings)',
          {detail: stderr, dismissable: true}
        )
      } else {
        atom.notifications.addSuccess('Finished generating ctags',
          {detail: stdout}
        )
      }
    })
  ))
})

// Define commands that should only be available in an editor view (and should
// not be available in a tree view, for example).
atom.commands.add('atom-text-editor', {
  // Approximate Vim's "H" motion: Move the cursor to the topmost line that is
  // currently visible.
  'me:move-to-top-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    let topRow = editor.getFirstVisibleScreenRow()
    if (topRow !== 0) { topRow = topRow + 1 }
    editor.setCursorBufferPosition([topRow, 0], {autoscroll: false})
  },

  // Approximate Vim's "M" motion: Move the cursor to the middle line that is
  // currently visible.
  'me:move-to-middle-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    const topRow = editor.getFirstVisibleScreenRow()
    const bottomRow = editor.getLastVisibleScreenRow()
    const middleRow = Math.floor((bottomRow - topRow) / 2) + topRow
    editor.setCursorBufferPosition([middleRow, 0])
  },

  // Approximate Vim's "L" motion: Move the cursor to the bottommost line that
  // is currently visible.
  'me:move-to-bottom-visible-line' () {
    const editor = atom.workspace.getActiveTextEditor()
    let bottomRow = editor.getLastVisibleScreenRow()
    if (bottomRow !== editor.getLastScreenRow()) { bottomRow = bottomRow - 1 }
    editor.setCursorBufferPosition([bottomRow, 0], {autoscroll: false})
  },

  // Approximate Vim's "zt" motion: Scroll the view such that the line containing
  // the cursor is at the top of the view.
  'me:scroll-cursor-to-top' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const pixelPositionForCursorPosition =
      textEditorElement.pixelPositionForScreenPosition(cursorPosition)
    textEditorElement.setScrollTop(pixelPositionForCursorPosition.top)
  },

  // Approximate Vim's "zz" motion: Scroll the view such that the line containing
  // the cursor is vertically centered in the view.
  'me:scroll-cursor-to-center' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const pixelPositionForCursorPosition =
      textEditorElement.pixelPositionForScreenPosition(cursorPosition)
    const halfScreenHeight = textEditorElement.getHeight() / 2
    const scrollTop = pixelPositionForCursorPosition.top - halfScreenHeight
    textEditorElement.setScrollTop(scrollTop)
  },

  // Approximate Vim's "zb" motion: Scroll the view such that the line containing
  // the cursor is at the bottom of the view.
  'me:scroll-cursor-to-bottom' () {
    const textEditor = atom.workspace.getActiveTextEditor()
    const textEditorElement = atom.views.getView(textEditor)
    const cursorPosition = textEditor.getCursorScreenPosition()
    const scrollBottomPosition = [cursorPosition.row + 1, cursorPosition.column]
    const pixelPositionForScrollBottomPosition =
      textEditorElement.pixelPositionForScreenPosition(scrollBottomPosition)
    textEditorElement.setScrollBottom(pixelPositionForScrollBottomPosition.top)
  },

  // Move the cursor to the next diff in the editor, and then scroll the view
  // such that the line containing the cursor is vertically centered in the view.
  'me:center-next-diff' () {
    const editor = atom.workspace.getActiveTextEditor()
    const editorView = atom.views.getView(editor)
    atom.commands.dispatch(editorView, 'git-diff:move-to-next-diff')
    atom.commands.dispatch(editorView, 'me:scroll-cursor-to-center')
  },

  // Move the cursor to the previous diff in the editor, and then scroll the view
  // such that the line containing the cursor is vertically centered in the view.
  'me:center-previous-diff' () {
    const editor = atom.workspace.getActiveTextEditor()
    const editorView = atom.views.getView(editor)
    atom.commands.dispatch(editorView, 'git-diff:move-to-previous-diff')
    atom.commands.dispatch(editorView, 'me:scroll-cursor-to-center')
  }
})

function focusTracer (event) {
  console.log('window.focus =', event.target)
}

atom.commands.add('atom-workspace', {
  // Log each time focus changes to a new element in Atom's UI.
  'me:trace-focus' (event) {
    window.addEventListener('focusin', focusTracer)
  },

  // Stop logging each time focus changes to a new element in Atom's UI.
  'me:untrace-focus' (event) {
    window.removeEventListener('focusin', focusTracer)
  }
})
<|endoftext|>


<commit_before>// Your init script
//
// Atom will evaluate this file each time a new window is opened. It is run
// after packages are loaded/activated and after the previous editor state
// has been restored.

const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');

// Sync Atom packages via package-sync package
function syncPackages() {
  const workspaceView = atom.views.getView(atom.workspace);
  return atom.commands.dispatch(workspaceView, 'package-sync:sync');
};

// Set preferred size of current Atom window while preserving window position;
// assumes Tree View is open
function setPreferredWindowDimensions() {
  const originalWindowDimensions = atom.getWindowDimensions();
  atom.setWindowDimensions({
    width: 1080,
    height: 710,
    x: originalWindowDimensions.x,
    y: originalWindowDimensions.y
  });
};

// Add command for revealing the project folder at the workspace level
atom.commands.add('atom-workspace', 'application:show-project-folder-in-file-manager', () => {
  const projectPaths = atom.project.getPaths();
  if (projectPaths.length !== 0) {
    exec(`open ${projectPaths[0]}`);
  }
});

// Override getGrammars to work around bug with tree-sitter grammars never
// applying; see
// <https://github.com/atom/atom/issues/17029#issuecomment-457084440>
atom.grammars.getGrammars = () => {
  const allGrammars = atom.grammars.textmateRegistry.getGrammars();
  const tsGrammars = Object.values(atom.grammars.treeSitterGrammarsById);
  const combinedGrammars = tsGrammars.concat(allGrammars);
  const combinedGrammarNames = combinedGrammars.map(grammar => grammar.name);
  return combinedGrammars.filter((grammar, g) => {
    return (
      // Prefer tree-sitter grammars whenever possible
      grammar.constructor.name === 'TreeSitterGrammar'
      // OR, if this is a first-mate grammar without a corresponding tree-sitter
      // grammar, add it to the list
      || combinedGrammarNames.indexOf(grammar.name) === g
    );
  });
};

// Add command to copy to clipboard an array of the editor's current cursor
// scopes
atom.commands.add('atom-text-editor:not([mini])', 'editor:copy-cursor-scope', () => {
  const editor = atom.workspace.getActiveTextEditor();
  const scopes = editor.getCursorScope().scopes
  if (scopes) {
    const scopeString = JSON.stringify(scopes)
      .replace(/,/g, ', ')
      .replace(/"/g, '\'');
    atom.clipboard.write(scopeString);
    const list = scopes.map(item => `* ${item}`);
    const content = `Copied Scopes at Cursor\n${list.join('\n')}`;
    atom.notifications.addInfo(content, {
      dismissable: true
    });
  } else {
    atom.notifications.addError('Scopes at Cursor', {
      detail: "Could not write scopes to clipboard"
    });
  }
});

setPreferredWindowDimensions();
syncPackages();
<commit_msg>Use > 0 check instead of !== 0<commit_after>// Your init script
//
// Atom will evaluate this file each time a new window is opened. It is run
// after packages are loaded/activated and after the previous editor state
// has been restored.

const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');

// Sync Atom packages via package-sync package
function syncPackages() {
  const workspaceView = atom.views.getView(atom.workspace);
  return atom.commands.dispatch(workspaceView, 'package-sync:sync');
};

// Set preferred size of current Atom window while preserving window position;
// assumes Tree View is open
function setPreferredWindowDimensions() {
  const originalWindowDimensions = atom.getWindowDimensions();
  atom.setWindowDimensions({
    width: 1080,
    height: 710,
    x: originalWindowDimensions.x,
    y: originalWindowDimensions.y
  });
};

// Add command for revealing the project folder at the workspace level
atom.commands.add('atom-workspace', 'application:show-project-folder-in-file-manager', () => {
  const projectPaths = atom.project.getPaths();
  if (projectPaths.length > 0) {
    exec(`open ${projectPaths[0]}`);
  }
});

// Override getGrammars to work around bug with tree-sitter grammars never
// applying; see
// <https://github.com/atom/atom/issues/17029#issuecomment-457084440>
atom.grammars.getGrammars = () => {
  const allGrammars = atom.grammars.textmateRegistry.getGrammars();
  const tsGrammars = Object.values(atom.grammars.treeSitterGrammarsById);
  const combinedGrammars = tsGrammars.concat(allGrammars);
  const combinedGrammarNames = combinedGrammars.map(grammar => grammar.name);
  return combinedGrammars.filter((grammar, g) => {
    return (
      // Prefer tree-sitter grammars whenever possible
      grammar.constructor.name === 'TreeSitterGrammar'
      // OR, if this is a first-mate grammar without a corresponding tree-sitter
      // grammar, add it to the list
      || combinedGrammarNames.indexOf(grammar.name) === g
    );
  });
};

// Add command to copy to clipboard an array of the editor's current cursor
// scopes
atom.commands.add('atom-text-editor:not([mini])', 'editor:copy-cursor-scope', () => {
  const editor = atom.workspace.getActiveTextEditor();
  const scopes = editor.getCursorScope().scopes
  if (scopes) {
    const scopeString = JSON.stringify(scopes)
      .replace(/,/g, ', ')
      .replace(/"/g, '\'');
    atom.clipboard.write(scopeString);
    const list = scopes.map(item => `* ${item}`);
    const content = `Copied Scopes at Cursor\n${list.join('\n')}`;
    atom.notifications.addInfo(content, {
      dismissable: true
    });
  } else {
    atom.notifications.addError('Scopes at Cursor', {
      detail: "Could not write scopes to clipboard"
    });
  }
});

setPreferredWindowDimensions();
syncPackages();
<|endoftext|>


<commit_before>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    var
        /**
         * Generates a read-only property accessor
         *
         * @param {String} member
         * @return {Function}
         * @closure
         */
        _roProperty = function (member) {
            return gpf._func("return this." + member + ";");
        },

        /**
         * Generates a property accessor
         *
         * @param {String} member
         * @return {Function}
         * @closure
         */
        _rwProperty = function (member) {
            return gpf._func("var r = this." + member
                + "; if (0 < arguments.length) { this." + member
                + " = arguments[0]; } return r;");
        },

        /**
         * Base class for class-specific attributes
         *
         * @class gpf.attributes.ClassAttribute
         * @extends gpf.attributes.Attribute
         */
        _base = gpf._defAttr("ClassAttribute");

    /**
     * Creates getter (and setter) methods for a private member. The created
     * accessor is a method with the following signature:
     * {type} MEMBER({type} [value=undefined] value)
     * When value is not set, the member acts as a getter
     *
     *
     * @class gpf.attributes.ClassPropertyAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassProperty
     */
    gpf._defAttr("$ClassProperty", _base, {

        private: {

            /**
             * If true, generates a write wrapper
             *
             * @type {Boolean}
             * @private
             */
            _writeAllowed: false,

            /**
             * If set, provides the member name. Otherwise, name is based on
             * member.
             *
             * @type {String|undefined}
             * @private
             */
            _publicName: undefined,

            /**
             * If set, provides the member visibility.
             * Default is 'public'
             * member.
             *
             * @type {String|undefined}
             * @private
             */
            _visibility: undefined

        },

        protected: {

            /**
             * @inheritdoc gpf.attributes.Attribute:_alterPrototype
             */
            _alterPrototype: function (objPrototype) {
                var
                    member = this._member,
                    publicName = this._publicName,
                    classDef = gpf.classDef(objPrototype.constructor),
                    accessor;
                if (!publicName) {
                    publicName = member.substr(1); // starts with _
                }
                if (this._writeAllowed) {
                    accessor = _rwProperty(member);
                } else {
                    accessor = _roProperty(member);
                }
                classDef.addMember(publicName, accessor, this._visibility);
            }

        },

        public: {

            /**
             * @param {Boolean} writeAllowed
             * @param {String} [publicName=undefined] publicName When not
             * specified, the member name (without _) is applied
             * @param {String} [visibility=undefined] visibility When not
             * specified, public is used
             * @constructor
             */
            constructor: function (writeAllowed, publicName, visibility) {
                if (writeAllowed) {
                    this._writeAllowed = true;
                }
                if ("string" === typeof publicName) {
                    this._publicName = publicName;
                }
                if ("string" === typeof visibility) {
                    this._visibility = visibility;
                }
            }

        }

    });

    /**
     * Used to flag a method which owns a last parameter being an event handler
     *
     * @class gpf.attributes.ClassEventHandlerAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassEventHandler
     */
    gpf._defAttr("$ClassEventHandler", _base, {});

    /**
     * Defines a class extension (internal)
     *
     * @param {String} ofClass
     * @param {String} [publicName=undefined] publicName When not specified,
     * the original method name is used
     *
     * @class gpf.attributes.ClassExtensionAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassExtension
     */
    gpf._defAttr("$ClassExtension", _base, {

        private: {

            /**
             * Constructor of the class to extend
             *
             * @type {Function}
             * @private
             */
            _ofClass: gpf.emptyFunction(),

            /**
             * Name of the method if added to the class
             *
             * @type {String}
             * @private
             */
            _publicName: ""

        },

        public: {

            /**
             * @param {Function} ofClass Constructor of the class to extend
             * @param {String} publicName Name of the method if added to the
             * class
             * @constructor
             */
            constructor: function (ofClass, publicName) {
                this._ofClass = ofClass;
                if ("string" === typeof publicName) {
                    this._publicName = publicName;
                }
            }

        }

    });

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<commit_msg>R/O & R/W properties have a name<commit_after>/*#ifndef(UMD)*/
(function () { /* Begin of privacy scope */
    "use strict";
/*#endif*/

    /*global _NAME_:true*/

    var
        /**
         * Read-only property accessor template
         *
         * @return {*}
         * @closure
         */
        _roProperty = function () {
            /*jshint -W120*/
            var template = _NAME_ = function () {
                return this._MEMBER_;
            };
            return template;
        },

        /**
         * Property accessor template
         *
         * @return {Function}
         * @closure
         */
        _rwProperty = function () {
            /*jshint -W120*/
            var template = _NAME_ = function () {
                var result = this._MEMBER_;
                if (0 < arguments.length) {
                    this._MEMBER_ = arguments[0];
                }
                return result;
            };
            return template;
        },

        /**
         * Base class for class-specific attributes
         *
         * @class gpf.attributes.ClassAttribute
         * @extends gpf.attributes.Attribute
         */
        _base = gpf._defAttr("ClassAttribute");

    /**
     * Creates getter (and setter) methods for a private member. The created
     * accessor is a method with the following signature:
     * {type} MEMBER({type} [value=undefined] value)
     * When value is not set, the member acts as a getter
     *
     *
     * @class gpf.attributes.ClassPropertyAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassProperty
     */
    gpf._defAttr("$ClassProperty", _base, {

        private: {

            /**
             * If true, generates a write wrapper
             *
             * @type {Boolean}
             * @private
             */
            _writeAllowed: false,

            /**
             * If set, provides the member name. Otherwise, name is based on
             * member.
             *
             * @type {String|undefined}
             * @private
             */
            _publicName: undefined,

            /**
             * If set, provides the member visibility.
             * Default is 'public'
             * member.
             *
             * @type {String|undefined}
             * @private
             */
            _visibility: undefined

        },

        protected: {

            /**
             * @inheritdoc gpf.attributes.Attribute:_alterPrototype
             */
            _alterPrototype: function (objPrototype) {
                var
                    member = this._member,
                    publicName = this._publicName,
                    classDef = gpf.classDef(objPrototype.constructor),
                    src,
                    start,
                    end;
                if (!publicName) {
                    publicName = member.substr(1); // starts with _
                }
                if (this._writeAllowed) {
                    src = _rwProperty.toString();
                } else {
                    src = _roProperty.toString();
                }
                // Replace all occurrences of _MEMBER_ zith the right name
                src = src.split("_MEMBER_").join(member);
                // Do the same for _NAME_ to customize accessor name
                src = src.replace("_NAME_", classDef.name() + "." + publicName);
                // Extract content of resulting function source
                start = src.indexOf("{") + 1;
                end = src.lastIndexOf("}") - 1;
                src =  src.substr(start, end - start + 1);
                /**
                 * If the classDef name is not a namespace, defines an empty
                 * object to allow the use of Name.member
                 */
                if (-1 === classDef.name().indexOf(".")) {
                    src = "var " + classDef.name() + " = {};\r\n" + src;
                }
                classDef.addMember(publicName, gpf._func(src)(),
                    this._visibility);
            }

        },

        public: {

            /**
             * @param {Boolean} writeAllowed
             * @param {String} [publicName=undefined] publicName When not
             * specified, the member name (without _) is applied
             * @param {String} [visibility=undefined] visibility When not
             * specified, public is used
             * @constructor
             */
            constructor: function (writeAllowed, publicName, visibility) {
                if (writeAllowed) {
                    this._writeAllowed = true;
                }
                if ("string" === typeof publicName) {
                    this._publicName = publicName;
                }
                if ("string" === typeof visibility) {
                    this._visibility = visibility;
                }
            }

        }

    });

    /**
     * Used to flag a method which owns a last parameter being an event handler
     *
     * @class gpf.attributes.ClassEventHandlerAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassEventHandler
     */
    gpf._defAttr("$ClassEventHandler", _base, {});

    /**
     * Defines a class extension (internal)
     *
     * @param {String} ofClass
     * @param {String} [publicName=undefined] publicName When not specified,
     * the original method name is used
     *
     * @class gpf.attributes.ClassExtensionAttribute
     * @extends gpf.attributes.ClassAttribute
     * @alias gpf.$ClassExtension
     */
    gpf._defAttr("$ClassExtension", _base, {

        private: {

            /**
             * Constructor of the class to extend
             *
             * @type {Function}
             * @private
             */
            _ofClass: gpf.emptyFunction(),

            /**
             * Name of the method if added to the class
             *
             * @type {String}
             * @private
             */
            _publicName: ""

        },

        public: {

            /**
             * @param {Function} ofClass Constructor of the class to extend
             * @param {String} publicName Name of the method if added to the
             * class
             * @constructor
             */
            constructor: function (ofClass, publicName) {
                this._ofClass = ofClass;
                if ("string" === typeof publicName) {
                    this._publicName = publicName;
                }
            }

        }

    });

/*#ifndef(UMD)*/
}()); /* End of privacy scope */
/*#endif*/<|endoftext|>


<commit_before>import * as ActionCreators from './action-creators';
import * as ActionTypes from './action-types';
import {request, response, fail, requiresAuth, isFeedRequest, isFeedResponse} from './action-helpers';

//middleware for api requests
export const apiMiddleware = store => next => async (action) => {
  //ignore normal actions
  if (!action.apiRequest) {
    return next(action);
  }

  //dispatch request begin action
  //clean apiRequest to not get caught by this middleware
  var t = store.dispatch({...action, type: request(action.type), apiRequest: null});
  try {
    const apiResponse = await action.apiRequest(action.payload);
    const obj = await apiResponse.json();
    if (apiResponse.status === 200) {
      return store.dispatch({payload: obj, type: response(action.type), request: action.payload});
    } else if (apiResponse.status === 401) {
      return store.dispatch(ActionCreators.unauthenticated(obj));
    } else {
      return store.dispatch({payload: obj, type: fail(action.type), request: action.payload, response: apiResponse});
    }
  } catch (e) {
    return store.dispatch(ActionCreators.serverError(e));
  }
};

import {setToken, persistUser} from '../services/auth';
import {userParser, getCurrentRouteName} from '../utils';
import {browserHistory} from 'react-router';

export const authMiddleware = store => next => action => {

  //stop action propagation if it should be authed and user is not authed
  if (requiresAuth(action) && !store.getState().authenticated) {
    return;
  }

  if (action.type === ActionTypes.UNAUTHENTICATED) {
    setToken();
    persistUser();
    next(action);
    const pathname = (store.getState().routing.locationBeforeTransitions || {}).pathname;
    if (!pathname || pathname.indexOf('signin') !== -1 || pathname.indexOf('signup') !== -1) {
      return;
    }
    return browserHistory.push('/signin');
  }

  if (action.type === response(ActionTypes.SIGN_IN) ||
     action.type === response(ActionTypes.SIGN_UP) ) {
    setToken(action.payload.authToken);
    next(action);
    store.dispatch(ActionCreators.whoAmI());
    store.dispatch(ActionCreators.managedGroups());
    return browserHistory.push('/');
  }

  if (action.type === response(ActionTypes.WHO_AM_I) ||
     action.type === response(ActionTypes.UPDATE_USER) ) {
    persistUser(userParser(action.payload.users));
    return next(action);
  }

  return next(action);
};

export const likesLogicMiddleware = store => next => action => {
  switch (action.type) {
    case ActionTypes.SHOW_MORE_LIKES: {
      const postId = action.payload.postId;
      const post = store.getState().posts[postId];
      const isSync = (post.omittedLikes === 0);
      if (isSync) {
        return store.dispatch(ActionCreators.showMoreLikesSync(postId));
      } else {
        return store.dispatch(ActionCreators.showMoreLikesAsync(postId));
      }
    }
  }

  return next(action);
};

export const userPhotoLogicMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.UPDATE_USER_PICTURE)) {
    // Update data after userpic is updated
    store.dispatch(ActionCreators.whoAmI());
  }

  return next(action);
};

export const groupPictureLogicMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.UPDATE_GROUP_PICTURE)) {
    // Update data after group picture is updated
    store.dispatch(ActionCreators.getUserInfo(action.request.groupName));
  }

  return next(action);
};

export const redirectionMiddleware = store => next => action => {
  //go to home if single post has been removed
  if (action.type === response(ActionTypes.DELETE_POST) && store.getState().singlePostId) {
    return browserHistory.push('/');
  }

  if (action.type === response(ActionTypes.UNADMIN_GROUP_ADMIN) &&
      store.getState().user.id === action.request.user.id) {
    browserHistory.push(`/${action.request.groupName}/subscribers`);
  }

  return next(action);
};

export const requestsMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.WHO_AM_I)) {
    next(action);

    if (store.getState().user.pendingGroupRequests) {
      store.dispatch(ActionCreators.managedGroups());
    }

    return;
  }

  if (action.type === response(ActionTypes.ACCEPT_USER_REQUEST)) {
    next(action);

    if (store.getState().routing.locationBeforeTransitions.pathname == '/friends') {
      const username = store.getState().user.username;
      store.dispatch(ActionCreators.subscribers(username));
    }

    return;
  }

  return next(action);
};

import {init} from '../services/realtime';
import {getPostWithAllComments} from '../services/api';
import {frontendPreferences as frontendPrefsConfig} from '../config';

const isPostLoaded = ({posts}, postId) => posts[postId];
const iLikedPost = ({user, posts}, postId) => (posts[postId] || {likes: []}).likes.indexOf(user.id) !== -1;
const dispatchWithPost = async (store, postId, action, filter = _ => true) => {
  if (isPostLoaded(store.getState(), postId)) {
    return store.dispatch(action);
  } else {
    const postResponse = await getPostWithAllComments({postId: postId});
    const post = await postResponse.json();
    if (filter(post, action, store.getState())) {
      return store.dispatch({...action, post});
    }
  }
};

const isFirstFriendInteraction = (post, {users}, {subscriptions, comments}) => {
  const newLike = users[0];
  const myFriends = Object.keys(subscriptions).map(key => subscriptions[key]).map(sub => sub.user);
  const likesWithoutCurrent = post.posts.likes.filter(like => like !== newLike);
  const friendsInvolved = list => list.filter(element => myFriends.indexOf(element) !== -1).length;
  const friendsLikedBefore = friendsInvolved(likesWithoutCurrent);
  const newPostCommentAuthors = post.comments.map(comment => comment.createdBy);
  const commentsAuthors = post.posts.comments.map(cId => (comments[cId] || {}).createdBy);
  const friendsCommented = friendsInvolved([...commentsAuthors, ...newPostCommentAuthors]);
  const wasFirstInteraction = !friendsCommented && !friendsLikedBefore;
  return wasFirstInteraction;
};

const bindHandlers = store => ({
  'post:new': data => {
    const state = store.getState();

    const isFirstPage = !state.routing.locationBeforeTransitions.query.offset;

    if (isFirstPage) {

      const isHomeFeed = state.routing.locationBeforeTransitions.pathname === '/';
      const useRealtimePreference = state.user.frontendPreferences.realtimeActive;

      if (!isHomeFeed || (useRealtimePreference && isHomeFeed)) {
        return store.dispatch({...data, type: ActionTypes.REALTIME_POST_NEW, post: data.posts});
      }
    }
    return false;
  },
  'post:update': data => store.dispatch({...data, type: ActionTypes.REALTIME_POST_UPDATE, post: data.posts}),
  'post:destroy': data => store.dispatch({type: ActionTypes.REALTIME_POST_DESTROY, postId: data.meta.postId}),
  'post:hide': data => store.dispatch({type: ActionTypes.REALTIME_POST_HIDE, postId: data.meta.postId}),
  'post:unhide': data => store.dispatch({type: ActionTypes.REALTIME_POST_UNHIDE, postId: data.meta.postId}),
  'comment:new': async data => {
    const postId = data.comments.postId;
    const action = {...data, type: ActionTypes.REALTIME_COMMENT_NEW, comment: data.comments};
    return dispatchWithPost(store, postId, action);
  },
  'comment:update': data => store.dispatch({...data, type: ActionTypes.REALTIME_COMMENT_UPDATE, comment: data.comments}),
  'comment:destroy': data => store.dispatch({type: ActionTypes.REALTIME_COMMENT_DESTROY, commentId: data.commentId, postId: data.postId}),
  'like:new': async data => {
    const postId = data.meta.postId;
    const iLiked = iLikedPost(store.getState(), data.meta.postId);
    const action = {type: ActionTypes.REALTIME_LIKE_NEW, postId: data.meta.postId, users:[data.users], iLiked};
    return dispatchWithPost(store, postId, action, isFirstFriendInteraction);
  },
  'like:remove': data => store.dispatch({type: ActionTypes.REALTIME_LIKE_REMOVE, postId: data.meta.postId, userId: data.meta.userId}),
});

export const realtimeMiddleware = store => {
  const handlers = bindHandlers(store);
  let realtimeConnection;
  return next => action => {

    if (action.type === ActionTypes.UNAUTHENTICATED) {
      if (realtimeConnection) {
        realtimeConnection.disconnect();
        realtimeConnection = undefined;
      }
    }

    if (isFeedRequest(action) ||
      action.type === request(ActionTypes.GET_SINGLE_POST)) {
      if (realtimeConnection) {
        realtimeConnection.unsubscribe();
      }
    }

    if (isFeedResponse(action)) {
      if (!realtimeConnection) {
        realtimeConnection = init(handlers);
      }
      realtimeConnection.subscribe({timeline:[action.payload.timelines.id]});
    }

    if (action.type === response(ActionTypes.GET_SINGLE_POST)) {
      if (!realtimeConnection) {
        realtimeConnection = init(handlers);
      }
      realtimeConnection.subscribe({post:[action.payload.posts.id]});
    }

    return next(action);
  };
};
<commit_msg>Repair the I-liked check<commit_after>import * as ActionCreators from './action-creators';
import * as ActionTypes from './action-types';
import {request, response, fail, requiresAuth, isFeedRequest, isFeedResponse} from './action-helpers';

//middleware for api requests
export const apiMiddleware = store => next => async (action) => {
  //ignore normal actions
  if (!action.apiRequest) {
    return next(action);
  }

  //dispatch request begin action
  //clean apiRequest to not get caught by this middleware
  var t = store.dispatch({...action, type: request(action.type), apiRequest: null});
  try {
    const apiResponse = await action.apiRequest(action.payload);
    const obj = await apiResponse.json();
    if (apiResponse.status === 200) {
      return store.dispatch({payload: obj, type: response(action.type), request: action.payload});
    } else if (apiResponse.status === 401) {
      return store.dispatch(ActionCreators.unauthenticated(obj));
    } else {
      return store.dispatch({payload: obj, type: fail(action.type), request: action.payload, response: apiResponse});
    }
  } catch (e) {
    return store.dispatch(ActionCreators.serverError(e));
  }
};

import {setToken, persistUser} from '../services/auth';
import {userParser, getCurrentRouteName} from '../utils';
import {browserHistory} from 'react-router';

export const authMiddleware = store => next => action => {

  //stop action propagation if it should be authed and user is not authed
  if (requiresAuth(action) && !store.getState().authenticated) {
    return;
  }

  if (action.type === ActionTypes.UNAUTHENTICATED) {
    setToken();
    persistUser();
    next(action);
    const pathname = (store.getState().routing.locationBeforeTransitions || {}).pathname;
    if (!pathname || pathname.indexOf('signin') !== -1 || pathname.indexOf('signup') !== -1) {
      return;
    }
    return browserHistory.push('/signin');
  }

  if (action.type === response(ActionTypes.SIGN_IN) ||
     action.type === response(ActionTypes.SIGN_UP) ) {
    setToken(action.payload.authToken);
    next(action);
    store.dispatch(ActionCreators.whoAmI());
    store.dispatch(ActionCreators.managedGroups());
    return browserHistory.push('/');
  }

  if (action.type === response(ActionTypes.WHO_AM_I) ||
     action.type === response(ActionTypes.UPDATE_USER) ) {
    persistUser(userParser(action.payload.users));
    return next(action);
  }

  return next(action);
};

export const likesLogicMiddleware = store => next => action => {
  switch (action.type) {
    case ActionTypes.SHOW_MORE_LIKES: {
      const postId = action.payload.postId;
      const post = store.getState().posts[postId];
      const isSync = (post.omittedLikes === 0);
      if (isSync) {
        return store.dispatch(ActionCreators.showMoreLikesSync(postId));
      } else {
        return store.dispatch(ActionCreators.showMoreLikesAsync(postId));
      }
    }
  }

  return next(action);
};

export const userPhotoLogicMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.UPDATE_USER_PICTURE)) {
    // Update data after userpic is updated
    store.dispatch(ActionCreators.whoAmI());
  }

  return next(action);
};

export const groupPictureLogicMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.UPDATE_GROUP_PICTURE)) {
    // Update data after group picture is updated
    store.dispatch(ActionCreators.getUserInfo(action.request.groupName));
  }

  return next(action);
};

export const redirectionMiddleware = store => next => action => {
  //go to home if single post has been removed
  if (action.type === response(ActionTypes.DELETE_POST) && store.getState().singlePostId) {
    return browserHistory.push('/');
  }

  if (action.type === response(ActionTypes.UNADMIN_GROUP_ADMIN) &&
      store.getState().user.id === action.request.user.id) {
    browserHistory.push(`/${action.request.groupName}/subscribers`);
  }

  return next(action);
};

export const requestsMiddleware = store => next => action => {
  if (action.type === response(ActionTypes.WHO_AM_I)) {
    next(action);

    if (store.getState().user.pendingGroupRequests) {
      store.dispatch(ActionCreators.managedGroups());
    }

    return;
  }

  if (action.type === response(ActionTypes.ACCEPT_USER_REQUEST)) {
    next(action);

    if (store.getState().routing.locationBeforeTransitions.pathname == '/friends') {
      const username = store.getState().user.username;
      store.dispatch(ActionCreators.subscribers(username));
    }

    return;
  }

  return next(action);
};

import {init} from '../services/realtime';
import {getPostWithAllComments} from '../services/api';
import {frontendPreferences as frontendPrefsConfig} from '../config';

const isPostLoaded = ({posts}, postId) => posts[postId];
const iLikedPost = ({user, posts}, postId) => {
  const post = posts[postId];
  if (!post) {
    return false;
  }
  const likes = post.likes || [];
  return likes.indexOf(user.id) !== -1;
};
const dispatchWithPost = async (store, postId, action, filter = _ => true) => {
  if (isPostLoaded(store.getState(), postId)) {
    return store.dispatch(action);
  } else {
    const postResponse = await getPostWithAllComments({postId: postId});
    const post = await postResponse.json();
    if (filter(post, action, store.getState())) {
      return store.dispatch({...action, post});
    }
  }
};

const isFirstFriendInteraction = (post, {users}, {subscriptions, comments}) => {
  const newLike = users[0];
  const myFriends = Object.keys(subscriptions).map(key => subscriptions[key]).map(sub => sub.user);
  const likesWithoutCurrent = post.posts.likes.filter(like => like !== newLike);
  const friendsInvolved = list => list.filter(element => myFriends.indexOf(element) !== -1).length;
  const friendsLikedBefore = friendsInvolved(likesWithoutCurrent);
  const newPostCommentAuthors = post.comments.map(comment => comment.createdBy);
  const commentsAuthors = post.posts.comments.map(cId => (comments[cId] || {}).createdBy);
  const friendsCommented = friendsInvolved([...commentsAuthors, ...newPostCommentAuthors]);
  const wasFirstInteraction = !friendsCommented && !friendsLikedBefore;
  return wasFirstInteraction;
};

const bindHandlers = store => ({
  'post:new': data => {
    const state = store.getState();

    const isFirstPage = !state.routing.locationBeforeTransitions.query.offset;

    if (isFirstPage) {

      const isHomeFeed = state.routing.locationBeforeTransitions.pathname === '/';
      const useRealtimePreference = state.user.frontendPreferences.realtimeActive;

      if (!isHomeFeed || (useRealtimePreference && isHomeFeed)) {
        return store.dispatch({...data, type: ActionTypes.REALTIME_POST_NEW, post: data.posts});
      }
    }
    return false;
  },
  'post:update': data => store.dispatch({...data, type: ActionTypes.REALTIME_POST_UPDATE, post: data.posts}),
  'post:destroy': data => store.dispatch({type: ActionTypes.REALTIME_POST_DESTROY, postId: data.meta.postId}),
  'post:hide': data => store.dispatch({type: ActionTypes.REALTIME_POST_HIDE, postId: data.meta.postId}),
  'post:unhide': data => store.dispatch({type: ActionTypes.REALTIME_POST_UNHIDE, postId: data.meta.postId}),
  'comment:new': async data => {
    const postId = data.comments.postId;
    const action = {...data, type: ActionTypes.REALTIME_COMMENT_NEW, comment: data.comments};
    return dispatchWithPost(store, postId, action);
  },
  'comment:update': data => store.dispatch({...data, type: ActionTypes.REALTIME_COMMENT_UPDATE, comment: data.comments}),
  'comment:destroy': data => store.dispatch({type: ActionTypes.REALTIME_COMMENT_DESTROY, commentId: data.commentId, postId: data.postId}),
  'like:new': async data => {
    const postId = data.meta.postId;
    const iLiked = iLikedPost(store.getState(), data.meta.postId);
    const action = {type: ActionTypes.REALTIME_LIKE_NEW, postId: data.meta.postId, users:[data.users], iLiked};
    return dispatchWithPost(store, postId, action, isFirstFriendInteraction);
  },
  'like:remove': data => store.dispatch({type: ActionTypes.REALTIME_LIKE_REMOVE, postId: data.meta.postId, userId: data.meta.userId}),
});

export const realtimeMiddleware = store => {
  const handlers = bindHandlers(store);
  let realtimeConnection;
  return next => action => {

    if (action.type === ActionTypes.UNAUTHENTICATED) {
      if (realtimeConnection) {
        realtimeConnection.disconnect();
        realtimeConnection = undefined;
      }
    }

    if (isFeedRequest(action) ||
      action.type === request(ActionTypes.GET_SINGLE_POST)) {
      if (realtimeConnection) {
        realtimeConnection.unsubscribe();
      }
    }

    if (isFeedResponse(action)) {
      if (!realtimeConnection) {
        realtimeConnection = init(handlers);
      }
      realtimeConnection.subscribe({timeline:[action.payload.timelines.id]});
    }

    if (action.type === response(ActionTypes.GET_SINGLE_POST)) {
      if (!realtimeConnection) {
        realtimeConnection = init(handlers);
      }
      realtimeConnection.subscribe({post:[action.payload.posts.id]});
    }

    return next(action);
  };
};
<|endoftext|>