<commit_before>import _ from 'lodash'

import * as ttypes from './constants/task-type'
import * as ctypes from './constants/connection-type'

import util from './util'
import flexio from './flexio'

var toBase64 = function(str) {
  try { return btoa(unescape(encodeURIComponent(str))) } catch(e) { return e }
}

var fromBase64 = function(str) {
  try { return decodeURIComponent(escape(atob(str))) } catch(e) { return e }
}

export default (auth_token) => {
  return _.assign({}, {
    // -- state --

    pipe: {
      name: 'Javascript SDK Pipe',
      description: 'This pipe was created using the Flex.io Javascript SDK',
      task: []
    },
    processes: [],
    loading: false,
    saving: false,
    running: false,

    // -- methods --

    getJSON() {
      return _.assign({}, this.pipe)
    },

    getProcesses() {
      return [].concat(this.processes)
    },

    getLastProcess() {
      return _.last(this.processes)
    },

    addTask(task) {
      this.pipe.task.push(task)
      return this
    },

    clearTasks() {
      this.pipe.task = []
      return this
    },

    load() {
      var args = Array.from(arguments)
      var identifier = _.get(args, '[0]')
      var callback = _.get(args, '[1]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.load.apply(this, arguments) }, 50)
        return this
      }

      if (_.isNil(identifier))
        return util.debug.call(this, "The `identifier` parameter is required. Either the pipe's eid or pipe's alias may be used.")

      this.loading = true
      util.debug.call(this, 'Loading Pipe `' + identifier + '`...')

      flexio.http().get('/pipes/' + identifier)
        .then(response => {
          var obj = _.get(response, 'data', {})
          this.pipe = _.assign({}, obj)
          this.loading = false
          util.debug.call(this, 'Pipe Loaded.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.loading = false
          util.debug.call(this, 'Pipe Load Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    save() {
      var args = Array.from(arguments)
      var params = _.get(args, '[0]')
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.save.apply(this, arguments) }, 50)
        return this
      }

      if (_.isObject(params))
      {
        _.assign(this.pipe, _.pick(params, ['name', 'description', 'ename']))
        callback = _.get(args, '[1]')
      }

      this.saving = true
      util.debug.call(this, 'Saving Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      flexio.http().post('/pipes', this.pipe)
        .then(response => {
          var pipe = _.get(response, 'data', {})
          this.pipe = _.assign({}, pipe)
          this.saving = false
          util.debug.call(this, 'Pipe Saved.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.saving = false
          util.debug.call(this, 'Pipe Save Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    run() {
      var args = Array.from(arguments)
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.run.apply(this, arguments) }, 50)
        return this
      }

      this.running = true
      util.debug.call(this, 'Running Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      var run_params = _.assign({}, this.pipe)

      // if we have saved this pipe, use the pipe's eid as the parent eid
      var parent_eid = _.get(this.pipe, 'eid', '')
      if (parent_eid.length > 0)
        run_params = { parent_eid }

      // set the process to run mode
      _.assign(run_params, {
        process_mode: 'R'
      })

      flexio.http().post('/processes', run_params)
        .then(response => {
          var obj = _.get(response, 'data', {})
          var process_eid = _.get(obj, 'eid', '')
          this.processes.push(obj)
          util.debug.call(this, 'Created Process.')

          flexio.http().post('/processes/'+process_eid+'/run')
            .then(response => {
              var obj = _.get(response, 'data', {})
              util.debug.call(this, 'Process Complete.')
              this.running = false

              if (typeof callback == 'function')
                callback.call(this, null, obj)
            })
        })
        .catch(error => {
          util.debug.call(this, 'Process Create Failed.')
          this.running = false

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    // -- tasks --

    input() {
      var type = ttypes.TASK_TYPE_INPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var items = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The input task requires at least 1 parameter')

      switch (connection_type)
      {
        default:
          connection_type = ctypes.CONNECTION_TYPE_HTTP
          items = [].concat(args)
          break

        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_HTTP:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          items = _.get(args, '[2]', [])
          break

        case ctypes.CONNECTION_TYPE_RSS:
          connection = connection_type
          items = _.get(args, '[1]', [])
          break
      }

      items = _.map(items, (item) => {
        return {
          path: item
        }
      })

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          items
        }
      })
    },

    output() {
      var type = ttypes.TASK_TYPE_OUTPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var location = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The output task requires at least 1 parameter')

      switch (connection_type)
      {
        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
          connection = _.get(args, '[1]', '')
          break

        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          location = _.get(args, '[2]', '/')
          break
      }

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          location
        }
      })
    },

    convert(input, output) {
      var type = ttypes.TASK_TYPE_CONVERT

      var task = {
        type,
        params: {}
      }

      var csv_format = {
        format: 'delimited',
        delimiter: '{comma}',
        header: true,
        qualifier: '{double-quote}'
      }

      var tsv_format = {
        format: 'delimited',
        delimiter: '{tab}',
        header: true,
        qualifier: '{none}'
      }

      // convert input

      if (_.isString(input))
      {
        if (input == 'csv')
          _.set(task, 'params.input', csv_format)
           else if (input == 'tsv')
          _.set(task, 'params.input', tsv_format)
           else
          _.set(task, 'params.input.format', input)
      }
       else if (_.isObject(input))
      {
        _.set(task, 'params.input', input)
      }

      // convert output

      if (_.isString(output))
      {
        if (output == 'csv')
          _.set(task, 'params.output', csv_format)
           else if (output == 'tsv')
          _.set(task, 'params.output', tsv_format)
           else
          _.set(task, 'params.output.format', output)
      }
       else if (_.isObject(output))
      {
        _.set(task, 'params.output', output)
      }

      return this.addTask(task)
    },

    email(to, subject, body_text, body_html, data) {
      var type = ttypes.TASK_TYPE_EMAIL_SEND

      if (_.isNil(to))
        return util.debug.call(this, 'The `to` parameter is required')

      if (_.isNil(subject))
        return util.debug.call(this, 'The `subject` parameter is required')

      if (_.isNil(body_text))
        return util.debug.call(this, 'The `body_text` parameter is required')

      // `to` parameter must be an array
      if (!_.isArray(to))
        to = [to]

      if (_.isNil(body_html))
        body_html = body_text

      if (data != 'body' && data != 'attachment')
        data = 'none'

      return this.addTask({
        type,
        params: {
          to,
          subject,
          body_text,
          body_html,
          data
        }
      })
    },

    execute() {
      var type = ttypes.TASK_TYPE_EXECUTE
      var args = Array.from(arguments)
      var lang = _.get(args, '[0]', '')
      var code = undefined

      var task = {
        type,
        params: {}
      }

      // allow for flexible parameters
      if (lang == 'python' || lang == 'javascript')
      {
        code = _.get(args, '[1]', '')
      }
       else
      {
        lang = undefined
        code = _.get(args, '[0]', '')
      }

      if (_.isFunction(code))
      {
        // first argument is a function; we're using javascript
        lang = 'javascript'
        code = _.get(args, '[0]', function(input, output) {})

        // stringify the javascript function
        try {
          code = code.toString()
        } catch(e) {
          code = 'function(input, output) {}'
        }
      }

      if (lang != 'python' && lang != 'javascript')
      {
        // default to python
        lang = 'python'
      }

      // set the job's language
      _.set(task, 'params.lang', lang)

      // handle files or code snippets
      var http_regex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/
      if (code.match(http_regex))
        _.set(task, 'params.file', code)
         else
        _.set(task, 'params.code', toBase64(code))

      return this.addTask(task)
    },

    filter(where) {
      var type = ttypes.TASK_TYPE_FILTER

      if (_.isNil(where))
        return util.debug.call(this, 'The `filter` parameter is required')

      return this.addTask({
        type,
        params: {
          where
        }
      })
    },

    // shorthand for .execute('javascript', ...)
    javascript() {
      var args = Array.from(arguments)
      return this.execute('javascript', _.get(args, '[0]', ''))
    },

    limit(value) {
      var type = ttypes.TASK_TYPE_LIMIT
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    },

    // shorthand for .execute('python', ...)
    python() {
      var args = Array.from(arguments)
      return this.execute('python', _.get(args, '[0]', ''))
    },

    request() {
      var type = ttypes.TASK_TYPE_REQUEST
      var args = Array.from(arguments)
      var params = _.get(args, '[0]', {})

      // defaults
      params = _.assign({
        method: 'GET'
      }, params)

      return this.addTask({
        type,
        params
      })
    },

    select() {
      var type = ttypes.TASK_TYPE_SELECT
      var columns = Array.from(arguments)

      return this.addTask({
        type,
        params: {
          columns
        }
      })
    },

    sleep(value) {
      var type = ttypes.TASK_TYPE_SLEEP
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    }
  })
}
<commit_msg>Pipe run now returns a stream output instead of the process JSON.<commit_after>import _ from 'lodash'

import * as ttypes from './constants/task-type'
import * as ctypes from './constants/connection-type'

import util from './util'
import flexio from './flexio'

var toBase64 = function(str) {
  try { return btoa(unescape(encodeURIComponent(str))) } catch(e) { return e }
}

var fromBase64 = function(str) {
  try { return decodeURIComponent(escape(atob(str))) } catch(e) { return e }
}

export default (auth_token) => {
  return _.assign({}, {
    // -- state --

    pipe: {
      name: 'Javascript SDK Pipe',
      description: 'This pipe was created using the Flex.io Javascript SDK',
      task: []
    },
    processes: [],
    loading: false,
    saving: false,
    running: false,

    // -- methods --

    getJSON() {
      return _.assign({}, this.pipe)
    },

    getProcesses() {
      return [].concat(this.processes)
    },

    getLastProcess() {
      return _.last(this.processes)
    },

    addTask(task) {
      this.pipe.task.push(task)
      return this
    },

    clearTasks() {
      this.pipe.task = []
      return this
    },

    load() {
      var args = Array.from(arguments)
      var identifier = _.get(args, '[0]')
      var callback = _.get(args, '[1]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.load.apply(this, arguments) }, 50)
        return this
      }

      if (_.isNil(identifier))
        return util.debug.call(this, "The `identifier` parameter is required. Either the pipe's eid or pipe's alias may be used.")

      this.loading = true
      util.debug.call(this, 'Loading Pipe `' + identifier + '`...')

      flexio.http().get('/pipes/' + identifier)
        .then(response => {
          var obj = _.get(response, 'data', {})
          this.pipe = _.assign({}, obj)
          this.loading = false
          util.debug.call(this, 'Pipe Loaded.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.loading = false
          util.debug.call(this, 'Pipe Load Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    save() {
      var args = Array.from(arguments)
      var params = _.get(args, '[0]')
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.save.apply(this, arguments) }, 50)
        return this
      }

      if (_.isObject(params))
      {
        _.assign(this.pipe, _.pick(params, ['name', 'description', 'ename']))
        callback = _.get(args, '[1]')
      }

      this.saving = true
      util.debug.call(this, 'Saving Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      flexio.http().post('/pipes', this.pipe)
        .then(response => {
          var pipe = _.get(response, 'data', {})
          this.pipe = _.assign({}, pipe)
          this.saving = false
          util.debug.call(this, 'Pipe Saved.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.saving = false
          util.debug.call(this, 'Pipe Save Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    run() {
      var args = Array.from(arguments)
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.run.apply(this, arguments) }, 50)
        return this
      }

      this.running = true
      util.debug.call(this, 'Running Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      var run_params = _.assign({}, this.pipe)

      // if we have saved this pipe, use the pipe's eid as the parent eid
      var parent_eid = _.get(this.pipe, 'eid', '')
      if (parent_eid.length > 0)
        run_params = { parent_eid }

      // set the process to run mode
      _.assign(run_params, {
        process_mode: 'R'
      })

      flexio.http().post('/processes', run_params)
        .then(response => {
          var obj = _.get(response, 'data', {})
          var process_eid = _.get(obj, 'eid', '')
          this.processes.push(obj)
          util.debug.call(this, 'Created Process.')

          flexio.http().post('/processes/'+process_eid+'/run')
            .then(response => {
              var obj2 = _.get(response, 'data', {})
              util.debug.call(this, 'Process Complete.')
              this.running = false

              if (typeof callback == 'function')
                callback.call(this, null, obj2)
            })
        })
        .catch(error => {
          util.debug.call(this, 'Process Create Failed.')
          this.running = false

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    // -- tasks --

    input() {
      var type = ttypes.TASK_TYPE_INPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var items = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The input task requires at least 1 parameter')

      switch (connection_type)
      {
        default:
          connection_type = ctypes.CONNECTION_TYPE_HTTP
          items = [].concat(args)
          break

        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_HTTP:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          items = _.get(args, '[2]', [])
          break

        case ctypes.CONNECTION_TYPE_RSS:
          connection = connection_type
          items = _.get(args, '[1]', [])
          break
      }

      items = _.map(items, (item) => {
        return {
          path: item
        }
      })

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          items
        }
      })
    },

    output() {
      var type = ttypes.TASK_TYPE_OUTPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var location = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The output task requires at least 1 parameter')

      switch (connection_type)
      {
        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
          connection = _.get(args, '[1]', '')
          break

        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          location = _.get(args, '[2]', '/')
          break
      }

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          location
        }
      })
    },

    convert(input, output) {
      var type = ttypes.TASK_TYPE_CONVERT

      var task = {
        type,
        params: {}
      }

      var csv_format = {
        format: 'delimited',
        delimiter: '{comma}',
        header: true,
        qualifier: '{double-quote}'
      }

      var tsv_format = {
        format: 'delimited',
        delimiter: '{tab}',
        header: true,
        qualifier: '{none}'
      }

      // convert input

      if (_.isString(input))
      {
        if (input == 'csv')
          _.set(task, 'params.input', csv_format)
           else if (input == 'tsv')
          _.set(task, 'params.input', tsv_format)
           else
          _.set(task, 'params.input.format', input)
      }
       else if (_.isObject(input))
      {
        _.set(task, 'params.input', input)
      }

      // convert output

      if (_.isString(output))
      {
        if (output == 'csv')
          _.set(task, 'params.output', csv_format)
           else if (output == 'tsv')
          _.set(task, 'params.output', tsv_format)
           else
          _.set(task, 'params.output.format', output)
      }
       else if (_.isObject(output))
      {
        _.set(task, 'params.output', output)
      }

      return this.addTask(task)
    },

    email(to, subject, body_text, body_html, data) {
      var type = ttypes.TASK_TYPE_EMAIL_SEND

      if (_.isNil(to))
        return util.debug.call(this, 'The `to` parameter is required')

      if (_.isNil(subject))
        return util.debug.call(this, 'The `subject` parameter is required')

      if (_.isNil(body_text))
        return util.debug.call(this, 'The `body_text` parameter is required')

      // `to` parameter must be an array
      if (!_.isArray(to))
        to = [to]

      if (_.isNil(body_html))
        body_html = body_text

      if (data != 'body' && data != 'attachment')
        data = 'none'

      return this.addTask({
        type,
        params: {
          to,
          subject,
          body_text,
          body_html,
          data
        }
      })
    },

    execute() {
      var type = ttypes.TASK_TYPE_EXECUTE
      var args = Array.from(arguments)
      var lang = _.get(args, '[0]', '')
      var code = undefined

      var task = {
        type,
        params: {}
      }

      // allow for flexible parameters
      if (lang == 'python' || lang == 'javascript')
      {
        code = _.get(args, '[1]', '')
      }
       else
      {
        lang = undefined
        code = _.get(args, '[0]', '')
      }

      if (_.isFunction(code))
      {
        // first argument is a function; we're using javascript
        lang = 'javascript'
        code = _.get(args, '[0]', function(input, output) {})

        // stringify the javascript function
        try {
          code = code.toString()
        } catch(e) {
          code = 'function(input, output) {}'
        }
      }

      if (lang != 'python' && lang != 'javascript')
      {
        // default to python
        lang = 'python'
      }

      // set the job's language
      _.set(task, 'params.lang', lang)

      // handle files or code snippets
      var http_regex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/
      if (code.match(http_regex))
        _.set(task, 'params.file', code)
         else
        _.set(task, 'params.code', toBase64(code))

      return this.addTask(task)
    },

    filter(where) {
      var type = ttypes.TASK_TYPE_FILTER

      if (_.isNil(where))
        return util.debug.call(this, 'The `filter` parameter is required')

      return this.addTask({
        type,
        params: {
          where
        }
      })
    },

    // shorthand for .execute('javascript', ...)
    javascript() {
      var args = Array.from(arguments)
      return this.execute('javascript', _.get(args, '[0]', ''))
    },

    limit(value) {
      var type = ttypes.TASK_TYPE_LIMIT
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    },

    // shorthand for .execute('python', ...)
    python() {
      var args = Array.from(arguments)
      return this.execute('python', _.get(args, '[0]', ''))
    },

    request() {
      var type = ttypes.TASK_TYPE_REQUEST
      var args = Array.from(arguments)
      var params = _.get(args, '[0]', {})

      // defaults
      params = _.assign({
        method: 'GET'
      }, params)

      return this.addTask({
        type,
        params
      })
    },

    select() {
      var type = ttypes.TASK_TYPE_SELECT
      var columns = Array.from(arguments)

      return this.addTask({
        type,
        params: {
          columns
        }
      })
    },

    sleep(value) {
      var type = ttypes.TASK_TYPE_SLEEP
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    }
  })
}
<|endoftext|>


<commit_before>#!/usr/bin/env node
/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
 */

// System modules.
var fs = require('fs'),
    path = require('path'),
    Zip = require('node-zip'),
    request = require('request'),
    nopt = require('nopt'),
    url = require('url');

// Returns a promise for the manifest contents.
function getManifest(manifestDir) {
  var manifestFilename = path.join(manifestDir, 'manifest.json');
  var manifestMobileFilename = path.join(manifestDir, 'manifest.mobile.json');

  return Q.ninvoke(fs, 'readFile', manifestFilename, { encoding: 'utf-8' }).then(function(manifestData) {
    var manifestMobileData = '{}';
    return Q.ninvoke(fs, 'readFile', manifestMobileFilename, { encoding: 'utf-8' })
    .then(function(mobile) {
      manifestMobileData = mobile;
    }, function(err) {
      // Swallow any errors opening the mobile manifest, it's not required.
    }).then(function() {
      try {
        // jshint evil:true
        var manifest = eval('(' + manifestData + ')');
        var manifestMobile = eval('(' + manifestMobileData + ')');
        // jshint evil:false
        var extend = require('node.extend');
        manifest = extend(true, manifest, manifestMobile); // true -> deep recursive merge of these objects
        return manifest;
      } catch (e) {
        console.error(e);
        return Q.reject('Unable to parse manifest ' + manifestFilename);
      }
    });
  }, function(err) {
    return Q.reject('Unable to open manifest ' + manifestFilename + ' for reading.');
  });
}

function zipDir(zip, dir) {
  var contents = fs.readdirSync(dir);
  contents.forEach(function(f) {
    var fullPath = path.join(dir, f);
    if (fs.statSync(fullPath).isDirectory()) {
      var inner = zip.folder(f);
      zipDir(inner, path.join(dir, f));
    } else {
      zip.file(f, fs.readFileSync(fullPath, 'binary'), { binary: true });
    }
  });
}

// Takes a Node-style callback: function(err).
exports.push = function(target, dir, cb) {
  // Fetch the manifest so we can get the app's name for use in the push.
  var manifestPath;
  if (path.basename(dir) == 'manifest.json') {
    manifestPath = dir;
    dir = path.resolve(dir, '..');
  } else {
    manifestPath = path.join(dir, 'manifest.json');
  }

  // Read in the manifest.
  var manifestData = fs.readFileSync(manifestPath, { encoding: 'utf-8' });
  var manifest = eval('(' + manifestData + ')');

  var appName = manifest.name || 'CCA-push';

  // Build the zip object.
  var zip = new Zip();
  zipDir(zip, dir);

  var tempzip = zip.generate({ type: 'base64' });
  var zipContents = new Buffer(tempzip, 'base64');

  // Build the CRX data from the zip object.
  var crxContents = new Buffer(zipContents.length + 16);
  // Magic number
  crxContents[0] = 0x43; // C
  crxContents[1] = 0x72; // r
  crxContents[2] = 0x32; // 2
  crxContents[3] = 0x34; // 4
  // Version
  crxContents[4] = 2;
  // Zeroes (latter 3 bytes of the version, 4 bytes of key length, 4 bytes of signature length.
  for(var i = 5; i < 16; i++) {
    crxContents[i] = 0;
  }
  zipContents.copy(crxContents, 16);

  exports.info(target, function(e, res) {
    var req;
    if (e && e.statusCode == 404) {
      // Old-style HarnessPush.
      req = doRequest('POST', target, 'push', { type: 'crx', name: appName }, cb);
      req.form().append('file', crxContents, { filename: 'push.crx', contentType: 'application/octet-stream' });
    } else if (!e) {
      // New-style.
      req = doRequest('POST', target, 'push', { type: 'crx', name: appName }, cb, crxContents.length);
      req.write(crxContents);
    }
  });
}

function doRequest(method, target, action, queryParams, cb, contentLength) {
  var targetParts = target.split(':');
  var host = targetParts[0];
  var port = +(targetParts[1] || 2424);

  // Send the HTTP request. crxContents is a Node Buffer, which is the payload.
  // Prepare the form data for upload.
  var uri = url.format({
    protocol: 'http',
    hostname: host,
    port: port,
    pathname: '/' + action,
    query: queryParams
  });

  var headers = {};
  if (typeof contentLength != 'undefined') {
      headers['Content-Length'] = String(contentLength);
  }
  var req = request.post({
    uri: uri,
    method: method,
    headers: headers
  }, cb);
  return req;
};

exports.info = function(target, cb) {
  function afterReq(err, res, body) {
    if (err) {
      err.statusCode = res.statusCode;
      cb(err);
    } else {
      try {
        cb(null, JSON.parse(body));
      } catch (e) {
        cb(e);
      }
    }
  }
  doRequest('GET', target, 'info', {}, afterReq);
};

exports.menu = function(target, cb) {
  doRequest('POST', target, 'menu', null, cb);
};

exports.eval = function(target, someJs, cb) {
  doRequest('POST', target, 'exec', { code: someJs }, cb);
};

function parseArgs(argv) {
    var opts = {
      'path': path,
      'help': Boolean,
      'menu': Boolean,
      'info': Boolean,
      'eval': String,
      'target': String
    };
    var ret = nopt(opts, null, argv);
    if (!ret.target) {
      ret.target = 'localhost:2424';
    }
    if (+!!ret.path + +!!ret.menu + +!!ret.eval + +!!ret.info !== 1) {
      usage();
    }
    return ret;
}

function usage() {
  console.log('Usage: cca-push --path=path/to/chrome_app --target=IP_ADDRESS:PORT');
  console.log('Usage: cca-push --menu');
  console.log('Usage: cca-push --eval "alert(1)"');
  console.log('Usage: cca-push --info');
  console.log();
  console.log('--target defaults to localhost:2424');
  console.log('To deploy to Android over USB, use: adb forward tcp:2424 tcp:2424');
  process.exit(1);
}

function main() {
  var args = parseArgs(process.argv);

  function cb(err, res, body) {
    if (err) {
      console.error(err);
    } else if (arguments.length == 2) {
      // info command.
      console.log(JSON.stringify(res, null, 4));
    } else if (body) {
      console.log(body);
    }
  }
  if (args.path) {
    exports.push(args.target, args.path, cb);
  } else if (args.menu) {
    exports.menu(args.target, cb);
  } else if (args.eval) {
    exports.eval(args.target, args.eval, cb);
  } else if (args.info) {
    exports.info(args.target, cb);
  }
}

if (require.main === module) {
  main();
}
<commit_msg>Fix failing INFO command, by sending GET not POST<commit_after>#!/usr/bin/env node
/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
 */

// System modules.
var fs = require('fs'),
    path = require('path'),
    Zip = require('node-zip'),
    request = require('request'),
    nopt = require('nopt'),
    url = require('url');

// Returns a promise for the manifest contents.
function getManifest(manifestDir) {
  var manifestFilename = path.join(manifestDir, 'manifest.json');
  var manifestMobileFilename = path.join(manifestDir, 'manifest.mobile.json');

  return Q.ninvoke(fs, 'readFile', manifestFilename, { encoding: 'utf-8' }).then(function(manifestData) {
    var manifestMobileData = '{}';
    return Q.ninvoke(fs, 'readFile', manifestMobileFilename, { encoding: 'utf-8' })
    .then(function(mobile) {
      manifestMobileData = mobile;
    }, function(err) {
      // Swallow any errors opening the mobile manifest, it's not required.
    }).then(function() {
      try {
        // jshint evil:true
        var manifest = eval('(' + manifestData + ')');
        var manifestMobile = eval('(' + manifestMobileData + ')');
        // jshint evil:false
        var extend = require('node.extend');
        manifest = extend(true, manifest, manifestMobile); // true -> deep recursive merge of these objects
        return manifest;
      } catch (e) {
        console.error(e);
        return Q.reject('Unable to parse manifest ' + manifestFilename);
      }
    });
  }, function(err) {
    return Q.reject('Unable to open manifest ' + manifestFilename + ' for reading.');
  });
}

function zipDir(zip, dir) {
  var contents = fs.readdirSync(dir);
  contents.forEach(function(f) {
    var fullPath = path.join(dir, f);
    if (fs.statSync(fullPath).isDirectory()) {
      var inner = zip.folder(f);
      zipDir(inner, path.join(dir, f));
    } else {
      zip.file(f, fs.readFileSync(fullPath, 'binary'), { binary: true });
    }
  });
}

// Takes a Node-style callback: function(err).
exports.push = function(target, dir, cb) {
  // Fetch the manifest so we can get the app's name for use in the push.
  var manifestPath;
  if (path.basename(dir) == 'manifest.json') {
    manifestPath = dir;
    dir = path.resolve(dir, '..');
  } else {
    manifestPath = path.join(dir, 'manifest.json');
  }

  // Read in the manifest.
  var manifestData = fs.readFileSync(manifestPath, { encoding: 'utf-8' });
  var manifest = eval('(' + manifestData + ')');

  var appName = manifest.name || 'CCA-push';

  // Build the zip object.
  var zip = new Zip();
  zipDir(zip, dir);

  var tempzip = zip.generate({ type: 'base64' });
  var zipContents = new Buffer(tempzip, 'base64');

  // Build the CRX data from the zip object.
  var crxContents = new Buffer(zipContents.length + 16);
  // Magic number
  crxContents[0] = 0x43; // C
  crxContents[1] = 0x72; // r
  crxContents[2] = 0x32; // 2
  crxContents[3] = 0x34; // 4
  // Version
  crxContents[4] = 2;
  // Zeroes (latter 3 bytes of the version, 4 bytes of key length, 4 bytes of signature length.
  for(var i = 5; i < 16; i++) {
    crxContents[i] = 0;
  }
  zipContents.copy(crxContents, 16);

  exports.info(target, function(e, res) {
    var req;
    if (e && e.statusCode == 404) {
      // Old-style HarnessPush.
      req = doRequest('POST', target, 'push', { type: 'crx', name: appName }, cb);
      req.form().append('file', crxContents, { filename: 'push.crx', contentType: 'application/octet-stream' });
    } else if (!e) {
      // New-style.
      req = doRequest('POST', target, 'push', { type: 'crx', name: appName }, cb, crxContents.length);
      req.write(crxContents);
    }
  });
}

function doRequest(method, target, action, queryParams, cb, contentLength) {
  var targetParts = target.split(':');
  var host = targetParts[0];
  var port = +(targetParts[1] || 2424);

  // Send the HTTP request. crxContents is a Node Buffer, which is the payload.
  // Prepare the form data for upload.
  var uri = url.format({
    protocol: 'http',
    hostname: host,
    port: port,
    pathname: '/' + action,
    query: queryParams
  });

  var headers = {};
  if (typeof contentLength != 'undefined') {
      headers['Content-Length'] = String(contentLength);
  }
  var req = request[method.toLowerCase()]({
    uri: uri,
    headers: headers
  }, cb);
  return req;
};

exports.info = function(target, cb) {
  function afterReq(err, res, body) {
    if (err) {
      err.statusCode = res.statusCode;
      cb(err);
    } else {
      try {
        cb(null, JSON.parse(body));
      } catch (e) {
        cb(e);
      }
    }
  }
  doRequest('GET', target, 'info', {}, afterReq);
};

exports.menu = function(target, cb) {
  doRequest('POST', target, 'menu', null, cb);
};

exports.eval = function(target, someJs, cb) {
  doRequest('POST', target, 'exec', { code: someJs }, cb);
};

function parseArgs(argv) {
    var opts = {
      'path': path,
      'help': Boolean,
      'menu': Boolean,
      'info': Boolean,
      'eval': String,
      'target': String
    };
    var ret = nopt(opts, null, argv);
    if (!ret.target) {
      ret.target = 'localhost:2424';
    }
    if (+!!ret.path + +!!ret.menu + +!!ret.eval + +!!ret.info !== 1) {
      usage();
    }
    return ret;
}

function usage() {
  console.log('Usage: cca-push --path=path/to/chrome_app --target=IP_ADDRESS:PORT');
  console.log('Usage: cca-push --menu');
  console.log('Usage: cca-push --eval "alert(1)"');
  console.log('Usage: cca-push --info');
  console.log();
  console.log('--target defaults to localhost:2424');
  console.log('To deploy to Android over USB, use: adb forward tcp:2424 tcp:2424');
  process.exit(1);
}

function main() {
  var args = parseArgs(process.argv);

  function cb(err, res, body) {
    if (err) {
      console.error(err);
    } else if (arguments.length == 2) {
      // info command.
      console.log(JSON.stringify(res, null, 4));
    } else if (body) {
      console.log(body);
    }
  }
  if (args.path) {
    exports.push(args.target, args.path, cb);
  } else if (args.menu) {
    exports.menu(args.target, cb);
  } else if (args.eval) {
    exports.eval(args.target, args.eval, cb);
  } else if (args.info) {
    exports.info(args.target, cb);
  }
}

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


<commit_before>var colors = require('colors/safe');
var path = require('path');
var readline = require('readline');

var pp = function(v) {
	if (v === null) {
		return "null";
	}

	if (v === undefined) {
		return undefined;
	}

	switch (v.constructor) {
		case String:
			return '"'+v+'"';
		case Array:
			return "["+v.join(' ')+"]";
		default:
			return v;
	}
};

var log = function(color) {
	console.log(color([].slice.call(arguments, 1).join(' ')));
};

var INTERPRET = 0;
var DEFINITION = 1;
var COMPILE = 2;
var COMMENT = 3;
var QUOTE = 4;

var state = INTERPRET;

var stack = [];
var P = function(v) { stack.push(v); };
var name = "";
var defstack = [];

var dict = {
	".": function(v1) { log(colors.green, pp(v1)); },
	"+": function(v1, v2) { P(v1 + v2); },
	"-": function(v1, v2) { P(v1 - v2); },
	"*": function(v1, v2) { P(v1 * v2); },
	"/": function(v1, v2) { P(v1 / v2); },
	"%": function(v1, v2) { P(v1 % v2); },
	"==": function(v1, v2) { P(v1 === v2); },
	"!=": function(v1, v2) { P(v1 !== v2); },
	"<": function(v1, v2) { P(v1 < v2); },
	"<=": function(v1, v2) { P(v1 <= v2); },
	">": function(v1, v2) { P(v1 > v2); },
	">=": function(v1, v2) { P(v1 >= v2); },
	"swap": function(v1, v2) { P(v2); P(v1); },
	"dup": function(v1) { P(v1); P(v1); },
	"dip": function(v1, v2) { exec(v2); P(v1);  },
	"if": function(v1, v2, v3) { if (v1) { exec(v2); } else { exec(v3); } },
	"over": function(v1, v2) { P(v1); P(v2); P(v1); },
	"tuck": function(v1, v2) { P(v2); P(v1); P(v2); },
	"rot": function(v1, v2, v3) { P(v2); P(v3); P(v1); },
	"drop": function(v1) { },
	"call": function(v1) { exec(v1); },
	"compose": function(v1, v2) { P(v1.concat(v2)) },
	"exit": function() { process.exit(); },
	"clear": function() { stack = []; },
	"Math.abs": function(v1) { P(Math.abs(v1)); },
};

var kernel = [
	": when swap [ call ] [ drop ] if ;",
	": 2dip ( x y quot -- x y ) swap [ dip ] dip ;",
	": 3dip ( x y z quot -- x y z ) swap [ 2dip ] dip ;",
];

var exec = function(quote) {
	var error = execute(quote);
	if (error) {
		log(colors.red, error);
	}
};

var isNum = function(val) {
	return !isNaN(val);
};

var isString = function(val) {
	return val.slice(0, 1) == '"' && val.slice(-1) == '"';
};

var call = function(word) {
	var argc = word.length
	var argv = stack.splice(stack.length-argc, argc);
	if (argv.length == argc) {
		word.apply(null, argv);
	} else {
		stack.push.apply(stack, argv);
		return "stack underflow";
	}
};

var execute = function(tokens) {
	var tokens = tokens || [];

	for (var i=0; i < tokens.length; i++) {
		var token = tokens[i];

		switch (state) {
			case INTERPRET:
				if (dict.hasOwnProperty(token)) {
					var word = dict[token];

					if (typeof(word) === 'function') {
						var error = call(word);
						if (error) {
							return error;
						}
					} else {
						var error = exec(word);
						if (error) {
							return error;
						}
					}
				} else if (token == 'null') {
					stack.push(null);
				} else if (token == 'undefined') {
					stack.push(undefined);
				} else if (token == 'true') {
					stack.push(true);
				} else if (token == 'false') {
					stack.push(false);
				} else if (isNum(token)) {
					stack.push(Number(token));
				} else if (isString(token)) {
					stack.push(token.slice(1, -1));
				} else if (token === ':') {
					state = DEFINITION;
				} else if (token === '(') {
					state = COMMENT;
				} else if (token === "[") {
					state = QUOTE;
				} else {
					return "UNKNOWN TOKEN: " + token;
				}
				break;
			case DEFINITION:
				name = token;
				state = COMPILE;
				break;
			case COMPILE:
				if (token === ":") {
					return "TOKEN : INSIDE DEFINITION";
				}	else if (token === ";") {
					if (dict.hasOwnProperty(name)) {
						return "DUPLICATE WORD: " + name;
					}
					dict[name] = defstack;
					defstack = [];
					state = INTERPRET;
				} else {
					defstack.push(token);
				}
				break;
			case COMMENT:
				if (token === ")") {
					state = INTERPRET;
				}
				break;
			case QUOTE:
				if (token === "[") {
					return "TOKEN [ INSIDE QUOTATION";
				}	else if (token === "]") {
					stack.push(defstack);
					defstack = [];
					state = INTERPRET;
				} else {
					defstack.push(token);
				}
				break;
			default:
				return "UNKNOWN STATE: " + state;
		}
	}
};

module.exports = function() {
	log(colors.white.bold, "Conkat v0.0.1\n")

	var stdout = process.stdout;
	var stdin = process.openStdin();
	var repl = readline.createInterface(stdin, stdout);

	exec(kernel.join("\n").match(/\S+/g));

	repl.on('close', function() {
		stdin.destroy();
	});

	repl.on('line', function(line) {
		exec(line.match(/\S+/g));

		if (state === INTERPRET) {
			repl.setPrompt('catcus> ');
			var pstack = [];
			for (var i=0; i < stack.length; i++) {
				pstack.push(pp(stack[i]));
			}
			log(colors.white.bold, '=>', pstack.join(' '));
		} else {
			repl.setPrompt(colors.yellow('     .. '), 8);
		}

		repl.prompt();
	});

	repl.setPrompt('catcus> ');
	repl.prompt();
};
<commit_msg>Add banner to repl<commit_after>var colors = require('colors/safe');
var path = require('path');
var readline = require('readline');

var pp = function(v) {
	if (v === null) {
		return "null";
	}

	if (v === undefined) {
		return undefined;
	}

	switch (v.constructor) {
		case String:
			return '"'+v+'"';
		case Array:
			return "["+v.join(' ')+"]";
		default:
			return v;
	}
};

var log = function(color) {
	console.log(color([].slice.call(arguments, 1).join(' ')));
};

var INTERPRET = 0;
var DEFINITION = 1;
var COMPILE = 2;
var COMMENT = 3;
var QUOTE = 4;

var state = INTERPRET;

var stack = [];
var P = function(v) { stack.push(v); };
var name = "";
var defstack = [];

var dict = {
	".": function(v1) { log(colors.green, pp(v1)); },
	"+": function(v1, v2) { P(v1 + v2); },
	"-": function(v1, v2) { P(v1 - v2); },
	"*": function(v1, v2) { P(v1 * v2); },
	"/": function(v1, v2) { P(v1 / v2); },
	"%": function(v1, v2) { P(v1 % v2); },
	"==": function(v1, v2) { P(v1 === v2); },
	"!=": function(v1, v2) { P(v1 !== v2); },
	"<": function(v1, v2) { P(v1 < v2); },
	"<=": function(v1, v2) { P(v1 <= v2); },
	">": function(v1, v2) { P(v1 > v2); },
	">=": function(v1, v2) { P(v1 >= v2); },
	"swap": function(v1, v2) { P(v2); P(v1); },
	"dup": function(v1) { P(v1); P(v1); },
	"dip": function(v1, v2) { exec(v2); P(v1);  },
	"if": function(v1, v2, v3) { if (v1) { exec(v2); } else { exec(v3); } },
	"over": function(v1, v2) { P(v1); P(v2); P(v1); },
	"tuck": function(v1, v2) { P(v2); P(v1); P(v2); },
	"rot": function(v1, v2, v3) { P(v2); P(v3); P(v1); },
	"drop": function(v1) { },
	"call": function(v1) { exec(v1); },
	"compose": function(v1, v2) { P(v1.concat(v2)) },
	"exit": function() { process.exit(); },
	"clear": function() { stack = []; },
	"Math.abs": function(v1) { P(Math.abs(v1)); },
};

var kernel = [
	": when swap [ call ] [ drop ] if ;",
	": 2dip ( x y quot -- x y ) swap [ dip ] dip ;",
	": 3dip ( x y z quot -- x y z ) swap [ 2dip ] dip ;",
];

var exec = function(quote) {
	var error = execute(quote);
	if (error) {
		log(colors.red, error);
	}
};

var isNum = function(val) {
	return !isNaN(val);
};

var isString = function(val) {
	return val.slice(0, 1) == '"' && val.slice(-1) == '"';
};

var call = function(word) {
	var argc = word.length
	var argv = stack.splice(stack.length-argc, argc);
	if (argv.length == argc) {
		word.apply(null, argv);
	} else {
		stack.push.apply(stack, argv);
		return "stack underflow";
	}
};

var execute = function(tokens) {
	var tokens = tokens || [];

	for (var i=0; i < tokens.length; i++) {
		var token = tokens[i];

		switch (state) {
			case INTERPRET:
				if (dict.hasOwnProperty(token)) {
					var word = dict[token];

					if (typeof(word) === 'function') {
						var error = call(word);
						if (error) {
							return error;
						}
					} else {
						var error = exec(word);
						if (error) {
							return error;
						}
					}
				} else if (token == 'null') {
					stack.push(null);
				} else if (token == 'undefined') {
					stack.push(undefined);
				} else if (token == 'true') {
					stack.push(true);
				} else if (token == 'false') {
					stack.push(false);
				} else if (isNum(token)) {
					stack.push(Number(token));
				} else if (isString(token)) {
					stack.push(token.slice(1, -1));
				} else if (token === ':') {
					state = DEFINITION;
				} else if (token === '(') {
					state = COMMENT;
				} else if (token === "[") {
					state = QUOTE;
				} else {
					return "UNKNOWN TOKEN: " + token;
				}
				break;
			case DEFINITION:
				name = token;
				state = COMPILE;
				break;
			case COMPILE:
				if (token === ":") {
					return "TOKEN : INSIDE DEFINITION";
				}	else if (token === ";") {
					if (dict.hasOwnProperty(name)) {
						return "DUPLICATE WORD: " + name;
					}
					dict[name] = defstack;
					defstack = [];
					state = INTERPRET;
				} else {
					defstack.push(token);
				}
				break;
			case COMMENT:
				if (token === ")") {
					state = INTERPRET;
				}
				break;
			case QUOTE:
				if (token === "[") {
					return "TOKEN [ INSIDE QUOTATION";
				}	else if (token === "]") {
					stack.push(defstack);
					defstack = [];
					state = INTERPRET;
				} else {
					defstack.push(token);
				}
				break;
			default:
				return "UNKNOWN STATE: " + state;
		}
	}
};

var banner = "   _________  ______________  _______\n" +
	"  / ____/   |/_  __/ ____/ / / / ___/  /\\___/\\\n" +
	" / /   / /| | / / / /   / / / /\\__ \\   ) -.- (  $%^&*!\n" +
	"/ /___/ ___ |/ / / /___/ /_/ /___/ /  =\\  o  /=\n" +
	"\\____/_/  |_/_/  \\____/\\____//____/     )   (\n";

module.exports = function() {
	log(colors.white.bold, banner);

	var stdout = process.stdout;
	var stdin = process.openStdin();
	var repl = readline.createInterface(stdin, stdout);

	exec(kernel.join("\n").match(/\S+/g));

	repl.on('close', function() {
		stdin.destroy();
	});

	repl.on('line', function(line) {
		exec(line.match(/\S+/g));

		if (state === INTERPRET) {
			repl.setPrompt('catcus> ');
			var pstack = [];
			for (var i=0; i < stack.length; i++) {
				pstack.push(pp(stack[i]));
			}
			log(colors.white.bold, '=>', pstack.join(' '));
		} else {
			repl.setPrompt(colors.yellow('     .. '), 8);
		}

		repl.prompt();
	});

	repl.setPrompt('catcus> ');
	repl.prompt();
};
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2008-2011  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// SaveWidget
//============================================================================

var IPython = (function (IPython) {

    var utils = IPython.utils;

    var SaveWidget = function (selector) {
        this.selector = selector;
        if (this.selector !== undefined) {
            this.element = $(selector);
            this.style();
            this.bind_events();
        }
    };

    SaveWidget.prototype.style = function () {
        this.element.find('span#save_widget').addClass('ui-widget');
        this.element.find('span#notebook_name').addClass('ui-widget');
        this.element.find('span#save_status').addClass('ui-widget')
            .css({border: 'none', 'margin-left': '20px'});
    };

    SaveWidget.prototype.bind_events = function () {
        var that = this;
        this.element.find('span#notebook_name').click(function () {
            that.rename_notebook();
        });
        this.element.find('span#notebook_name').hover(function () {
            $(this).addClass("ui-state-hover");
        }, function () {
            $(this).removeClass("ui-state-hover");
        });
        $([IPython.events]).on('notebook_loaded.Notebook', function () {
            that.set_last_saved();
            that.update_notebook_name();
            that.update_document_title();
        });
        $([IPython.events]).on('notebook_saved.Notebook', function () {
            that.set_last_saved();
            that.update_notebook_name();
            that.update_document_title();
        });
        $([IPython.events]).on('notebook_save_failed.Notebook', function () {
            that.set_save_status('Last Save Failed!');
        });
    };

    SaveWidget.prototype.rename_notebook = function () {
        var that = this;
        var dialog = $('<div/>');
        dialog.append(
            $('<h3/>').html('Enter a new notebook name:')
            .css({'margin-bottom': '10px'})
        );
        dialog.append(
            $('<input/>').attr('type','text').attr('size','25')
            .addClass('ui-widget ui-widget-content')
            .attr('value',IPython.notebook.get_notebook_name())
        );
        // $(document).append(dialog);
        dialog.dialog({
            resizable: false,
            modal: true,
            title: "Rename Notebook",
            closeText: "",
            close: function(event, ui) {$(this).dialog('destroy').remove();},
            buttons : {
                "OK": function () {
                    var new_name = $(this).find('input').attr('value');
                    if (!IPython.notebook.test_notebook_name(new_name)) {
                        $(this).find('h3').html(
                            "Invalid notebook name. Notebook names must "+
                            "have 1 or more characters and can contain any characters " +
                            "except :/\\. Please enter a new notebook name:"
                        );
                    } else {
                        IPython.notebook.set_notebook_name(new_name);
                        IPython.notebook.save_notebook();
                        $(this).dialog('close');
                    }
                },
                "Cancel": function () {
                    $(this).dialog('close');
                }
            },
            open : function (event, ui) {
                var that = $(this);
                // Upon ENTER, click the OK button.
                that.find('input[type="text"]').keydown(function (event, ui) {
                    if (event.which === utils.keycodes.ENTER) {
                        that.parent().find('button').first().click();
                    }
                });
            }
        });
    }

    SaveWidget.prototype.update_notebook_name = function () {
        var nbname = IPython.notebook.get_notebook_name();
        this.element.find('span#notebook_name').html(nbname);
    };

    SaveWidget.prototype.update_document_title = function () {
        var nbname = IPython.notebook.get_notebook_name();
        document.title = nbname;
    };

    SaveWidget.prototype.set_save_status = function (msg) {
        this.element.find('span#save_status').html(msg);
    }

    SaveWidget.prototype.set_last_saved = function () {
        var d = new Date();
        this.set_save_status('Last saved: '+d.format('mmm dd HH:MM'));
    };

    IPython.SaveWidget = SaveWidget;

    return IPython;

}(IPython));

<commit_msg>Text in rename dialog was way too big - making it <p>.<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2008-2011  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

//============================================================================
// SaveWidget
//============================================================================

var IPython = (function (IPython) {

    var utils = IPython.utils;

    var SaveWidget = function (selector) {
        this.selector = selector;
        if (this.selector !== undefined) {
            this.element = $(selector);
            this.style();
            this.bind_events();
        }
    };

    SaveWidget.prototype.style = function () {
        this.element.find('span#save_widget').addClass('ui-widget');
        this.element.find('span#notebook_name').addClass('ui-widget');
        this.element.find('span#save_status').addClass('ui-widget')
            .css({border: 'none', 'margin-left': '20px'});
    };

    SaveWidget.prototype.bind_events = function () {
        var that = this;
        this.element.find('span#notebook_name').click(function () {
            that.rename_notebook();
        });
        this.element.find('span#notebook_name').hover(function () {
            $(this).addClass("ui-state-hover");
        }, function () {
            $(this).removeClass("ui-state-hover");
        });
        $([IPython.events]).on('notebook_loaded.Notebook', function () {
            that.set_last_saved();
            that.update_notebook_name();
            that.update_document_title();
        });
        $([IPython.events]).on('notebook_saved.Notebook', function () {
            that.set_last_saved();
            that.update_notebook_name();
            that.update_document_title();
        });
        $([IPython.events]).on('notebook_save_failed.Notebook', function () {
            that.set_save_status('Last Save Failed!');
        });
    };

    SaveWidget.prototype.rename_notebook = function () {
        var that = this;
        var dialog = $('<div/>');
        dialog.append(
            $('<p/>').html('Enter a new notebook name:')
            .css({'margin-bottom': '10px'})
        );
        dialog.append(
            $('<input/>').attr('type','text').attr('size','25')
            .addClass('ui-widget ui-widget-content')
            .attr('value',IPython.notebook.get_notebook_name())
        );
        // $(document).append(dialog);
        dialog.dialog({
            resizable: false,
            modal: true,
            title: "Rename Notebook",
            closeText: "",
            close: function(event, ui) {$(this).dialog('destroy').remove();},
            buttons : {
                "OK": function () {
                    var new_name = $(this).find('input').attr('value');
                    if (!IPython.notebook.test_notebook_name(new_name)) {
                        $(this).find('h3').html(
                            "Invalid notebook name. Notebook names must "+
                            "have 1 or more characters and can contain any characters " +
                            "except :/\\. Please enter a new notebook name:"
                        );
                    } else {
                        IPython.notebook.set_notebook_name(new_name);
                        IPython.notebook.save_notebook();
                        $(this).dialog('close');
                    }
                },
                "Cancel": function () {
                    $(this).dialog('close');
                }
            },
            open : function (event, ui) {
                var that = $(this);
                // Upon ENTER, click the OK button.
                that.find('input[type="text"]').keydown(function (event, ui) {
                    if (event.which === utils.keycodes.ENTER) {
                        that.parent().find('button').first().click();
                    }
                });
            }
        });
    }

    SaveWidget.prototype.update_notebook_name = function () {
        var nbname = IPython.notebook.get_notebook_name();
        this.element.find('span#notebook_name').html(nbname);
    };

    SaveWidget.prototype.update_document_title = function () {
        var nbname = IPython.notebook.get_notebook_name();
        document.title = nbname;
    };

    SaveWidget.prototype.set_save_status = function (msg) {
        this.element.find('span#save_status').html(msg);
    }

    SaveWidget.prototype.set_last_saved = function () {
        var d = new Date();
        this.set_save_status('Last saved: '+d.format('mmm dd HH:MM'));
    };

    IPython.SaveWidget = SaveWidget;

    return IPython;

}(IPython));

<|endoftext|>


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

//
// Initialize UI
//

function initIncidentPage() {
    function loadedBody() {
        addLocationAddressOptions();
        disableEditing();
        loadAndDisplayIncident();
    }

    loadBody(loadedBody);
}

//
// Load incident
//

var incident = null;

function loadIncident(success) {
    function loaded(data, status, request) {
        incident = data;

        if (success != undefined) {
            success();
        }
    }

    $.get(incidentsURL + "/" + incidentNumber, loaded, "json");
}

function loadAndDisplayIncident(success) {
    function loaded() {
        drawIncidentFields();
        enableEditing();

        if (success != undefined) {
            success();
        }
    }

    loadIncident(loaded);
}

//
// Draw all fields
//

function drawIncidentFields() {
    drawNumber();
    drawState();
    drawPriority();
    drawSummary();
    drawRangers();
    drawIncidentTypes();
    drawLocationName();
    drawLocationAddressRadialHour();
    drawLocationAddressRadialMinute();
    drawLocationAddressConcentric();
    drawLocationDescription();
    drawReportEntries();
}

//
// Add option elements to location address select elements
//

function addLocationAddressOptions() {
    var hours = range(1, 13);
    for (var i in hours) {
        var hour = padTwo(hours[i]);
        $("#incident_location_address_radial_hour")
            .append($("<option />", { "value": hour, "text": hour }))
            ;
    }

    var minutes = range(0, 12, 5);
    for (var i in minutes) {
        var minute = padTwo(minutes[i]);
        $("#incident_location_address_radial_minute")
            .append($("<option />", { "value": minute, "text": minute }))
            ;
    }

    for (var id in concentricStreetNameByID) {
        var name = concentricStreetNameByID[id];
        $("#incident_location_address_concentric")
            .append($("<option />", { "value": id, "text": name }))
            ;
    }
}

//
// Populate incident number
//

function drawNumber() {
    $("#incident_number").text(incident.number);
}

//
// Populate incident state
//

function drawState() {
    selectOptionWithValue(
        $("#incident_state"), stateForIncident(incident)
    );
}

//
// Populate incident priority
//

function drawPriority() {
    selectOptionWithValue(
        $("#incident_priority"), priorityNameFromNumber(incident.priority)
    );
}

//
// Populate incident summary
//

function drawSummary() {
    $("#incident_summary").attr("value", summarizeIncident(incident));
}

//
// Populate Rangers list
//

var _rangerItem = null;

function drawRangers() {
    if (_rangerItem == null) {
        _rangerItem = $("#incident_rangers_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.ranger_handles) {
        var item = _rangerItem.clone();
        item.append(incident.ranger_handles[i]);
        items.push(item);
    }

    var container = $("#incident_rangers_list");
    container.empty();
    container.append(items);
}

//
// Populate incident types list
//

var _typesItem = null;

function drawIncidentTypes() {
    if (_typesItem == null) {
        _typesItem = $("#incident_types_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.incident_types) {
        var item = _typesItem.clone();
        item.append(incident.incident_types[i]);
        items.push(item);
    }

    var container = $("#incident_types_list");
    container.empty();
    container.append(items);
}

//
// Populate location
//

function drawLocationName() {
    if (incident.location != undefined) {
        if (incident.location.name != undefined) {
            $("#incident_location_name").attr("value", incident.location.name);
        }
    }
}

function drawLocationAddressRadialHour() {
    var hour = null;
    if (incident.location != undefined) {
        if (incident.location.radial_hour != undefined) {
            hour = padTwo(incident.location.radial_hour);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_hour"), hour
    );
}

function drawLocationAddressRadialMinute() {
    var minute = null;
    if (incident.location != undefined) {
        if (incident.location.radial_minute != undefined) {
            minute = normalizeMinute(incident.location.radial_minute);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_minute"), minute
    );
}

function drawLocationAddressConcentric() {
    var concentric = null;
    if (incident.location != undefined) {
        if (incident.location.concentric != undefined) {
            concentric = incident.location.concentric;
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_concentric"), concentric
    );
}

function drawLocationDescription() {
    if (incident.location != undefined) {
        if (incident.location.description != undefined) {
            $("#incident_location_description")
                .attr("value", incident.location.description)
                ;
        }
    }
}

//
// Populate report entry text
//

function reportEntryElement(entry) {
    // Build a container for the entry

    var entryContainer = $("<div />", {"class": "report_entry"});

    if (entry.system_entry) {
        entryContainer.addClass("report_entry_system");
    } else {
        entryContainer.addClass("report_entry_user");
    }

    // Add the timestamp and author

    metaDataContainer = $("<p />", {"class": "report_entry_metadata"})

    var timeStampContainer = timeElement(new Date(entry.created));
    timeStampContainer.addClass("report_entry_timestamp");

    metaDataContainer.append([timeStampContainer, ", "]);

    var author = entry.author;
    if (author == undefined) {
        author = "(unknown)";
    }
    var authorContainer = $("<span />");
    authorContainer.text(entry.author);
    authorContainer.addClass("report_entry_author");

    metaDataContainer.append(author);
    metaDataContainer.append(":");

    entryContainer.append(metaDataContainer);

    // Add report text

    var lines = entry.text.split("\n");
    for (var i in lines) {
        var textContainer = $("<p />", {"class": "report_entry_text"});
        textContainer.text(lines[i]);

        entryContainer.append(textContainer);
    }

    // Return container

    return entryContainer;
}

function drawReportEntries() {
    var container = $("#incident_report");
    container.empty();

    var entries = incident.report_entries;

    if (entries != undefined) {
        for (var i in entries) {
            container.append(reportEntryElement(entries[i]));
        }
    }
}

//
// Editing
//

function performEdit(element, jsonKey, transform) {
    var value = element.val();

    if (transform != undefined) {
        value = transform(value);
    }

    var edits = {"number": incident.number};

    var keyPath = jsonKey.split(".");
    var lastKey = keyPath.pop();

    var current = edits;
    for (var i in keyPath) {
        var next = {};
        current[keyPath[i]] = next;
        current = next;
    }
    current[lastKey] = value;

    var jsonText = JSON.stringify(edits);

    console.log("Sending edit: " + jsonText);

    var url = incidentsURL + "/" + incident.number;

    function ok(data, status, xhr) {
        console.log("Updated element: " + status);
        controlHasSuccess(element);
        // Clear success state after a 1s delay
        element.delay("1000").queue(function() {controlClear(element)});
    }

    function fail(xhr, status, error) {
        console.log("Oh no: " + status + ": " + error);
        controlHasError(element);
        loadAndDisplayIncident();
        window.alert("Failed to apply edit:\n" + error);
    }

    $.ajax({
        "url": url,
        "method": "POST",
        "contentType": "application/json",
        "data": jsonText,
        "success": ok,
        "error": fail,
    })
}

function editState() {
    performEdit($("#incident_state"), "state");
}

function editPriority() {
    performEdit($("#incident_priority"), "priority", Number);
}

function editSummary() {
    performEdit($("#incident_summary"), "summary");
}

function editLocationName() {
    performEdit($("#incident_location_name"), "location.name");
}

function editLocationAddressRadialHour() {
    performEdit(
        $("#incident_location_address_radial_hour"),
        "location.radial_hour",
        Number
    );
}

function editLocationAddressRadialMinute() {
    performEdit(
        $("#incident_location_address_radial_minute"),
        "location.radial_minute",
        Number
    );
}

function editLocationAddressConcentric() {
    performEdit(
        $("#incident_location_address_concentric"),
        "location.concentric",
        Number
    );
}

function editLocationDescription() {
    performEdit($("#incident_location_description"), "location.description");
}
<commit_msg>Add location type to location edits<commit_after>// See the file COPYRIGHT for copyright information.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//
// Initialize UI
//

function initIncidentPage() {
    function loadedBody() {
        addLocationAddressOptions();
        disableEditing();
        loadAndDisplayIncident();
    }

    loadBody(loadedBody);
}

//
// Load incident
//

var incident = null;

function loadIncident(success) {
    function loaded(data, status, request) {
        incident = data;

        if (success != undefined) {
            success();
        }
    }

    $.get(incidentsURL + "/" + incidentNumber, loaded, "json");
}

function loadAndDisplayIncident(success) {
    function loaded() {
        drawIncidentFields();
        enableEditing();

        if (success != undefined) {
            success();
        }
    }

    loadIncident(loaded);
}

//
// Draw all fields
//

function drawIncidentFields() {
    drawNumber();
    drawState();
    drawPriority();
    drawSummary();
    drawRangers();
    drawIncidentTypes();
    drawLocationName();
    drawLocationAddressRadialHour();
    drawLocationAddressRadialMinute();
    drawLocationAddressConcentric();
    drawLocationDescription();
    drawReportEntries();
}

//
// Add option elements to location address select elements
//

function addLocationAddressOptions() {
    var hours = range(1, 13);
    for (var i in hours) {
        var hour = padTwo(hours[i]);
        $("#incident_location_address_radial_hour")
            .append($("<option />", { "value": hour, "text": hour }))
            ;
    }

    var minutes = range(0, 12, 5);
    for (var i in minutes) {
        var minute = padTwo(minutes[i]);
        $("#incident_location_address_radial_minute")
            .append($("<option />", { "value": minute, "text": minute }))
            ;
    }

    for (var id in concentricStreetNameByID) {
        var name = concentricStreetNameByID[id];
        $("#incident_location_address_concentric")
            .append($("<option />", { "value": id, "text": name }))
            ;
    }
}

//
// Populate incident number
//

function drawNumber() {
    $("#incident_number").text(incident.number);
}

//
// Populate incident state
//

function drawState() {
    selectOptionWithValue(
        $("#incident_state"), stateForIncident(incident)
    );
}

//
// Populate incident priority
//

function drawPriority() {
    selectOptionWithValue(
        $("#incident_priority"), priorityNameFromNumber(incident.priority)
    );
}

//
// Populate incident summary
//

function drawSummary() {
    $("#incident_summary").attr("value", summarizeIncident(incident));
}

//
// Populate Rangers list
//

var _rangerItem = null;

function drawRangers() {
    if (_rangerItem == null) {
        _rangerItem = $("#incident_rangers_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.ranger_handles) {
        var item = _rangerItem.clone();
        item.append(incident.ranger_handles[i]);
        items.push(item);
    }

    var container = $("#incident_rangers_list");
    container.empty();
    container.append(items);
}

//
// Populate incident types list
//

var _typesItem = null;

function drawIncidentTypes() {
    if (_typesItem == null) {
        _typesItem = $("#incident_types_list")
            .children(".list-group-item:first")
            ;
    }

    var items = [];

    for (var i in incident.incident_types) {
        var item = _typesItem.clone();
        item.append(incident.incident_types[i]);
        items.push(item);
    }

    var container = $("#incident_types_list");
    container.empty();
    container.append(items);
}

//
// Populate location
//

function drawLocationName() {
    if (incident.location != undefined) {
        if (incident.location.name != undefined) {
            $("#incident_location_name").attr("value", incident.location.name);
        }
    }
}

function drawLocationAddressRadialHour() {
    var hour = null;
    if (incident.location != undefined) {
        if (incident.location.radial_hour != undefined) {
            hour = padTwo(incident.location.radial_hour);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_hour"), hour
    );
}

function drawLocationAddressRadialMinute() {
    var minute = null;
    if (incident.location != undefined) {
        if (incident.location.radial_minute != undefined) {
            minute = normalizeMinute(incident.location.radial_minute);
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_radial_minute"), minute
    );
}

function drawLocationAddressConcentric() {
    var concentric = null;
    if (incident.location != undefined) {
        if (incident.location.concentric != undefined) {
            concentric = incident.location.concentric;
        }
    }
    selectOptionWithValue(
        $("#incident_location_address_concentric"), concentric
    );
}

function drawLocationDescription() {
    if (incident.location != undefined) {
        if (incident.location.description != undefined) {
            $("#incident_location_description")
                .attr("value", incident.location.description)
                ;
        }
    }
}

//
// Populate report entry text
//

function reportEntryElement(entry) {
    // Build a container for the entry

    var entryContainer = $("<div />", {"class": "report_entry"});

    if (entry.system_entry) {
        entryContainer.addClass("report_entry_system");
    } else {
        entryContainer.addClass("report_entry_user");
    }

    // Add the timestamp and author

    metaDataContainer = $("<p />", {"class": "report_entry_metadata"})

    var timeStampContainer = timeElement(new Date(entry.created));
    timeStampContainer.addClass("report_entry_timestamp");

    metaDataContainer.append([timeStampContainer, ", "]);

    var author = entry.author;
    if (author == undefined) {
        author = "(unknown)";
    }
    var authorContainer = $("<span />");
    authorContainer.text(entry.author);
    authorContainer.addClass("report_entry_author");

    metaDataContainer.append(author);
    metaDataContainer.append(":");

    entryContainer.append(metaDataContainer);

    // Add report text

    var lines = entry.text.split("\n");
    for (var i in lines) {
        var textContainer = $("<p />", {"class": "report_entry_text"});
        textContainer.text(lines[i]);

        entryContainer.append(textContainer);
    }

    // Return container

    return entryContainer;
}

function drawReportEntries() {
    var container = $("#incident_report");
    container.empty();

    var entries = incident.report_entries;

    if (entries != undefined) {
        for (var i in entries) {
            container.append(reportEntryElement(entries[i]));
        }
    }
}

//
// Editing
//

function performEdit(element, jsonKey, transform) {
    var value = element.val();

    if (transform != undefined) {
        value = transform(value);
    }

    // Build a JSON object representing the requested edits

    var edits = {"number": incident.number};

    var keyPath = jsonKey.split(".");
    var lastKey = keyPath.pop();

    var current = edits;
    for (var i in keyPath) {
        var next = {};
        current[keyPath[i]] = next;
        current = next;
    }
    current[lastKey] = value;

    // Location must include type

    if (edits.location != undefined) {
        edits.location.type = "garett";  // UI only supports one type
    }

    // Send request to server

    var jsonText = JSON.stringify(edits);

    console.log("Sending edit: " + jsonText);

    var url = incidentsURL + "/" + incident.number;

    function ok(data, status, xhr) {
        console.log("Updated element: " + status);
        controlHasSuccess(element);
        // Clear success state after a 1s delay
        element.delay("1000").queue(function() {controlClear(element)});
    }

    function fail(xhr, status, error) {
        console.log("Oh no: " + status + ": " + error);
        controlHasError(element);
        loadAndDisplayIncident();
        window.alert("Failed to apply edit:\n" + error);
    }

    $.ajax({
        "url": url,
        "method": "POST",
        "contentType": "application/json",
        "data": jsonText,
        "success": ok,
        "error": fail,
    })
}

function editState() {
    performEdit($("#incident_state"), "state");
}

function editPriority() {
    performEdit($("#incident_priority"), "priority", Number);
}

function editSummary() {
    performEdit($("#incident_summary"), "summary");
}

function editLocationName() {
    performEdit($("#incident_location_name"), "location.name");
}

function editLocationAddressRadialHour() {
    performEdit(
        $("#incident_location_address_radial_hour"),
        "location.radial_hour",
        Number
    );
}

function editLocationAddressRadialMinute() {
    performEdit(
        $("#incident_location_address_radial_minute"),
        "location.radial_minute",
        Number
    );
}

function editLocationAddressConcentric() {
    performEdit(
        $("#incident_location_address_concentric"),
        "location.concentric",
        Number
    );
}

function editLocationDescription() {
    performEdit($("#incident_location_description"), "location.description");
}
<|endoftext|>


<commit_before>define(
  [
    'underscore',
    'dispatchers/Dispatcher',
    'stores/Store',
    'rsvp',
    'constants/ProjectVolumeConstants',
    'controllers/NotificationController',
    'models/Volume',
    'backbone',
    'globals'
  ],
  function (_, Dispatcher, Store, RSVP, ProjectVolumeConstants, NotificationController, Volume, Backbone, globals) {

    var _projectVolumes = {};
    var _isBeingFetched = {};

    //
    // Project Volume Model
    // Mainly a helper for generating add/remove urls
    //

    var ProjectVolume = Backbone.Model.extend({
      urlRoot: function() {
        return globals.API_ROOT + "/project/" + this.project.id + "/volume";
      },

      url: function () {
        var url = Backbone.Model.prototype.url.apply(this) + globals.slash();
        return url;
      },

      initialize: function(options){
        this.volume = options.volume;
        this.project = options.project;
        this.set("id", this.volume.id);
      }
    });

    var ProjectVolumeCollection = Backbone.Collection.extend({
      model: Volume,

      url: function () {
        var url = globals.API_ROOT + "/project/" + this.project.id + "/volume" + globals.slash();
        return url;
      },

      initialize: function(models, options){
        this.project = options.project;
      }
    });

    //
    // CRUD Operations
    //

    var fetchProjectVolumes = function (project) {
      _isBeingFetched[project.id] = true;
      var promise = new RSVP.Promise(function (resolve, reject) {
        var projectVolumes = new ProjectVolumeCollection(null, {
          project: project
        });
        projectVolumes.fetch().done(function () {
          _isBeingFetched[project.id] = false;
          _projectVolumes[project.id] = projectVolumes;
          resolve();
        });
      });
      return promise;
    };

    function addVolumeToProject(volume, project){
      var projectVolume = new ProjectVolume({
        volume: volume,
        project: project
      });

      projectVolume.save().done(function(){
        var successMessage = "Volume '" + volume.get('name') + "' added to Project '" + project.get('name') + "'";
        //NotificationController.success(successMessage);
      }).fail(function(){
        var failureMessage = "Error adding Volume '" + volume.get('name') + "' to Project '" + project.get('name') + "'.";
        NotificationController.error(failureMessage);
        _projectVolumes[project.id].remove(volume);
      });
      _projectVolumes[project.id].add(volume);
    }

    function removeVolumeFromProject(volume, project){
      var projectVolume = new ProjectVolume({
        volume: volume,
        project: project
      });

      projectVolume.destroy().done(function(){
        var successMessage = "Volume '" + volume.get('name') + "' removed from Project '" + project.get('name') + "'";
        //NotificationController.success(successMessage);
      }).fail(function(){
        var failureMessage = "Error removing Volume '" + volume.get('name') + "' from Project '" + project.get('name') + "'.";
        NotificationController.error(failureMessage);
        _projectVolumes[project.id].add(volume);
      });
      _projectVolumes[project.id].remove(volume);
    }

    //
    // Project Volume Store
    //

    var ProjectVolumeStore = {

      getVolumesInProject: function (project) {
        var projectVolumes = _projectVolumes[project.id];
        var volumesAreBeingFetched = _isBeingFetched[project.id];

        // If there are no volumes for the project, and the volumes aren't being fetched
        // already, then go fetch them, otherwise return the volumes we already have
        if(!projectVolumes && !volumesAreBeingFetched) {
          fetchProjectVolumes(project).then(function(){
            ProjectVolumeStore.emitChange();
          }.bind(this));
        }
        return projectVolumes;
      }

    };

    Dispatcher.register(function (payload) {
      var action = payload.action;

      switch (action.actionType) {
        case ProjectVolumeConstants.ADD_VOLUME_TO_PROJECT:
          addVolumeToProject(action.volume, action.project);
          break;

        case ProjectVolumeConstants.REMOVE_VOLUME_FROM_PROJECT:
          removeVolumeFromProject(action.volume, action.project);
          break;

        default:
          return true;
      }

      ProjectVolumeStore.emitChange();

      return true;
    });

    _.extend(ProjectVolumeStore, Store);

    return ProjectVolumeStore;
  });
<commit_msg>remove unused functions<commit_after>define(
  [
    'underscore',
    'dispatchers/Dispatcher',
    'stores/Store',
    'rsvp',
    'constants/ProjectVolumeConstants',
    'controllers/NotificationController',
    'models/Volume',
    'backbone',
    'globals'
  ],
  function (_, Dispatcher, Store, RSVP, ProjectVolumeConstants, NotificationController, Volume, Backbone, globals) {

    var _projectVolumes = {};
    var _isBeingFetched = {};

    //
    // CRUD Operations
    //

    function addVolumeToProject(volume, project){
      var projectVolume = new ProjectVolume({
        volume: volume,
        project: project
      });

      projectVolume.save().done(function(){
        var successMessage = "Volume '" + volume.get('name') + "' added to Project '" + project.get('name') + "'";
        //NotificationController.success(successMessage);
      }).fail(function(){
        var failureMessage = "Error adding Volume '" + volume.get('name') + "' to Project '" + project.get('name') + "'.";
        NotificationController.error(failureMessage);
        _projectVolumes[project.id].remove(volume);
      });
      _projectVolumes[project.id].add(volume);
    }

    function removeVolumeFromProject(volume, project){
      var projectVolume = new ProjectVolume({
        volume: volume,
        project: project
      });

      projectVolume.destroy().done(function(){
        var successMessage = "Volume '" + volume.get('name') + "' removed from Project '" + project.get('name') + "'";
        //NotificationController.success(successMessage);
      }).fail(function(){
        var failureMessage = "Error removing Volume '" + volume.get('name') + "' from Project '" + project.get('name') + "'.";
        NotificationController.error(failureMessage);
        _projectVolumes[project.id].add(volume);
      });
      _projectVolumes[project.id].remove(volume);
    }

    //
    // Project Volume Store
    //

    var ProjectVolumeStore = {

    };

    Dispatcher.register(function (payload) {
      var action = payload.action;

      switch (action.actionType) {
        case ProjectVolumeConstants.ADD_VOLUME_TO_PROJECT:
          addVolumeToProject(action.volume, action.project);
          break;

        case ProjectVolumeConstants.REMOVE_VOLUME_FROM_PROJECT:
          removeVolumeFromProject(action.volume, action.project);
          break;

        default:
          return true;
      }

      ProjectVolumeStore.emitChange();

      return true;
    });

    _.extend(ProjectVolumeStore, Store);

    return ProjectVolumeStore;
  });
<|endoftext|>