<commit_before>const crypto = require('crypto')
const striptags = require('striptags');
const {PromiseUtils, IMAPConnection} = require('isomorphic-core')
const {DatabaseTypes: {JSONColumn, JSONArrayColumn}} = require('isomorphic-core');
const {Errors: {APIError}} = require('isomorphic-core')

const SNIPPET_LENGTH = 191;

function getLengthValidator(fieldName, min, max) {
  return (stringifiedArr) => {
    const arr = JSON.parse(stringifiedArr);
    if ((arr.length < min) || (arr.length > max)) {
      throw new Error(`Value for ${fieldName} must have a length in range [${min}-${max}]. Value: ${stringifiedArr}`);
    }
  };
}

function validateRecipientsPresent(message) {
  if (message.getRecipients().length === 0) {
    throw new APIError(`No recipients specified`, 400);
  }
}

module.exports = (sequelize, Sequelize) => {
  return sequelize.define('message', {
    id: { type: Sequelize.STRING(65), primaryKey: true },
    accountId: { type: Sequelize.STRING, allowNull: false },
    version: Sequelize.INTEGER,
    headerMessageId: Sequelize.STRING,
    gMsgId: { type: Sequelize.STRING, allowNull: true },
    body: Sequelize.TEXT,
    headers: JSONColumn('headers'),
    subject: Sequelize.STRING(500),
    snippet: Sequelize.STRING(255),
    date: Sequelize.DATE,
    isDraft: Sequelize.BOOLEAN,
    isSent: Sequelize.BOOLEAN,
    isSending: Sequelize.BOOLEAN,
    unread: Sequelize.BOOLEAN,
    starred: Sequelize.BOOLEAN,
    processed: Sequelize.INTEGER,
    to: JSONArrayColumn('to'),
    from: JSONArrayColumn('from', {
      allowNull: true,
    }),
    cc: JSONArrayColumn('cc'),
    bcc: JSONArrayColumn('bcc'),
    replyTo: JSONArrayColumn('replyTo', {
      allowNull: true,
    }),
    inReplyTo: { type: Sequelize.STRING, allowNull: true},
    references: JSONArrayColumn('references'),
    folderImapUID: { type: Sequelize.STRING, allowNull: true},
    folderImapXGMLabels: { type: Sequelize.TEXT, allowNull: true},
    uploads: JSONArrayColumn('uploads', {
      validate: {
        uploadStructure(stringifiedArr) {
          const arr = JSON.parse(stringifiedArr);
          const requiredKeys = ['filename', 'targetPath', 'id']
          arr.forEach((upload) => {
            requiredKeys.forEach((key) => {
              if (!upload.hasOwnPropery(key)) {
                throw new Error(`Upload must have '${key}' key.`)
              }
            })
          })
        },
      },
    }),
  }, {
    indexes: [
      {
        unique: true,
        fields: ['id'],
      },
      {
        unique: false,
        fields: ['folderId'],
      },
      {
        unique: false,
        fields: ['threadId'],
      },
    ],
    hooks: {
      beforeUpdate(message) {
        // Update the snippet if the body has changed
        if (!message.changed('body')) { return; }

        const plainText = striptags(message.body);
        // consolidate whitespace groups into single spaces and then truncate
        message.snippet = plainText.split(/\s+/).join(" ").substring(0, SNIPPET_LENGTH)
      },
    },
    classMethods: {
      associate({Message, Folder, Label, File, Thread, MessageLabel}) {
        Message.belongsTo(Thread)
        Message.belongsTo(Folder)
        Message.belongsToMany(Label, {through: MessageLabel})
        Message.hasMany(File)
      },

      hash({from = [], to = [], cc = [], bcc = [], date = '', subject = '', headerMessageId = ''} = {}) {
        const emails = from.concat(to, cc, bcc)
        .map(participant => participant.email)
        .sort();
        const participants = emails.join('')
        const data = `${date}-${subject}-${participants}-${headerMessageId}`;
        return crypto.createHash('sha256').update(data, 'utf8').digest('hex');
      },

      buildHeaderMessageId(id) {
        return `<${id}@mailer.nylas.com>`
      },

      async findMultiSendMessage(db, messageId) {
        const message = await this.findById(messageId, {
          include: [
            {model: db.Folder},
          ],
        })
        if (!message) {
          throw new APIError(`Couldn't find multi-send message ${messageId}`, 400);
        }
        if (message.isSent || !message.isSending) {
          throw new APIError(`Message ${messageId} is not a multi-send message`, 400);
        }
        return message;
      },

      requiredAssociationsForJSON({Folder, Label}) {
        return [
          {model: Folder},
          {model: Label},
        ]
      },
    },
    instanceMethods: {
      getRecipients() {
        const {to, cc, bcc} = this;
        return [].concat(to, cc, bcc);
      },

      async setLabelsFromXGM(xGmLabels, {Label, preloadedLabels} = {}) {
        this.folderImapXGMLabels = JSON.stringify(xGmLabels);
        const labels = await Label.findXGMLabels(xGmLabels, {preloadedLabels})
        return this.setLabels(labels);
      },

      setIsSent(val) {
        if (val) {
          this.isDraft = false
          this.isSending = false
        }
        this.isSent = val
      },

      setIsSending(val) {
        if (val) {
          if (this.isSent || this.isSending) {
            throw new APIError('Cannot mark a sent message as sending', 400);
          }
          validateRecipientsPresent(this);
          this.isDraft = false;
        }
        this.isSending = val
      },

      fetchRaw({account, db, logger}) {
        const settings = Object.assign({}, account.connectionSettings, account.decryptedCredentials())
        return PromiseUtils.props({
          folder: this.getFolder(),
          connection: IMAPConnection.connect({db, settings, logger}),
        })
        .then(({folder, connection}) => {
          return connection.openBox(folder.name)
          .then((imapBox) => imapBox.fetchMessage(this.folderImapUID))
          .then((message) => {
            if (message) {
              return Promise.resolve(`${message.headers}${message.parts.TEXT}`)
            }
            return Promise.reject(new Error(`Unable to fetch raw message for Message ${this.id}`))
          })
          .finally(() => connection.end())
        })
      },

      toJSON() {
        if (this.folderId && !this.folder) {
          throw new Error("Message.toJSON called on a message where folder was not eagerly loaded.")
        }

        // When we request messages as a sub-object of a thread, we only
        // request the `id` field from the database. We still toJSON the
        // Message though and need to protect `this.date` from null
        // errors.
        // Folders and labels can be null if the message is sending!
        return {
          id: this.id,
          account_id: this.accountId,
          object: this.isDraft ? 'draft' : 'message',
          body: this.body,
          subject: this.subject,
          snippet: this.snippet,
          to: this.to,
          from: this.from,
          cc: this.cc,
          bcc: this.bcc,
          reply_to: this.replyTo,
          date: this.date ? this.date.getTime() / 1000.0 : null,
          unread: this.unread,
          starred: this.starred,
          folder: this.folder ? this.folder.toJSON() : null,
          labels: this.labels ? this.labels.map(l => l.toJSON()) : null,
          imap_uid: this.folderImapUID,
          thread_id: this.threadId,
        };
      },
    },
  });
};
<commit_msg>[local-sync] Remove no-longer-used getLengthValidator function<commit_after>const crypto = require('crypto')
const striptags = require('striptags');
const {PromiseUtils, IMAPConnection} = require('isomorphic-core')
const {DatabaseTypes: {JSONColumn, JSONArrayColumn}} = require('isomorphic-core');
const {Errors: {APIError}} = require('isomorphic-core')

const SNIPPET_LENGTH = 191;

function validateRecipientsPresent(message) {
  if (message.getRecipients().length === 0) {
    throw new APIError(`No recipients specified`, 400);
  }
}

module.exports = (sequelize, Sequelize) => {
  return sequelize.define('message', {
    id: { type: Sequelize.STRING(65), primaryKey: true },
    accountId: { type: Sequelize.STRING, allowNull: false },
    version: Sequelize.INTEGER,
    headerMessageId: Sequelize.STRING,
    gMsgId: { type: Sequelize.STRING, allowNull: true },
    body: Sequelize.TEXT,
    headers: JSONColumn('headers'),
    subject: Sequelize.STRING(500),
    snippet: Sequelize.STRING(255),
    date: Sequelize.DATE,
    isDraft: Sequelize.BOOLEAN,
    isSent: Sequelize.BOOLEAN,
    isSending: Sequelize.BOOLEAN,
    unread: Sequelize.BOOLEAN,
    starred: Sequelize.BOOLEAN,
    processed: Sequelize.INTEGER,
    to: JSONArrayColumn('to'),
    from: JSONArrayColumn('from', {
      allowNull: true,
    }),
    cc: JSONArrayColumn('cc'),
    bcc: JSONArrayColumn('bcc'),
    replyTo: JSONArrayColumn('replyTo', {
      allowNull: true,
    }),
    inReplyTo: { type: Sequelize.STRING, allowNull: true},
    references: JSONArrayColumn('references'),
    folderImapUID: { type: Sequelize.STRING, allowNull: true},
    folderImapXGMLabels: { type: Sequelize.TEXT, allowNull: true},
    uploads: JSONArrayColumn('uploads', {
      validate: {
        uploadStructure(stringifiedArr) {
          const arr = JSON.parse(stringifiedArr);
          const requiredKeys = ['filename', 'targetPath', 'id']
          arr.forEach((upload) => {
            requiredKeys.forEach((key) => {
              if (!upload.hasOwnPropery(key)) {
                throw new Error(`Upload must have '${key}' key.`)
              }
            })
          })
        },
      },
    }),
  }, {
    indexes: [
      {
        unique: true,
        fields: ['id'],
      },
      {
        unique: false,
        fields: ['folderId'],
      },
      {
        unique: false,
        fields: ['threadId'],
      },
    ],
    hooks: {
      beforeUpdate(message) {
        // Update the snippet if the body has changed
        if (!message.changed('body')) { return; }

        const plainText = striptags(message.body);
        // consolidate whitespace groups into single spaces and then truncate
        message.snippet = plainText.split(/\s+/).join(" ").substring(0, SNIPPET_LENGTH)
      },
    },
    classMethods: {
      associate({Message, Folder, Label, File, Thread, MessageLabel}) {
        Message.belongsTo(Thread)
        Message.belongsTo(Folder)
        Message.belongsToMany(Label, {through: MessageLabel})
        Message.hasMany(File)
      },

      hash({from = [], to = [], cc = [], bcc = [], date = '', subject = '', headerMessageId = ''} = {}) {
        const emails = from.concat(to, cc, bcc)
        .map(participant => participant.email)
        .sort();
        const participants = emails.join('')
        const data = `${date}-${subject}-${participants}-${headerMessageId}`;
        return crypto.createHash('sha256').update(data, 'utf8').digest('hex');
      },

      buildHeaderMessageId(id) {
        return `<${id}@mailer.nylas.com>`
      },

      async findMultiSendMessage(db, messageId) {
        const message = await this.findById(messageId, {
          include: [
            {model: db.Folder},
          ],
        })
        if (!message) {
          throw new APIError(`Couldn't find multi-send message ${messageId}`, 400);
        }
        if (message.isSent || !message.isSending) {
          throw new APIError(`Message ${messageId} is not a multi-send message`, 400);
        }
        return message;
      },

      requiredAssociationsForJSON({Folder, Label}) {
        return [
          {model: Folder},
          {model: Label},
        ]
      },
    },
    instanceMethods: {
      getRecipients() {
        const {to, cc, bcc} = this;
        return [].concat(to, cc, bcc);
      },

      async setLabelsFromXGM(xGmLabels, {Label, preloadedLabels} = {}) {
        this.folderImapXGMLabels = JSON.stringify(xGmLabels);
        const labels = await Label.findXGMLabels(xGmLabels, {preloadedLabels})
        return this.setLabels(labels);
      },

      setIsSent(val) {
        if (val) {
          this.isDraft = false
          this.isSending = false
        }
        this.isSent = val
      },

      setIsSending(val) {
        if (val) {
          if (this.isSent || this.isSending) {
            throw new APIError('Cannot mark a sent message as sending', 400);
          }
          validateRecipientsPresent(this);
          this.isDraft = false;
        }
        this.isSending = val
      },

      fetchRaw({account, db, logger}) {
        const settings = Object.assign({}, account.connectionSettings, account.decryptedCredentials())
        return PromiseUtils.props({
          folder: this.getFolder(),
          connection: IMAPConnection.connect({db, settings, logger}),
        })
        .then(({folder, connection}) => {
          return connection.openBox(folder.name)
          .then((imapBox) => imapBox.fetchMessage(this.folderImapUID))
          .then((message) => {
            if (message) {
              return Promise.resolve(`${message.headers}${message.parts.TEXT}`)
            }
            return Promise.reject(new Error(`Unable to fetch raw message for Message ${this.id}`))
          })
          .finally(() => connection.end())
        })
      },

      toJSON() {
        if (this.folderId && !this.folder) {
          throw new Error("Message.toJSON called on a message where folder was not eagerly loaded.")
        }

        // When we request messages as a sub-object of a thread, we only
        // request the `id` field from the database. We still toJSON the
        // Message though and need to protect `this.date` from null
        // errors.
        // Folders and labels can be null if the message is sending!
        return {
          id: this.id,
          account_id: this.accountId,
          object: this.isDraft ? 'draft' : 'message',
          body: this.body,
          subject: this.subject,
          snippet: this.snippet,
          to: this.to,
          from: this.from,
          cc: this.cc,
          bcc: this.bcc,
          reply_to: this.replyTo,
          date: this.date ? this.date.getTime() / 1000.0 : null,
          unread: this.unread,
          starred: this.starred,
          folder: this.folder ? this.folder.toJSON() : null,
          labels: this.labels ? this.labels.map(l => l.toJSON()) : null,
          imap_uid: this.folderImapUID,
          thread_id: this.threadId,
        };
      },
    },
  });
};
<|endoftext|>


<commit_before>import WeCropper from 'we-cropper'

Component({
  properties: {
    // 这里定义了innerText属性，属性值可以在组件使用时指定
    option: {
      type: Object,
      value: {}
    }
  },
  attached() {
    if (this.options) {
      this.init()
    } else {
      console.warn(
        '[miniprogram-cropper] ' +
        '请传入option参数\n参数配置见文档：' +
        'https://we-plugin.github.io/we-cropper/#/api'
      )
    }
  },
  methods: {
    init() {
      const {
        id,
        targetId
      } = this.properties.options
      const option = Object.assign(this.properties.options, {
        ctx: wx.createCanvasContext(id, this),
        targetCtx: wx.createCanvasContext(targetId, this)
      })

      this.__wecropper__ = new WeCropper(option)
    },
    // 这里是一个自定义方法
    touchStart(e) {
      this.__wecropper__.touchStart(e)
    },

    touchMove(e) {
      this.__wecropper__.touchMove(e)
    },

    touchEnd(e) {
      this.__wecropper__.touchEnd(e)
    },

    pushOrign(src) {
      this.__wecropper__.pushOrign(src)
    },

    updateCanvas() {
      this.__wecropper__.updateCanvas()
    },

    getCropperBase64(fn) {
      return this.__wecropper__.getCropperBase64(fn)
    },

    getCropperImage(opt, fn) {
      const option = Object.assign({
        // 传入自定义组件上下文
        componentContext: this,
      }, opt)
      return this.__wecropper__.getCropperImage(option, fn)
    }
  }
})
<commit_msg>fix options<commit_after>import WeCropper from 'we-cropper'

Component({
  properties: {
    // 这里定义了innerText属性，属性值可以在组件使用时指定
    options: {
      type: Object,
      value: {}
    }
  },
  attached() {
    if (this.properties.options) {
      this.init()
    } else {
      console.warn(
        '[miniprogram-cropper] ' +
        '请传入options参数\n参数配置见文档：' +
        'https://we-plugin.github.io/we-cropper/#/api'
      )
    }
  },
  methods: {
    init() {
      const {
        id,
        targetId
      } = this.properties.options
      const option = Object.assign(this.properties.options, {
        ctx: wx.createCanvasContext(id, this),
        targetCtx: wx.createCanvasContext(targetId, this)
      })

      this.__wecropper__ = new WeCropper(option)
    },
    // 这里是一个自定义方法
    touchStart(e) {
      this.__wecropper__.touchStart(e)
    },

    touchMove(e) {
      this.__wecropper__.touchMove(e)
    },

    touchEnd(e) {
      this.__wecropper__.touchEnd(e)
    },

    pushOrign(src) {
      this.__wecropper__.pushOrign(src)
    },

    updateCanvas() {
      this.__wecropper__.updateCanvas()
    },

    getCropperBase64(fn) {
      return this.__wecropper__.getCropperBase64(fn)
    },

    getCropperImage(opt, fn) {
      const option = Object.assign({
        // 传入自定义组件上下文
        componentContext: this,
      }, opt)
      return this.__wecropper__.getCropperImage(option, fn)
    }
  }
})
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Builds app binaries for OS X, Linux, and Windows.
 */

var config = require('../config')
var cp = require('child_process')
var electronPackager = require('electron-packager')
var fs = require('fs')
var path = require('path')
var pkg = require('../package.json')

var BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION

function build () {
  var platform = process.argv[2]
  if (platform === '--darwin') {
    buildDarwin(printDone)
  } else if (platform === '--win32') {
    buildWin32(printDone)
  } else if (platform === '--linux') {
    buildLinux(printDone)
  } else {
    buildDarwin(function (err, buildPath) {
      printDone(err, buildPath)
      buildWin32(function (err, buildPath) {
        printDone(err, buildPath)
        buildLinux(printDone)
      })
    })
  }
}

var all = {
  // Build 64 bit binaries only.
  arch: 'x64',

  // The application source directory.
  dir: config.ROOT_PATH,

  // The release version of the application. Maps to the `ProductVersion` metadata
  // property on Windows, and `CFBundleShortVersionString` on OS X.
  'app-version': pkg.version,

  // Package the application's source code into an archive, using Electron's archive
  // format. Mitigates issues around long path names on Windows and slightly speeds up
  // require().
  asar: true,

  // A glob expression, that unpacks the files with matching names to the
  // "app.asar.unpacked" directory.
  'asar-unpack': 'WebTorrent*',

  // The build version of the application. Maps to the FileVersion metadata property on
  // Windows, and CFBundleVersion on OS X. We're using the short git hash (e.g. 'e7d837e')
  // Windows requires the build version to start with a number :/ so we stick on a prefix
  'build-version': '0-' + cp.execSync('git rev-parse --short HEAD').toString().replace('\n', ''),

  // Pattern which specifies which files to ignore when copying files to create the
  // package(s).
  ignore: /^\/dist|\/(appveyor.yml|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^\/]*|.*\.md|.*\.markdown)$/,

  // The application name.
  name: config.APP_NAME,

  // The base directory where the finished package(s) are created.
  out: path.join(config.ROOT_PATH, 'dist'),

  // Replace an already existing output directory.
  overwrite: true,

  // Runs `npm prune --production` which remove the packages specified in
  // "devDependencies" before starting to package the app.
  prune: true,

  // The Electron version with which the app is built (without the leading 'v')
  version: pkg.devDependencies['electron-prebuilt']
}

var darwin = {
  platform: 'darwin',

  // The bundle identifier to use in the application's plist (OS X only).
  'app-bundle-id': 'io.webtorrent.app',

  // The application category type, as shown in the Finder via "View" -> "Arrange by
  // Application Category" when viewing the Applications directory (OS X only).
  'app-category-type': 'public.app-category.utilities',

  // The bundle identifier to use in the application helper's plist (OS X only).
  'helper-bundle-id': 'io.webtorrent.app.helper',

  // Application icon.
  icon: config.APP_ICON + '.icns'
}

var win32 = {
  platform: 'win32',

  // Object hash of application metadata to embed into the executable (Windows only)
  'version-string': {

    // Company that produced the file.
    CompanyName: config.APP_NAME,

    // Copyright notices that apply to the file.
    LegalCopyright: config.APP_COPYRIGHT,

    // Name of the program, displayed to users
    FileDescription: config.APP_NAME,

    // Original name of the file, not including a path. This information enables an
    // application to determine whether a file has been renamed by a user. The format of
    // the name depends on the file system for which the file was created.
    OriginalFilename: 'WebTorrent.exe',

    // Name of the product with which the file is distributed.
    ProductName: config.APP_NAME,

    // Internal name of the file, if one exists, for example, a module name if the file
    // is a dynamic-link library. If the file has no internal name, this string should be
    // the original filename, without extension. This string is required.
    InternalName: config.APP_NAME
  },

  // Application icon.
  icon: config.APP_ICON + '.ico'
}

var linux = {
  platform: 'linux'

  // Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
}

build()

function buildDarwin (cb) {
  var appDmg = require('appdmg')
  var plist = require('plist')
  var sign = require('electron-osx-sign')

  electronPackager(Object.assign({}, all, darwin), function (err, buildPath) {
    if (err) return cb(err)

    var appPath = path.join(buildPath[0], config.APP_NAME + '.app')
    var contentsPath = path.join(appPath, 'Contents')
    var resourcesPath = path.join(contentsPath, 'Resources')
    var infoPlistPath = path.join(contentsPath, 'Info.plist')
    var infoPlist = plist.parse(fs.readFileSync(infoPlistPath, 'utf8'))

    infoPlist.CFBundleDocumentTypes = [
      {
        CFBundleTypeExtensions: [ 'torrent' ],
        CFBundleTypeIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
        CFBundleTypeName: 'BitTorrent Document',
        CFBundleTypeRole: 'Editor',
        LSHandlerRank: 'Owner',
        LSItemContentTypes: [ 'org.bittorrent.torrent' ]
      },
      {
        CFBundleTypeName: 'Any',
        CFBundleTypeOSTypes: [ '****' ],
        CFBundleTypeRole: 'Editor',
        LSHandlerRank: 'Owner',
        LSTypeIsPackage: false
      }
    ]

    infoPlist.CFBundleURLTypes = [
      {
        CFBundleTypeRole: 'Editor',
        CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
        CFBundleURLName: 'BitTorrent Magnet URL',
        CFBundleURLSchemes: [ 'magnet' ]
      }
    ]

    infoPlist.NSHumanReadableCopyright = config.APP_COPYRIGHT

    fs.writeFileSync(infoPlistPath, plist.build(infoPlist))

    // Copy torrent file icon into app bundle
    cp.execSync(`cp ${config.APP_FILE_ICON + '.icns'} ${resourcesPath}`)

    if (process.platform === 'darwin') {
      /*
       * Sign the app with Apple Developer ID certificate. We sign the app for 2 reasons:
       *   - So the auto-updater (Squirrrel.Mac) can check that app updates are signed by
       *     the same author as the current version.
       *   - So users will not a see a warning about the app coming from an "Unidentified
       *     Developer" when they open it for the first time (OS X Gatekeeper).
       *
       * To sign an OS X app for distribution outside the App Store, the following are
       * required:
       *   - Xcode
       *   - Xcode Command Line Tools (xcode-select --install)
       *   - Membership in the Apple Developer Program
       */
      var signOpts = {
        app: appPath,
        platform: 'darwin',
        verbose: true
      }

      sign(signOpts, function (err) {
        if (err) return cb(err)

        // Create .zip file (used by the auto-updater)
        var zipPath = path.join(buildPath[0], BUILD_NAME + '.zip')
        cp.execSync(`zip -r -y ${zipPath} ${appPath}`)

        // Create a .dmg (OS X disk image) file, for easy user installation.
        var dmgOpts = {
          basepath: config.ROOT_PATH,
          target: path.join(buildPath[0], BUILD_NAME + '.dmg'),
          specification: {
            title: config.APP_NAME,
            icon: config.APP_ICON + '.icns',
            background: path.join(config.STATIC_PATH, 'appdmg.png'),
            'icon-size': 128,
            contents: [
              { x: 122, y: 240, type: 'file', path: appPath },
              { x: 380, y: 240, type: 'link', path: '/Applications' },
              // Hide hidden icons out of view, for users who have hidden files shown.
              // https://github.com/LinusU/node-appdmg/issues/45#issuecomment-153924954
              { x: 50, y: 500, type: 'position', path: '.background' },
              { x: 100, y: 500, type: 'position', path: '.DS_Store' },
              { x: 150, y: 500, type: 'position', path: '.Trashes' },
              { x: 200, y: 500, type: 'position', path: '.VolumeIcon.icns' }
            ]
          }
        }

        var dmg = appDmg(dmgOpts)
        dmg.on('error', cb)
        dmg.on('progress', function (info) {
          if (info.type === 'step-begin') console.log(info.title + '...')
        })
        dmg.on('finish', function (info) {
          cb(null, buildPath)
        })
      })
    }
  })
}

function buildWin32 (cb) {
  electronPackager(Object.assign({}, all, win32), cb)
}

function buildLinux (cb) {
  electronPackager(Object.assign({}, all, linux), cb)
}

function printDone (err, buildPath) {
  if (err) console.error(err.message || err)
  else console.log('Built ' + buildPath[0])
}
<commit_msg>fix zip bundle<commit_after>#!/usr/bin/env node

/**
 * Builds app binaries for OS X, Linux, and Windows.
 */

var config = require('../config')
var cp = require('child_process')
var electronPackager = require('electron-packager')
var fs = require('fs')
var path = require('path')
var pkg = require('../package.json')

var BUILD_NAME = config.APP_NAME + '-v' + config.APP_VERSION

function build () {
  var platform = process.argv[2]
  if (platform === '--darwin') {
    buildDarwin(printDone)
  } else if (platform === '--win32') {
    buildWin32(printDone)
  } else if (platform === '--linux') {
    buildLinux(printDone)
  } else {
    buildDarwin(function (err, buildPath) {
      printDone(err, buildPath)
      buildWin32(function (err, buildPath) {
        printDone(err, buildPath)
        buildLinux(printDone)
      })
    })
  }
}

var all = {
  // Build 64 bit binaries only.
  arch: 'x64',

  // The application source directory.
  dir: config.ROOT_PATH,

  // The release version of the application. Maps to the `ProductVersion` metadata
  // property on Windows, and `CFBundleShortVersionString` on OS X.
  'app-version': pkg.version,

  // Package the application's source code into an archive, using Electron's archive
  // format. Mitigates issues around long path names on Windows and slightly speeds up
  // require().
  asar: true,

  // A glob expression, that unpacks the files with matching names to the
  // "app.asar.unpacked" directory.
  'asar-unpack': 'WebTorrent*',

  // The build version of the application. Maps to the FileVersion metadata property on
  // Windows, and CFBundleVersion on OS X. We're using the short git hash (e.g. 'e7d837e')
  // Windows requires the build version to start with a number :/ so we stick on a prefix
  'build-version': '0-' + cp.execSync('git rev-parse --short HEAD').toString().replace('\n', ''),

  // Pattern which specifies which files to ignore when copying files to create the
  // package(s).
  ignore: /^\/dist|\/(appveyor.yml|AUTHORS|CONTRIBUTORS|bench|benchmark|benchmark\.js|bin|bower\.json|component\.json|coverage|doc|docs|docs\.mli|dragdrop\.min\.js|example|examples|example\.html|example\.js|externs|ipaddr\.min\.js|Makefile|min|minimist|perf|rusha|simplepeer\.min\.js|simplewebsocket\.min\.js|static\/screenshot\.png|test|tests|test\.js|tests\.js|webtorrent\.min\.js|\.[^\/]*|.*\.md|.*\.markdown)$/,

  // The application name.
  name: config.APP_NAME,

  // The base directory where the finished package(s) are created.
  out: path.join(config.ROOT_PATH, 'dist'),

  // Replace an already existing output directory.
  overwrite: true,

  // Runs `npm prune --production` which remove the packages specified in
  // "devDependencies" before starting to package the app.
  prune: true,

  // The Electron version with which the app is built (without the leading 'v')
  version: pkg.devDependencies['electron-prebuilt']
}

var darwin = {
  platform: 'darwin',

  // The bundle identifier to use in the application's plist (OS X only).
  'app-bundle-id': 'io.webtorrent.app',

  // The application category type, as shown in the Finder via "View" -> "Arrange by
  // Application Category" when viewing the Applications directory (OS X only).
  'app-category-type': 'public.app-category.utilities',

  // The bundle identifier to use in the application helper's plist (OS X only).
  'helper-bundle-id': 'io.webtorrent.app.helper',

  // Application icon.
  icon: config.APP_ICON + '.icns'
}

var win32 = {
  platform: 'win32',

  // Object hash of application metadata to embed into the executable (Windows only)
  'version-string': {

    // Company that produced the file.
    CompanyName: config.APP_NAME,

    // Copyright notices that apply to the file.
    LegalCopyright: config.APP_COPYRIGHT,

    // Name of the program, displayed to users
    FileDescription: config.APP_NAME,

    // Original name of the file, not including a path. This information enables an
    // application to determine whether a file has been renamed by a user. The format of
    // the name depends on the file system for which the file was created.
    OriginalFilename: 'WebTorrent.exe',

    // Name of the product with which the file is distributed.
    ProductName: config.APP_NAME,

    // Internal name of the file, if one exists, for example, a module name if the file
    // is a dynamic-link library. If the file has no internal name, this string should be
    // the original filename, without extension. This string is required.
    InternalName: config.APP_NAME
  },

  // Application icon.
  icon: config.APP_ICON + '.ico'
}

var linux = {
  platform: 'linux'

  // Note: Application icon for Linux is specified via the BrowserWindow `icon` option.
}

build()

function buildDarwin (cb) {
  var appDmg = require('appdmg')
  var plist = require('plist')
  var sign = require('electron-osx-sign')

  electronPackager(Object.assign({}, all, darwin), function (err, buildPath) {
    if (err) return cb(err)

    var appPath = path.join(buildPath[0], config.APP_NAME + '.app')
    var contentsPath = path.join(appPath, 'Contents')
    var resourcesPath = path.join(contentsPath, 'Resources')
    var infoPlistPath = path.join(contentsPath, 'Info.plist')
    var infoPlist = plist.parse(fs.readFileSync(infoPlistPath, 'utf8'))

    infoPlist.CFBundleDocumentTypes = [
      {
        CFBundleTypeExtensions: [ 'torrent' ],
        CFBundleTypeIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
        CFBundleTypeName: 'BitTorrent Document',
        CFBundleTypeRole: 'Editor',
        LSHandlerRank: 'Owner',
        LSItemContentTypes: [ 'org.bittorrent.torrent' ]
      },
      {
        CFBundleTypeName: 'Any',
        CFBundleTypeOSTypes: [ '****' ],
        CFBundleTypeRole: 'Editor',
        LSHandlerRank: 'Owner',
        LSTypeIsPackage: false
      }
    ]

    infoPlist.CFBundleURLTypes = [
      {
        CFBundleTypeRole: 'Editor',
        CFBundleURLIconFile: path.basename(config.APP_FILE_ICON) + '.icns',
        CFBundleURLName: 'BitTorrent Magnet URL',
        CFBundleURLSchemes: [ 'magnet' ]
      }
    ]

    infoPlist.NSHumanReadableCopyright = config.APP_COPYRIGHT

    fs.writeFileSync(infoPlistPath, plist.build(infoPlist))

    // Copy torrent file icon into app bundle
    cp.execSync(`cp ${config.APP_FILE_ICON + '.icns'} ${resourcesPath}`)

    if (process.platform === 'darwin') {
      /*
       * Sign the app with Apple Developer ID certificate. We sign the app for 2 reasons:
       *   - So the auto-updater (Squirrrel.Mac) can check that app updates are signed by
       *     the same author as the current version.
       *   - So users will not a see a warning about the app coming from an "Unidentified
       *     Developer" when they open it for the first time (OS X Gatekeeper).
       *
       * To sign an OS X app for distribution outside the App Store, the following are
       * required:
       *   - Xcode
       *   - Xcode Command Line Tools (xcode-select --install)
       *   - Membership in the Apple Developer Program
       */
      var signOpts = {
        app: appPath,
        platform: 'darwin',
        verbose: true
      }

      sign(signOpts, function (err) {
        if (err) return cb(err)

        // Create .zip file (used by the auto-updater)
        var zipPath = path.join(buildPath[0], BUILD_NAME + '.zip')
        cp.execSync(`pushd ${buildPath[0]} && zip -r -y ${zipPath} ${config.APP_NAME + '.app'} && popd`)

        // Create a .dmg (OS X disk image) file, for easy user installation.
        var dmgOpts = {
          basepath: config.ROOT_PATH,
          target: path.join(buildPath[0], BUILD_NAME + '.dmg'),
          specification: {
            title: config.APP_NAME,
            icon: config.APP_ICON + '.icns',
            background: path.join(config.STATIC_PATH, 'appdmg.png'),
            'icon-size': 128,
            contents: [
              { x: 122, y: 240, type: 'file', path: appPath },
              { x: 380, y: 240, type: 'link', path: '/Applications' },
              // Hide hidden icons out of view, for users who have hidden files shown.
              // https://github.com/LinusU/node-appdmg/issues/45#issuecomment-153924954
              { x: 50, y: 500, type: 'position', path: '.background' },
              { x: 100, y: 500, type: 'position', path: '.DS_Store' },
              { x: 150, y: 500, type: 'position', path: '.Trashes' },
              { x: 200, y: 500, type: 'position', path: '.VolumeIcon.icns' }
            ]
          }
        }

        var dmg = appDmg(dmgOpts)
        dmg.on('error', cb)
        dmg.on('progress', function (info) {
          if (info.type === 'step-begin') console.log(info.title + '...')
        })
        dmg.on('finish', function (info) {
          cb(null, buildPath)
        })
      })
    }
  })
}

function buildWin32 (cb) {
  electronPackager(Object.assign({}, all, win32), cb)
}

function buildLinux (cb) {
  electronPackager(Object.assign({}, all, linux), cb)
}

function printDone (err, buildPath) {
  if (err) console.error(err.message || err)
  else console.log('Built ' + buildPath[0])
}
<|endoftext|>


<commit_before>(function($){

    var data = {
        state : {
            isOverWorld: function(){
                return $('.overworld-locations').is(':visible');
            },
            isChooseParty: function(){
                return $('.page-dungeon-chooseparty').is(':visible');
            },
            isAdventure: function(){
                return $('.dungeon-map-inner').is(':visible') && !$('.modal-window').is(':visible');
            },
            isSelectEncounterCompanion: function(){
                return $('.encounter-party-list').is(':visible');
            },
            isEncounter: function(){
                return $('.page-dungeon-combat').is(':visible') &&
                        (!$('.modal-window').is(':visible') ||
                          $('.modal-confirm.combat-wild > h3:contains(' + data.text.criticalHit + ')').is(':visible'));
            },
            isDiceRoller: function(){
                return $('.combatDiceBox').is(':visible') && !$('.modal-window').is(':visible');
            },
            isModal: function(){
                return $('.modal-window').is(':visible');
            }
        },
        text:{
            chooseYourParty: 'Choose Your Party',
            ok: 'OK',
            d20: 'D20',
            criticalHit: 'Critical Hit!'
        }
    };

    var Adventure = function(character, dicePickerBrain){

        var _adv = [
            {
                tier: "tier-[1|2|3|4|5|6]",
                companions: [
                    {
                        "name":"cleric",
                        "required":true||false,
                    },
                    {
                        "name":"guy",
                        "required":true||false,
                    }
                ]
            },
            {
                tier: "tier-1",
                companions: [
                    {
                        "name":"lowbies",
                        "required":true||false,
                    }
                ]
            },

        ];

        this.character = character;
        this.changeCharacter = $.nwg.changeCharacter.create(this.character);
        this.adventures = this.character.adv;
        this.dicePicker = $.nwg.dicePicker.create(this.character,
            !dicePickerBrain ? $.nwg.dicePickerBrain.create() : dicePickerBrain);
    };

    Adventure.prototype.make_adventure_active = function(task) {
        $('.nav-dungeons').trigger('click');
        return {
            error: false,
            delay: 3000
        };
    };

    Adventure.prototype.check_adventure_state = function(task) {
        console.log("check_adventure_state");
        //var task = self.crete_base_task();
        /*
            Should only be called from the crate_base_task method
        */

        if(data.state.isOverWorld()){
            console.log("isOverWorld");
            task.then(this.start_adventure.bind(this))
            task.then(this.confirm_adventure.bind(this));
            task.then(this.clear_adventure_party.bind(this));
            task.then(this.select_adventure_party.bind(this));
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isChooseParty()){
            console.log("isChooseParty");
            task.then(this.clear_adventure_party.bind(this));
            task.then(this.select_adventure_party.bind(this));
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isAdventure()){
            console.log("isAdventure");
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isEncounter()){
            console.log("isEncounter");
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isSelectEncounterCompanion()){
            console.log("isSelectEncounterCompanion");
            task.then(this.select_encounter_companion.bind(this));
        }
        //isAdventure
            //select encounter
            //choose encounter companion
        //isSelectEncounterCompanion
        //isEncounter || isCritical
            //select die
        //isDiceRoller
            //wait
        //isModal
            //clear
        else if (data.state.isModal){
            console.log("isModal");
            task.then(this.clear_modal.bind(this));
            task.then($.nwg.adventure.create(this.character).check_adventure_state.bind(this)));
            return {error:false, delay: 3000};
        }
        //else

        return {error:false, delay: 3000};
    };

    Adventure.prototype.start_adventure = function(task) {
        console.log("start_adventure");
        $('a.' + 'tier-1').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.confirm_adventure = function(task) {
        console.log("confirm_adventure");
        $('.choosePartyButton > button:contains(' + data.text.chooseYourParty + ')').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_adventure_party = function(old_task) {
        console.log("select_adventure_party");
        //select adventure party member (which attemps to cancel the confirm if up THEN clears THEN selects)
        var PARTY_SIZE = 4;
        var adventures = this.adventures;
        var self = this;
        var companionsToSelect = [];

        $(adventures).each(function(indx, adventure){

            var tier = adventure.tier;
            var adventureCompanions = adventure.companions;
            var requiredCompanions = [];
            var optionalCompanions = [];
            var totalCompanionCount = $('.party-entry.full-sheet:not(.promo)').length;
            var disabledCount = $('.party-entry.full-sheet.disabled').length;
            var maxAvailableCount = totalCompanionCount - disabledCount;
            var availableCompanions = $('.party-entry.full-sheet.available:not(.promo)>a:not(.selected)');

            if(adventureCompanions && adventureCompanions.length > 0 &&
                totalCompanionCount > PARTY_SIZE && maxAvailableCount > PARTY_SIZE){

                $(availableCompanions).each(function(indx, aCmp){
                    var aComp = $(aCmp);
                    var matched = false;
                    $(adventureCompanions).each(function(indx, cmp){
                        var companion = aComp.has(':contains(' + cmp.name + ')');
                        if(companion.length === 1){
                            matched = true;
                            if(cmp.required){
                                requiredCompanions.push(companion);
                            }
                            else if(!cmp.excluded){
                                optionalCompanions.push(companion);
                            }
                        }
                    });
                    if(!matched){
                        optionalCompanions.push(aComp);
                    }
                    //console.log("rC=" + requiredCompanions.length + " | oC=" + optionalCompanions.length);
                });

                if(requiredCompanions.length + optionalCompanions.length >= PARTY_SIZE){

                    for(var i = 0; i < requiredCompanions.length && i < PARTY_SIZE; i++){
                        companionsToSelect.push($(requiredCompanions[i]));
                    }

                    for(var ii = 0; ii < optionalCompanions.length && ii < PARTY_SIZE && ii < PARTY_SIZE - requiredCompanions.length; ii++){
                        companionsToSelect.push($(optionalCompanions[ii]));
                    }

                    return false;
                }
            }
            else if(totalCompanionCount <= PARTY_SIZE && disabledCount === 0){
                companionsToSelect = availableCompanions;
                return false;
            }
        });

        if(companionsToSelect.length > 0){
            for(var i = 0; i < companionsToSelect.length && i < PARTY_SIZE; i++){
                $(companionsToSelect[i]).trigger('click');
            }

            var task = self.create_base_task();
            task.then(self.comfirm_adventure_party.bind(self));
            task.then(self.select_encounter.bind(self));
            task.then(self.select_encounter_companion.bind(self));
            task.then(self.select_die.bind(self));

            task.start_in(1500);
        }

        old_task.finish();
    };

    Adventure.prototype.clear_adventure_party = function(task){
        console.log("clear_adventure_party");
        var partyCloseButtons = $('.party-entry > button.close-button');
        partyCloseButtons.each(function(idx, btn) {
            $(btn).trigger('click');
        });

        return {
            error: false,
            delay: 3000
        };
    };

    Adventure.prototype.comfirm_adventure_party = function(task) {
        console.log("comfirm_adventure_party");
        $('.modal-window  button:contains(' + data.text.ok + ')').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_encounter = function(task) {
        console.log("select_encounter");
        if($('.dungeon-map-inner').length === 0){
            //console.log("Dungeon not ready");
            return {
                error: false,
                delay: 1000
            };
        }
        var encounters = $('.overlay.button:not(.complete, .exit, .boss)');
        var boss = $('.overlay.button.boss');

        //console.log("[encounters=" + encounters.length + "]");
        if(encounters.length > 0){
            encounters.eq(0).trigger('click');
        }
        else if(boss.length > 0){
            boss.eq(0).trigger('click');
        }

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_encounter_companion = function(task) {
        console.log("select_encounter_companion");
        var companions = $('a.selectable');
        if(!companions.length){
            //console.log("companions not found");
            return {
                error: false,
                delay: 1000
            };
        };
        $('a.selectable').eq(0).trigger('click');
        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.clear_modal = function(task) {
        var m = $('.modal-window');

        var okBtn = m.find('button:contains(' + data.text.ok + ')')
        var d20Btn = m.find('button:contains('+ data.text.d20+')');

        if(okBtn.length === 1){
            okBtn.trigger('click');
        }
        if(d20Btn.l === 1){
            d20Btn.trigger('click');
        }

        return {
            error:false,
            delay: 3000
        }
    };

    Adventure.prototype.create_base_task = function create_base_task() {
        console.log("create_base_task");
        var self = this;
        var task = $.task.create(this.changeCharacter.activate.bind(this.changeCharacter));
        task.then(this.make_adventure_active.bind(this));
        task.then(this.check_adventure_state.bind(this));

        return task;
    };

    $.extend(true, $.nwg, {
        adventure: {
            create:function(character){
                return new Adventure(character);
            }
        }
    });

}(jQuery));<commit_msg>SCA wip [this is faster than C&P into js-console]<commit_after>(function($){

    var data = {
        state : {
            isOverWorld: function(){
                return $('.overworld-locations').is(':visible');
            },
            isChooseParty: function(){
                return $('.page-dungeon-chooseparty').is(':visible');
            },
            isAdventure: function(){
                return $('.dungeon-map-inner').is(':visible') && !$('.modal-window').is(':visible');
            },
            isSelectEncounterCompanion: function(){
                return $('.encounter-party-list').is(':visible');
            },
            isEncounter: function(){
                return $('.page-dungeon-combat').is(':visible') &&
                        (!$('.modal-window').is(':visible') ||
                          $('.modal-confirm.combat-wild > h3:contains(' + data.text.criticalHit + ')').is(':visible'));
            },
            isDiceRoller: function(){
                return $('.combatDiceBox').is(':visible') && !$('.modal-window').is(':visible');
            },
            isModal: function(){
                return $('.modal-window').is(':visible');
            }
        },
        text:{
            chooseYourParty: 'Choose Your Party',
            ok: 'OK',
            d20: 'D20',
            criticalHit: 'Critical Hit!'
        }
    };

    var Adventure = function(character, dicePickerBrain){

        var _adv = [
            {
                tier: "tier-[1|2|3|4|5|6]",
                companions: [
                    {
                        "name":"cleric",
                        "required":true||false,
                    },
                    {
                        "name":"guy",
                        "required":true||false,
                    }
                ]
            },
            {
                tier: "tier-1",
                companions: [
                    {
                        "name":"lowbies",
                        "required":true||false,
                    }
                ]
            },

        ];

        this.character = character;
        this.changeCharacter = $.nwg.changeCharacter.create(this.character);
        this.adventures = this.character.adv;
        this.dicePicker = $.nwg.dicePicker.create(this.character,
            !dicePickerBrain ? $.nwg.dicePickerBrain.create() : dicePickerBrain);
    };

    Adventure.prototype.make_adventure_active = function(task) {
        $('.nav-dungeons').trigger('click');
        return {
            error: false,
            delay: 3000
        };
    };

    Adventure.prototype.check_adventure_state = function(task) {
        console.log("check_adventure_state");
        //var task = self.crete_base_task();
        /*
            Should only be called from the crate_base_task method
        */

        if(data.state.isOverWorld()){
            console.log("isOverWorld");
            task.then(this.start_adventure.bind(this))
            task.then(this.confirm_adventure.bind(this));
            task.then(this.clear_adventure_party.bind(this));
            task.then(this.select_adventure_party.bind(this));
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isChooseParty()){
            console.log("isChooseParty");
            task.then(this.clear_adventure_party.bind(this));
            task.then(this.select_adventure_party.bind(this));
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isAdventure()){
            console.log("isAdventure");
            task.then(this.select_encounter.bind(this));
            task.then(this.select_encounter_companion.bind(this));
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isEncounter()){
            console.log("isEncounter");
            task.then(this.dicePicker.pick_die.bind(this.dicePicker));
        }
        else if(data.state.isSelectEncounterCompanion()){
            console.log("isSelectEncounterCompanion");
            task.then(this.select_encounter_companion.bind(this));
        }
        //isAdventure
            //select encounter
            //choose encounter companion
        //isSelectEncounterCompanion
        //isEncounter || isCritical
            //select die
        //isDiceRoller
            //wait
        //isModal
            //clear
        else if (data.state.isModal){
            console.log("isModal");
            task.then(this.clear_modal.bind(this));
            task.then($.nwg.adventure.create(this.character).check_adventure_state.bind(this));
            return {error:false, delay: 3000};
        }
        //else

        return {error:false, delay: 3000};
    };

    Adventure.prototype.start_adventure = function(task) {
        console.log("start_adventure");
        $('a.' + 'tier-1').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.confirm_adventure = function(task) {
        console.log("confirm_adventure");
        $('.choosePartyButton > button:contains(' + data.text.chooseYourParty + ')').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_adventure_party = function(old_task) {
        console.log("select_adventure_party");
        //select adventure party member (which attemps to cancel the confirm if up THEN clears THEN selects)
        var PARTY_SIZE = 4;
        var adventures = this.adventures;
        var self = this;
        var companionsToSelect = [];

        $(adventures).each(function(indx, adventure){

            var tier = adventure.tier;
            var adventureCompanions = adventure.companions;
            var requiredCompanions = [];
            var optionalCompanions = [];
            var totalCompanionCount = $('.party-entry.full-sheet:not(.promo)').length;
            var disabledCount = $('.party-entry.full-sheet.disabled').length;
            var maxAvailableCount = totalCompanionCount - disabledCount;
            var availableCompanions = $('.party-entry.full-sheet.available:not(.promo)>a:not(.selected)');

            if(adventureCompanions && adventureCompanions.length > 0 &&
                totalCompanionCount > PARTY_SIZE && maxAvailableCount > PARTY_SIZE){

                $(availableCompanions).each(function(indx, aCmp){
                    var aComp = $(aCmp);
                    var matched = false;
                    $(adventureCompanions).each(function(indx, cmp){
                        var companion = aComp.has(':contains(' + cmp.name + ')');
                        if(companion.length === 1){
                            matched = true;
                            if(cmp.required){
                                requiredCompanions.push(companion);
                            }
                            else if(!cmp.excluded){
                                optionalCompanions.push(companion);
                            }
                        }
                    });
                    if(!matched){
                        optionalCompanions.push(aComp);
                    }
                    //console.log("rC=" + requiredCompanions.length + " | oC=" + optionalCompanions.length);
                });

                if(requiredCompanions.length + optionalCompanions.length >= PARTY_SIZE){

                    for(var i = 0; i < requiredCompanions.length && i < PARTY_SIZE; i++){
                        companionsToSelect.push($(requiredCompanions[i]));
                    }

                    for(var ii = 0; ii < optionalCompanions.length && ii < PARTY_SIZE && ii < PARTY_SIZE - requiredCompanions.length; ii++){
                        companionsToSelect.push($(optionalCompanions[ii]));
                    }

                    return false;
                }
            }
            else if(totalCompanionCount <= PARTY_SIZE && disabledCount === 0){
                companionsToSelect = availableCompanions;
                return false;
            }
        });

        if(companionsToSelect.length > 0){
            for(var i = 0; i < companionsToSelect.length && i < PARTY_SIZE; i++){
                $(companionsToSelect[i]).trigger('click');
            }

            var task = self.create_base_task();
            task.then(self.comfirm_adventure_party.bind(self));
            task.then(self.select_encounter.bind(self));
            task.then(self.select_encounter_companion.bind(self));
            task.then(self.select_die.bind(self));

            task.start_in(1500);
        }

        old_task.finish();
    };

    Adventure.prototype.clear_adventure_party = function(task){
        console.log("clear_adventure_party");
        var partyCloseButtons = $('.party-entry > button.close-button');
        partyCloseButtons.each(function(idx, btn) {
            $(btn).trigger('click');
        });

        return {
            error: false,
            delay: 3000
        };
    };

    Adventure.prototype.comfirm_adventure_party = function(task) {
        console.log("comfirm_adventure_party");
        $('.modal-window  button:contains(' + data.text.ok + ')').trigger('click');

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_encounter = function(task) {
        console.log("select_encounter");
        if($('.dungeon-map-inner').length === 0){
            //console.log("Dungeon not ready");
            return {
                error: false,
                delay: 1000
            };
        }
        var encounters = $('.overlay.button:not(.complete, .exit, .boss)');
        var boss = $('.overlay.button.boss');

        //console.log("[encounters=" + encounters.length + "]");
        if(encounters.length > 0){
            encounters.eq(0).trigger('click');
        }
        else if(boss.length > 0){
            boss.eq(0).trigger('click');
        }

        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.select_encounter_companion = function(task) {
        console.log("select_encounter_companion");
        var companions = $('a.selectable');
        if(!companions.length){
            //console.log("companions not found");
            return {
                error: false,
                delay: 1000
            };
        };
        $('a.selectable').eq(0).trigger('click');
        return {
            error: false,
            delay: 1000
        };
    };

    Adventure.prototype.clear_modal = function(task) {
        var m = $('.modal-window');

        var okBtn = m.find('button:contains(' + data.text.ok + ')')
        var d20Btn = m.find('button:contains('+ data.text.d20+')');

        if(okBtn.length === 1){
            okBtn.trigger('click');
        }
        if(d20Btn.l === 1){
            d20Btn.trigger('click');
        }

        return {
            error:false,
            delay: 3000
        }
    };

    Adventure.prototype.create_base_task = function create_base_task() {
        console.log("create_base_task");
        var self = this;
        var task = $.task.create(this.changeCharacter.activate.bind(this.changeCharacter));
        task.then(this.make_adventure_active.bind(this));
        task.then(this.check_adventure_state.bind(this));

        return task;
    };

    $.extend(true, $.nwg, {
        adventure: {
            create:function(character){
                return new Adventure(character);
            }
        }
    });

}(jQuery));<|endoftext|>


<commit_before>'use strict';

angular.module('mean.products').service('shoppingCart', ['$resource', 'Items',
  function($resource, Items) {
  	var cart = {
  		loaded: false,
  		items: [],
  		count: function(){
  			var $this = this;
  			var count = 0;
  			for(var i in $this.items){
  				var item = $this.items[i];
  				if(item && item.quantity){
  					count += item.quantity;
  				}
  			}
  			return count;
  		},
  		total: function(){
  			var total = 0;
  			var $this = this;
  			$this.loadItems();
  			angular.forEach($this.items, function(item, key){
  				if(item && item.quantity){
	  				total += (item.price_in_cents * item.quantity)/100;
  				}
  			});
  			return total;
  		},
	  	addItem: function(product){
	  		var $this = this;
  			var item = $this.findItem(product._id);

  			if(item){
	  			console.log('addItem');
		  		console.log(item.quantity);
  				item.quantity += 1;
  				console.log(item.quantity);

  				item.$update();
  			}else{
  				console.log('adddddItem......');
	  			item = new Items({
	  				product: product._id,
	  				quantity: 1
	  			});
	  			item.$save(function(resp){
	  				$this.items.push(resp);
	  			});
  			}

	  	},
	  	removeItem: function(item){
	  		console.log('removeItem');
	  		var $this = this;
	  		for(var i in $this.items){
	  			var hold = $this.items[i];
	  			if(hold._id === item.product._id || (hold.product && hold.product._id===item.product._id || hold.product === item.product._id)){
	  				var rmItem = $this.items.splice(i,1);
	  				console.log(rmItem);
	  				item.$remove();
	  				break;
	  			}
	  		}
	  	},
	  	updateItem: function(item, quantity){
	  		console.log('updateItem');
	  		// var $this = this;
	  		item.quantity = quantity;
	  		item.$save();
	  	},
	  	loadItems: function(){
	  		var $this = this;
	  		if(!$this.loaded){
	  			$this.loaded = true;
		  		$this.items = [];
	  			Items.query(function(items){
	  				angular.forEach(items, function(item){
	  					$this.items.push(item);
	  				});
	  			});
			  }
	  	},

	  	updateCart: function(){
	  		var $this = this;
	  		angular.forEach($this.items,function(item){
	  			item.$update();
	  		});
	  	},

	  	findItem: function(productId){
	  		var $this = this;
	  		var rtn = null;
	  		for(var i in $this.items){
	  			var item = $this.items[i];
	  			if(item._id === productId || (item.product && item.product._id===productId || item.product === productId)){
	  				rtn = item;
	  				break;
	  			}
	  		}
	  		return rtn;
	  	}

  	};
  	return cart;
  }
]);
<commit_msg>fix bug price<commit_after>'use strict';

angular.module('mean.products').service('shoppingCart', ['$resource', 'Items',
  function($resource, Items) {
  	var cart = {
  		loaded: false,
  		items: [],
  		count: function(){
  			var $this = this;
  			var count = 0;
  			for(var i in $this.items){
  				var item = $this.items[i];
  				if(item && item.quantity){
  					count += item.quantity;
  				}
  			}
  			return count;
  		},
  		total: function(){
  			var total = 0;
  			var $this = this;
  			$this.loadItems();
  			angular.forEach($this.items, function(item, key){
  				if(item && item.quantity){
	  				total += (item.product.price_in_cents * item.quantity)/100;
  				}
  			});
  			return total;
  		},
	  	addItem: function(product){
	  		var $this = this;
  			var item = $this.findItem(product._id);

  			if(item){
	  			console.log('addItem');
		  		console.log(item.quantity);
  				item.quantity += 1;
  				console.log(item.quantity);

  				item.$update();
  			}else{
  				console.log('adddddItem......');
	  			item = new Items({
	  				product: product._id,
	  				quantity: 1
	  			});
	  			item.$save(function(resp){
	  				$this.items.push(resp);
	  			});
  			}

	  	},
	  	removeItem: function(item){
	  		console.log('removeItem');
	  		var $this = this;
	  		for(var i in $this.items){
	  			var hold = $this.items[i];
	  			if(hold._id === item.product._id || (hold.product && hold.product._id===item.product._id || hold.product === item.product._id)){
	  				var rmItem = $this.items.splice(i,1);
	  				console.log(rmItem);
	  				item.$remove();
	  				break;
	  			}
	  		}
	  	},
	  	updateItem: function(item, quantity){
	  		console.log('updateItem');
	  		// var $this = this;
	  		item.quantity = quantity;
	  		item.$save();
	  	},
	  	loadItems: function(){
	  		var $this = this;
	  		if(!$this.loaded){
	  			$this.loaded = true;
		  		$this.items = [];
	  			Items.query(function(items){
	  				angular.forEach(items, function(item){
	  					$this.items.push(item);
	  				});
	  			});
			  }
	  	},

	  	updateCart: function(){
	  		var $this = this;
	  		angular.forEach($this.items,function(item){
	  			item.$update();
	  		});
	  	},

	  	findItem: function(productId){
	  		var $this = this;
	  		var rtn = null;
	  		for(var i in $this.items){
	  			var item = $this.items[i];
	  			if(item._id === productId || (item.product && item.product._id===productId || item.product === productId)){
	  				rtn = item;
	  				break;
	  			}
	  		}
	  		return rtn;
	  	}

  	};
  	return cart;
  }
]);
<|endoftext|>


<commit_before>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const chalk = require('chalk');
const readline = require('readline');
const semver = require('semver');

const Project = require('../build/utils/project');

const DIST_PATH = path.resolve(__dirname, '../dist/node_modules');
const PACKAGES_PATH = path.resolve(__dirname, '../packages');

const DRY_RUN = process.argv.indexOf('--dry-run') > -1;
if (DRY_RUN) {
  console.log(chalk.yellow("--dry-run"), "- side effects disabled");
}

// Fail fast if we haven't done a build first.
assertDistExists();
assertGitIsClean();

let cli = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Load up the built packages in dist.
let packages = Project.from(DIST_PATH)
  .packages
  .filter(pkg => pkg.isPublishable);

let packageNames = packages.map(package => package.name);
let newVersion;
let distTag;

// Begin interactive CLI
printExistingVersions();
promptForVersion();

function printExistingVersions() {
  let packageVersions = packages.map(package => [package.name, package.version]);
  printPadded(packageVersions);
}

function promptForVersion() {
  let defaultVersion = generateDefaultVersion();

  cli.question(chalk.green(`\nNew version to publish? [${defaultVersion}] `), version => {
    version = version.trim();
    if (version === '') {
      version = defaultVersion;
    }

    validateNewVersion(version);
    console.log(chalk.green(`Publishing v${version}...`));

    newVersion = version;
    applyNewVersion();
    gitCommitAndTag();
    confirmPublish();
  });
}

function generateDefaultVersion() {
  let currentVersion = require('../package.json').version;
  return semver.inc(currentVersion, 'patch');
}

function validateNewVersion(version) {
  if (version === '') { fatalError("Version must not be empty."); }
  if (!semver.valid(version)) { fatalError("Version must be a valid SemVer version."); }

  packages.forEach(package => {
    if (!semver.gt(version, package.version)) {
      fatalError(`Version must be greater than existing versions. ${package.name} has version ${package.version}, which is greater than or equal to ${version}.`);
    }
  });
}

function applyNewVersion() {
  console.log(`Apply ${newVersion}`);

  // Update packages in the dist directory
  packages.forEach(package => {
    package.pkg.version = newVersion;
    package.updateDependencies(newVersion);

    if (!DRY_RUN) {
      package.savePackageJSON();
    }
  });

  // Update source packages
  Project.from(PACKAGES_PATH)
    .packages
    .forEach(package => {
      package.pkg.version = newVersion;
      package.updateDependencies(newVersion);

      if (!DRY_RUN) {
        package.savePackageJSON();
      }
      execWithSideEffects(`git add "${package.packageJSONPath}"`);
    });

  // Update root package.json
  let rootPkgPath = path.join(__dirname, '../package.json');
  let rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
  rootPkg.version = newVersion;
  if (!DRY_RUN) {
    fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2));
      execWithSideEffects(`git add package.json`);
  }
}

function gitCommitAndTag() {
  execWithSideEffects(`git commit -m "Release v${newVersion}"`);
  execWithSideEffects(`git tag "v${newVersion}"`);
}

function confirmPublish() {
  distTag = semver.prerelease(newVersion) ? 'next' : 'latest';

  console.log(chalk.blue("Version"), newVersion);
  console.log(chalk.blue("Dist Tag"), distTag);

  cli.question(chalk.bgRed.white.bold("Are you sure? [Y/N]") + " ", answer => {
    if (answer !== 'y' && answer !== 'Y') {
      console.log(chalk.red("Aborting"));
      cli.close();
      return;
    }

    packages.filter(pkg => !pkg.private).forEach(package => {
      execWithSideEffects(`npm publish --tag ${distTag}`, {
        cwd: package.absolutePath
      });
    });

    execWithSideEffects(`git push origin master --tags`);

    console.log(chalk.green(`\nv${newVersion} deployed!`));
    console.log(chalk.green('Done.'));
    cli.close();
  });
}

function fatalError(message) {
  console.log(chalk.red(message));
  process.exit(1);
}

function throwNoPackagesErr() {
  console.log(chalk.red('No dist directory found. Did you do a build first? (npm run build)'))
  process.exit(1);
}

function assertDistExists() {
  try {
    let stat = fs.statSync(DIST_PATH);
    if (!stat.isDirectory()) {
      throwNoPackagesErr()
    }
  } catch (e) {
    throwNoPackagesErr();
  }
}

function assertGitIsClean() {
  let status = execSync('git status').toString();
  let force = process.argv.indexOf('--force') > -1;

  if (!status.match(/^nothing to commit/m)) {
    if (force) {
      console.log(chalk.yellow("--force"), "- ignoring unclean git working tree");
    } else {
      console.log(chalk.red("Git working tree isn't clean. Use --force to ignore this warning."));
      process.exit(1);
    }
  }
}

function execWithSideEffects(cmd, options) {
  let cwd = '';
  if (options && options.cwd) {
    cwd = chalk.gray.dim(` (cwd: ${options.cwd}`);
  }

  console.log(chalk.green('>') + ' ' + chalk.gray(cmd) + cwd);
  if (!DRY_RUN) {
    return execSync.apply(null, arguments);
  }
}

function printPadded(table) {
  let maxWidth = Math.max(...table.map(r => r[0].length));
  table.forEach(row => {
    console.log(chalk.blue(pad(row[0], maxWidth)) + "  " + row[1]);
  })
}

function pad(string, width) {
  return string + " ".repeat(width - string.length);
}
<commit_msg>Fix `DIST_PATH` in publish script.<commit_after>#!/usr/bin/env node

const fs = require('fs');
const path = require('path');
const execSync = require('child_process').execSync;
const chalk = require('chalk');
const readline = require('readline');
const semver = require('semver');

const Project = require('../build/utils/project');

const DIST_PATH = path.resolve(__dirname, '../dist');
const PACKAGES_PATH = path.resolve(__dirname, '../packages');

const DRY_RUN = process.argv.indexOf('--dry-run') > -1;
if (DRY_RUN) {
  console.log(chalk.yellow("--dry-run"), "- side effects disabled");
}

// Fail fast if we haven't done a build first.
assertDistExists();
assertGitIsClean();

let cli = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

// Load up the built packages in dist.
let packages = Project.from(DIST_PATH)
  .packages
  .filter(pkg => pkg.isPublishable);

let packageNames = packages.map(package => package.name);
let newVersion;
let distTag;

// Begin interactive CLI
printExistingVersions();
promptForVersion();

function printExistingVersions() {
  let packageVersions = packages.map(package => [package.name, package.version]);
  printPadded(packageVersions);
}

function promptForVersion() {
  let defaultVersion = generateDefaultVersion();

  cli.question(chalk.green(`\nNew version to publish? [${defaultVersion}] `), version => {
    version = version.trim();
    if (version === '') {
      version = defaultVersion;
    }

    validateNewVersion(version);
    console.log(chalk.green(`Publishing v${version}...`));

    newVersion = version;
    applyNewVersion();
    gitCommitAndTag();
    confirmPublish();
  });
}

function generateDefaultVersion() {
  let currentVersion = require('../package.json').version;
  return semver.inc(currentVersion, 'patch');
}

function validateNewVersion(version) {
  if (version === '') { fatalError("Version must not be empty."); }
  if (!semver.valid(version)) { fatalError("Version must be a valid SemVer version."); }

  packages.forEach(package => {
    if (!semver.gt(version, package.version)) {
      fatalError(`Version must be greater than existing versions. ${package.name} has version ${package.version}, which is greater than or equal to ${version}.`);
    }
  });
}

function applyNewVersion() {
  console.log(`Apply ${newVersion}`);

  // Update packages in the dist directory
  packages.forEach(package => {
    package.pkg.version = newVersion;
    package.updateDependencies(newVersion);

    if (!DRY_RUN) {
      package.savePackageJSON();
    }
  });

  // Update source packages
  Project.from(PACKAGES_PATH)
    .packages
    .forEach(package => {
      package.pkg.version = newVersion;
      package.updateDependencies(newVersion);

      if (!DRY_RUN) {
        package.savePackageJSON();
      }
      execWithSideEffects(`git add "${package.packageJSONPath}"`);
    });

  // Update root package.json
  let rootPkgPath = path.join(__dirname, '../package.json');
  let rootPkg = JSON.parse(fs.readFileSync(rootPkgPath, 'utf8'));
  rootPkg.version = newVersion;
  if (!DRY_RUN) {
    fs.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2));
      execWithSideEffects(`git add package.json`);
  }
}

function gitCommitAndTag() {
  execWithSideEffects(`git commit -m "Release v${newVersion}"`);
  execWithSideEffects(`git tag "v${newVersion}"`);
}

function confirmPublish() {
  distTag = semver.prerelease(newVersion) ? 'next' : 'latest';

  console.log(chalk.blue("Version"), newVersion);
  console.log(chalk.blue("Dist Tag"), distTag);

  cli.question(chalk.bgRed.white.bold("Are you sure? [Y/N]") + " ", answer => {
    if (answer !== 'y' && answer !== 'Y') {
      console.log(chalk.red("Aborting"));
      cli.close();
      return;
    }

    packages.filter(pkg => !pkg.private).forEach(package => {
      execWithSideEffects(`npm publish --tag ${distTag}`, {
        cwd: package.absolutePath
      });
    });

    execWithSideEffects(`git push origin master --tags`);

    console.log(chalk.green(`\nv${newVersion} deployed!`));
    console.log(chalk.green('Done.'));
    cli.close();
  });
}

function fatalError(message) {
  console.log(chalk.red(message));
  process.exit(1);
}

function throwNoPackagesErr() {
  console.log(chalk.red('No dist directory found. Did you do a build first? (npm run build)'))
  process.exit(1);
}

function assertDistExists() {
  try {
    let stat = fs.statSync(DIST_PATH);
    if (!stat.isDirectory()) {
      throwNoPackagesErr()
    }
  } catch (e) {
    throwNoPackagesErr();
  }
}

function assertGitIsClean() {
  let status = execSync('git status').toString();
  let force = process.argv.indexOf('--force') > -1;

  if (!status.match(/^nothing to commit/m)) {
    if (force) {
      console.log(chalk.yellow("--force"), "- ignoring unclean git working tree");
    } else {
      console.log(chalk.red("Git working tree isn't clean. Use --force to ignore this warning."));
      process.exit(1);
    }
  }
}

function execWithSideEffects(cmd, options) {
  let cwd = '';
  if (options && options.cwd) {
    cwd = chalk.gray.dim(` (cwd: ${options.cwd}`);
  }

  console.log(chalk.green('>') + ' ' + chalk.gray(cmd) + cwd);
  if (!DRY_RUN) {
    return execSync.apply(null, arguments);
  }
}

function printPadded(table) {
  let maxWidth = Math.max(...table.map(r => r[0].length));
  table.forEach(row => {
    console.log(chalk.blue(pad(row[0], maxWidth)) + "  " + row[1]);
  })
}

function pad(string, width) {
  return string + " ".repeat(width - string.length);
}
<|endoftext|>