<commit_before>var Utils      = require("./../utils")
  , DataTypes  = require('./../data-types')
  , Helpers    = require('./helpers')
  , _          = require('lodash')
  , Transaction = require('../transaction')

var HasManySingleLinked = require("./has-many-single-linked")
  , HasManyDoubleLinked  = require("./has-many-double-linked")

module.exports = (function() {
  var HasMany = function(source, target, options) {
    var self = this

    this.associationType = 'HasMany'
    this.source = source
    this.target = target
    this.targetAssociation = null
    this.options = options
    this.sequelize = source.daoFactoryManager.sequelize
    this.through = options.through
    this.isMultiAssociation = true
    this.isSelfAssociation = this.source === this.target
    this.doubleLinked = false
    this.as = this.options.as
    this.combinedTableName = Utils.combineTableNames(
      this.source.tableName,
      this.isSelfAssociation ? (this.as || this.target.tableName) : this.target.tableName
    )

    /*
     * Map joinTableModel/Name to through for BC
     */
    if (this.through === undefined) {
      this.through = this.options.joinTableModel || this.options.joinTableName;

      /*
       * If both are undefined, see if useJunctionTable was false (for self associations) - else assume through to be true
       */
      if (this.through === undefined) {
        if (this.options.useJunctionTable === false) {
          this.through = null;
        } else {
          this.through = true;
        }
      }
    }

    /*
     * Determine associationAccessor, especially for include options to identify the correct model
     */

    this.associationAccessor = this.as
    if (!this.associationAccessor && (typeof this.through === "string" || Object(this.through) === this.through)) {
      this.associationAccessor = this.through.tableName || this.through
    }
    else if (!this.associationAccessor) {
      this.associationAccessor = this.combinedTableName
    }

    /*
     * If self association, this is the target association - Unless we find a pairing association
     */
    if (this.isSelfAssociation) {
      this.targetAssociation = this
    }

    /*
     * Else find partner DAOFactory if present, to identify double linked association
     */
    if (this.through) {
      _.each(this.target.associations, function (association, accessor) {
        if (self.source === association.target) {
          var paired = false

          // If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will)
          if (self.through === true && accessor === self.associationAccessor) {
            paired = true
          }
          // If through is not default, determine pairing by through value (model/string)
          if (self.through !== true && self.options.through === association.options.through) {
            paired = true
          }
          // If paired, set properties identifying both associations as double linked, and allow them to each eachtoerh
          if (paired) {
            self.doubleLinked = true
            association.doubleLinked = true

            self.targetAssociation = association
            association.targetAssociation = self
          }
        }
      })
    }
    /*
     * If we are double linked, and through is either default or a string, we create the through model and set it on both associations
     */
    if (this.doubleLinked) {
      if (this.through === true) {
        this.through = this.combinedTableName
      }
    }

    if (typeof this.through === "string") {
      this.through = this.sequelize.define(this.through, {}, _.extend(this.options, {
        tableName: this.through,
        paranoid: false  // A paranoid join table does not make sense
      }))

      if (this.targetAssociation) {
        this.targetAssociation.through = this.through
      }
    }

    this.options.tableName = this.combinedName = (this.through === Object(this.through) ? this.through.tableName : this.through)

    if (this.as) {
      this.isAliased = true
    } else {
      this.as = Utils.pluralize(this.target.name, this.target.options.language)
    }

    this.accessors = {
      get: Utils._.camelize('get_' + this.as),
      set: Utils._.camelize('set_' + this.as),
      add: Utils._.camelize(Utils.singularize('add_' + this.as, this.target.options.language)),
      create: Utils._.camelize(Utils.singularize('create_' + this.as, this.target.options.language)),
      remove: Utils._.camelize(Utils.singularize('remove_' + this.as, this.target.options.language)),
      hasSingle: Utils._.camelize(Utils.singularize('has_' + this.as, this.target.options.language)),
      hasAll: Utils._.camelize('has_' + this.as)
    }
  }

  // the id is in the target table
  // or in an extra table which connects two tables
  HasMany.prototype.injectAttributes = function() {
    var doubleLinked      = this.doubleLinked
      , self              = this
      , primaryKeyDeleted = false

    this.identifier = this.options.foreignKey || Utils._.camelizeIf(
      [Utils.singularize(this.source.name, this.source.options.language), this.source.primaryKeyAttribute].join("_"),
      !this.source.options.underscored
    )

    // is there already a single sided association between the source and the target?
    // or is the association on the model itself?

    if ((this.isSelfAssociation && Object(this.through) === this.through) || doubleLinked) {
      // We need to remove the keys that 1:M have added
      if (this.isSelfAssociation && doubleLinked) {
        if (self.through.rawAttributes[this.targetAssociation.identifier]._autoGenerated) {
          delete self.through.rawAttributes[this.targetAssociation.identifier];
        }
        if (self.through.rawAttributes[this.targetAssociation.foreignIdentifier]._autoGenerated) {
          delete self.through.rawAttributes[this.targetAssociation.foreignIdentifier];
        }
      }

      this.foreignIdentifier = this.targetAssociation.identifier
      this.targetAssociation.foreignIdentifier = this.identifier

      if (this.isSelfAssociation && this.foreignIdentifier === this.identifier) {
        this.foreignIdentifier = Utils._.camelizeIf(
          [Utils.singularize(this.as, this.source.options.language), this.source.primaryKeyAttribute].join("_"),
          !this.source.options.underscored
        );
      }

      if (isForeignKeyDeletionAllowedFor.call(this, this.source, this.foreignIdentifier)) {
        delete this.source.rawAttributes[this.foreignIdentifier]
      }

      if (isForeignKeyDeletionAllowedFor.call(this, this.target, this.identifier)) {
        delete this.targetAssociation.source.rawAttributes[this.identifier]
      }

      // remove any PKs previously defined by sequelize
      Utils._.each(this.through.attributes, function(dataTypeString, attributeName) {
        if (dataTypeString.toString().indexOf('PRIMARY KEY') !== -1 && self.through.rawAttributes[attributeName]._autoGenerated === true) {
          delete self.through.rawAttributes[attributeName]
          primaryKeyDeleted = true
        }
      })

      // define a new model, which connects the models
      var combinedTableAttributes = {}
        , sourceKeyType = this.source.rawAttributes[this.source.primaryKeyAttribute].type
        , targetKeyType = this.target.rawAttributes[this.target.primaryKeyAttribute].type
        , sourceAttribute = { type: sourceKeyType }
        , targetAttribute = { type: targetKeyType }

      if (this.options.constraints !== false) {
        sourceAttribute.references = this.source.getTableName()
        sourceAttribute.referencesKey = this.source.primaryKeyAttribute
        sourceAttribute.onDelete = this.options.onDelete || 'CASCADE'
        sourceAttribute.onUpdate = this.options.onUpdate || 'CASCADE'
      }
      if (this.targetAssociation.options.constraints !== false) {
        targetAttribute.references = this.target.getTableName()
        targetAttribute.referencesKey = this.target.primaryKeyAttribute
        targetAttribute.onDelete = this.targetAssociation.options.onDelete || 'CASCADE'
        targetAttribute.onUpdate = this.targetAssociation.options.onUpdate || 'CASCADE'
      }

      if (primaryKeyDeleted) {
        targetAttribute.primaryKey = sourceAttribute.primaryKey = true

      } else {
        var uniqueKey = [this.through.tableName, this.identifier, this.foreignIdentifier, 'unique'].join('_')
        targetAttribute.unique = sourceAttribute.unique = uniqueKey
      }

      combinedTableAttributes[this.identifier] = sourceAttribute
      combinedTableAttributes[this.foreignIdentifier] = targetAttribute

      if (!this.through.rawAttributes[this.identifier]) {
        this.through.rawAttributes[this.identifier] = {
          _autoGenerated: true
        }
      }

      if (!this.through.rawAttributes[this.foreignIdentifier]) {
        this.through.rawAttributes[this.foreignIdentifier] = {
          _autoGenerated: true
        }
      }

      this.through.rawAttributes[this.identifier] = Utils._.merge(this.through.rawAttributes[this.identifier], sourceAttribute);
      this.through.rawAttributes[this.foreignIdentifier] = Utils._.merge(this.through.rawAttributes[this.foreignIdentifier], targetAttribute);

      this.through.init(this.through.daoFactoryManager)
    } else {
      var newAttributes = {}
      var constraintOptions = _.clone(this.options) // Create a new options object for use with addForeignKeyConstraints, to avoid polluting this.options in case it is later used for a n:m
      newAttributes[this.identifier] = { type: this.options.keyType || this.source.rawAttributes[this.source.primaryKeyAttribute].type }

      if (this.options.constraints !== false) {
        constraintOptions.onDelete = constraintOptions.onDelete || 'SET NULL'
        constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE'
      }
      Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, constraintOptions)
      Utils._.defaults(this.target.rawAttributes, newAttributes)
    }

    // Sync attributes and setters/getters to DAO prototype
    this.target.refreshAttributes()
    this.source.refreshAttributes()

    return this
  }

  HasMany.prototype.injectGetter = function(obj) {
    var self = this

    obj[this.accessors.get] = function(options) {
      var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
      return new Class(self, this).injectGetter(options)
    }

    obj[this.accessors.hasAll] = function(objects, options) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get](options)
          .error(function(err) { customEventEmitter.emit('error', err) })
          .success(function(associatedObjects) {
            customEventEmitter.emit('success',
              Utils._.all(objects, function(o) {
                return Utils._.any(associatedObjects, function(associatedObject) {
                  return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                    return o[identifier] == associatedObject[identifier];
                  });
                })
              })
            )
          })
      })
      return customEventEmitter.run()
    }

    obj[this.accessors.hasSingle] = function(o, options) {
      var instance           = this
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get](options)
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.any(associatedObjects, function(associatedObject) {
              return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                return o[identifier] == associatedObject[identifier];
              });
            })
          )
        })
      })
      return customEventEmitter.run()
    }
    return this
  }

  HasMany.prototype.injectSetter = function(obj) {
    var self = this

    obj[this.accessors.set] = function(newAssociatedObjects, defaultAttributes) {
      if (newAssociatedObjects === null) {
        newAssociatedObjects = []
      }

      var instance = this

      // define the returned customEventEmitter, which will emit the success event once everything is done
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]()
          .success(function(oldAssociatedObjects) {
            var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
            new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
          })
          .proxy(emitter, {events: ['error', 'sql']})
      }).run()
    }

    obj[this.accessors.add] = function(newInstance, additionalAttributes) {
      var instance = this
        , primaryKey = newInstance.Model.primaryKeyAttribute
        , where = new Utils.where(self.target.rawAttributes[primaryKey], newInstance[primaryKey])

      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({ where: where })
          .proxy(emitter, {events: ['error', 'sql']})
          .success(function(currentAssociatedObjects) {
            if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) {
              var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
              new Class(self, instance).injectAdder(emitter, newInstance, additionalAttributes, !!currentAssociatedObjects.length)
            } else {
              emitter.emit('success', newInstance);
            }
          })
      }).run()
    }

    obj[this.accessors.remove] = function(oldAssociatedObject) {
      var instance = this
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]().success(function(currentAssociatedObjects) {
          var newAssociations = []
            , oldAssociations = []

          currentAssociatedObjects.forEach(function(association) {
            if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
              newAssociations.push(association)
            }
          })

          var tick = 0
          var next = function(err, i) {
            if (!!err || i >= oldAssociations.length) {
              return run(err)
            }

            oldAssociations[i].destroy().error(function(err) {
              next(err)
            })
            .success(function() {
              tick++
              next(null, tick)
            })
          }

          var run = function(err) {
            if (!!err) {
              return emitter.emit('error', err)
            }

            instance[self.accessors.set](newAssociations).proxy(emitter)
          }

          if (oldAssociations.length > 0) {
            next(null, tick)
          } else {
            run()
          }
        })
      }).run()
    }

    return this
  }

  HasMany.prototype.injectCreator = function(obj) {
    var self = this

    obj[this.accessors.create] = function(values, fieldsOrOptions) {
      var instance = this
        , options = {}

      if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
        options.transaction = fieldsOrOptions.transaction
      }

      if (Object(self.through) === self.through) {
        return new Utils.CustomEventEmitter(function(emitter) {
          // Create the related model instance
          self.target
            .create(values, fieldsOrOptions)
            .proxy(emitter, { events: ['error', 'sql'] })
            .success(function(newAssociatedObject) {
              instance[self.accessors.add](newAssociatedObject, options)
                .proxy(emitter)
            })
        }).run()
      } else {
        values[self.identifier] = instance.get(self.source.primaryKeyAttribute);
        return self.target.create(values, fieldsOrOptions)
      }
    }

    return this
  };

  /**
   * The method checks if it is ok to delete the previously defined foreign key.
   * This is done because we need to keep the foreign key if another association
   * is depending on it.
   *
   * @param  {DaoFactory}  daoFactory The source or target DaoFactory of this assocation
   * @param  {[type]}  identifier     The name of the foreign key identifier
   * @return {Boolean}                Whether or not the deletion of the foreign key is ok.
   */
  var isForeignKeyDeletionAllowedFor = function(daoFactory, identifier) {
    var isAllowed        = true
      , associationNames = Utils._.without(Object.keys(daoFactory.associations), this.associationAccessor)

    associationNames.forEach(function(associationName) {
      if (daoFactory.associations[associationName].identifier === identifier) {
        isAllowed = false
      }
    })

    return isAllowed
  }

  return HasMany
})()
<commit_msg>fix(hasMany): make sure the getter called by the setters use the transaction<commit_after>var Utils      = require("./../utils")
  , DataTypes  = require('./../data-types')
  , Helpers    = require('./helpers')
  , _          = require('lodash')
  , Transaction = require('../transaction')

var HasManySingleLinked = require("./has-many-single-linked")
  , HasManyDoubleLinked  = require("./has-many-double-linked")

module.exports = (function() {
  var HasMany = function(source, target, options) {
    var self = this

    this.associationType = 'HasMany'
    this.source = source
    this.target = target
    this.targetAssociation = null
    this.options = options
    this.sequelize = source.daoFactoryManager.sequelize
    this.through = options.through
    this.isMultiAssociation = true
    this.isSelfAssociation = this.source === this.target
    this.doubleLinked = false
    this.as = this.options.as
    this.combinedTableName = Utils.combineTableNames(
      this.source.tableName,
      this.isSelfAssociation ? (this.as || this.target.tableName) : this.target.tableName
    )

    /*
     * Map joinTableModel/Name to through for BC
     */
    if (this.through === undefined) {
      this.through = this.options.joinTableModel || this.options.joinTableName;

      /*
       * If both are undefined, see if useJunctionTable was false (for self associations) - else assume through to be true
       */
      if (this.through === undefined) {
        if (this.options.useJunctionTable === false) {
          this.through = null;
        } else {
          this.through = true;
        }
      }
    }

    /*
     * Determine associationAccessor, especially for include options to identify the correct model
     */

    this.associationAccessor = this.as
    if (!this.associationAccessor && (typeof this.through === "string" || Object(this.through) === this.through)) {
      this.associationAccessor = this.through.tableName || this.through
    }
    else if (!this.associationAccessor) {
      this.associationAccessor = this.combinedTableName
    }

    /*
     * If self association, this is the target association - Unless we find a pairing association
     */
    if (this.isSelfAssociation) {
      this.targetAssociation = this
    }

    /*
     * Else find partner DAOFactory if present, to identify double linked association
     */
    if (this.through) {
      _.each(this.target.associations, function (association, accessor) {
        if (self.source === association.target) {
          var paired = false

          // If through is default, we determine pairing by the accesor value (i.e. DAOFactory's using as won't pair, but regular ones will)
          if (self.through === true && accessor === self.associationAccessor) {
            paired = true
          }
          // If through is not default, determine pairing by through value (model/string)
          if (self.through !== true && self.options.through === association.options.through) {
            paired = true
          }
          // If paired, set properties identifying both associations as double linked, and allow them to each eachtoerh
          if (paired) {
            self.doubleLinked = true
            association.doubleLinked = true

            self.targetAssociation = association
            association.targetAssociation = self
          }
        }
      })
    }
    /*
     * If we are double linked, and through is either default or a string, we create the through model and set it on both associations
     */
    if (this.doubleLinked) {
      if (this.through === true) {
        this.through = this.combinedTableName
      }
    }

    if (typeof this.through === "string") {
      this.through = this.sequelize.define(this.through, {}, _.extend(this.options, {
        tableName: this.through,
        paranoid: false  // A paranoid join table does not make sense
      }))

      if (this.targetAssociation) {
        this.targetAssociation.through = this.through
      }
    }

    this.options.tableName = this.combinedName = (this.through === Object(this.through) ? this.through.tableName : this.through)

    if (this.as) {
      this.isAliased = true
    } else {
      this.as = Utils.pluralize(this.target.name, this.target.options.language)
    }

    this.accessors = {
      get: Utils._.camelize('get_' + this.as),
      set: Utils._.camelize('set_' + this.as),
      add: Utils._.camelize(Utils.singularize('add_' + this.as, this.target.options.language)),
      create: Utils._.camelize(Utils.singularize('create_' + this.as, this.target.options.language)),
      remove: Utils._.camelize(Utils.singularize('remove_' + this.as, this.target.options.language)),
      hasSingle: Utils._.camelize(Utils.singularize('has_' + this.as, this.target.options.language)),
      hasAll: Utils._.camelize('has_' + this.as)
    }
  }

  // the id is in the target table
  // or in an extra table which connects two tables
  HasMany.prototype.injectAttributes = function() {
    var doubleLinked      = this.doubleLinked
      , self              = this
      , primaryKeyDeleted = false

    this.identifier = this.options.foreignKey || Utils._.camelizeIf(
      [Utils.singularize(this.source.name, this.source.options.language), this.source.primaryKeyAttribute].join("_"),
      !this.source.options.underscored
    )

    // is there already a single sided association between the source and the target?
    // or is the association on the model itself?

    if ((this.isSelfAssociation && Object(this.through) === this.through) || doubleLinked) {
      // We need to remove the keys that 1:M have added
      if (this.isSelfAssociation && doubleLinked) {
        if (self.through.rawAttributes[this.targetAssociation.identifier]._autoGenerated) {
          delete self.through.rawAttributes[this.targetAssociation.identifier];
        }
        if (self.through.rawAttributes[this.targetAssociation.foreignIdentifier]._autoGenerated) {
          delete self.through.rawAttributes[this.targetAssociation.foreignIdentifier];
        }
      }

      this.foreignIdentifier = this.targetAssociation.identifier
      this.targetAssociation.foreignIdentifier = this.identifier

      if (this.isSelfAssociation && this.foreignIdentifier === this.identifier) {
        this.foreignIdentifier = Utils._.camelizeIf(
          [Utils.singularize(this.as, this.source.options.language), this.source.primaryKeyAttribute].join("_"),
          !this.source.options.underscored
        );
      }

      if (isForeignKeyDeletionAllowedFor.call(this, this.source, this.foreignIdentifier)) {
        delete this.source.rawAttributes[this.foreignIdentifier]
      }

      if (isForeignKeyDeletionAllowedFor.call(this, this.target, this.identifier)) {
        delete this.targetAssociation.source.rawAttributes[this.identifier]
      }

      // remove any PKs previously defined by sequelize
      Utils._.each(this.through.attributes, function(dataTypeString, attributeName) {
        if (dataTypeString.toString().indexOf('PRIMARY KEY') !== -1 && self.through.rawAttributes[attributeName]._autoGenerated === true) {
          delete self.through.rawAttributes[attributeName]
          primaryKeyDeleted = true
        }
      })

      // define a new model, which connects the models
      var combinedTableAttributes = {}
        , sourceKeyType = this.source.rawAttributes[this.source.primaryKeyAttribute].type
        , targetKeyType = this.target.rawAttributes[this.target.primaryKeyAttribute].type
        , sourceAttribute = { type: sourceKeyType }
        , targetAttribute = { type: targetKeyType }

      if (this.options.constraints !== false) {
        sourceAttribute.references = this.source.getTableName()
        sourceAttribute.referencesKey = this.source.primaryKeyAttribute
        sourceAttribute.onDelete = this.options.onDelete || 'CASCADE'
        sourceAttribute.onUpdate = this.options.onUpdate || 'CASCADE'
      }
      if (this.targetAssociation.options.constraints !== false) {
        targetAttribute.references = this.target.getTableName()
        targetAttribute.referencesKey = this.target.primaryKeyAttribute
        targetAttribute.onDelete = this.targetAssociation.options.onDelete || 'CASCADE'
        targetAttribute.onUpdate = this.targetAssociation.options.onUpdate || 'CASCADE'
      }

      if (primaryKeyDeleted) {
        targetAttribute.primaryKey = sourceAttribute.primaryKey = true

      } else {
        var uniqueKey = [this.through.tableName, this.identifier, this.foreignIdentifier, 'unique'].join('_')
        targetAttribute.unique = sourceAttribute.unique = uniqueKey
      }

      combinedTableAttributes[this.identifier] = sourceAttribute
      combinedTableAttributes[this.foreignIdentifier] = targetAttribute

      if (!this.through.rawAttributes[this.identifier]) {
        this.through.rawAttributes[this.identifier] = {
          _autoGenerated: true
        }
      }

      if (!this.through.rawAttributes[this.foreignIdentifier]) {
        this.through.rawAttributes[this.foreignIdentifier] = {
          _autoGenerated: true
        }
      }

      this.through.rawAttributes[this.identifier] = Utils._.merge(this.through.rawAttributes[this.identifier], sourceAttribute);
      this.through.rawAttributes[this.foreignIdentifier] = Utils._.merge(this.through.rawAttributes[this.foreignIdentifier], targetAttribute);

      this.through.init(this.through.daoFactoryManager)
    } else {
      var newAttributes = {}
      var constraintOptions = _.clone(this.options) // Create a new options object for use with addForeignKeyConstraints, to avoid polluting this.options in case it is later used for a n:m
      newAttributes[this.identifier] = { type: this.options.keyType || this.source.rawAttributes[this.source.primaryKeyAttribute].type }

      if (this.options.constraints !== false) {
        constraintOptions.onDelete = constraintOptions.onDelete || 'SET NULL'
        constraintOptions.onUpdate = constraintOptions.onUpdate || 'CASCADE'
      }
      Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, constraintOptions)
      Utils._.defaults(this.target.rawAttributes, newAttributes)
    }

    // Sync attributes and setters/getters to DAO prototype
    this.target.refreshAttributes()
    this.source.refreshAttributes()

    return this
  }

  HasMany.prototype.injectGetter = function(obj) {
    var self = this

    obj[this.accessors.get] = function(options) {
      var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
      return new Class(self, this).injectGetter(options)
    }

    obj[this.accessors.hasAll] = function(objects, options) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get](options)
          .error(function(err) { customEventEmitter.emit('error', err) })
          .success(function(associatedObjects) {
            customEventEmitter.emit('success',
              Utils._.all(objects, function(o) {
                return Utils._.any(associatedObjects, function(associatedObject) {
                  return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                    return o[identifier] == associatedObject[identifier];
                  });
                })
              })
            )
          })
      })
      return customEventEmitter.run()
    }

    obj[this.accessors.hasSingle] = function(o, options) {
      var instance           = this
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get](options)
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.any(associatedObjects, function(associatedObject) {
              return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                return o[identifier] == associatedObject[identifier];
              });
            })
          )
        })
      })
      return customEventEmitter.run()
    }
    return this
  }

  HasMany.prototype.injectSetter = function(obj) {
    var self = this

    obj[this.accessors.set] = function(newAssociatedObjects, defaultAttributes) {
      if (newAssociatedObjects === null) {
        newAssociatedObjects = []
      }

      var instance = this

      // define the returned customEventEmitter, which will emit the success event once everything is done
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({
          transaction: (defaultAttributes || {}).transaction
        })
          .success(function(oldAssociatedObjects) {
            var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
            new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
          })
          .proxy(emitter, {events: ['error', 'sql']})
      }).run()
    }

    obj[this.accessors.add] = function(newInstance, additionalAttributes) {
      var instance = this
        , primaryKey = newInstance.Model.primaryKeyAttribute
        , where = new Utils.where(self.target.rawAttributes[primaryKey], newInstance[primaryKey])

      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({
          where: where,
          transaction: (additionalAttributes || {}).transaction
        })
          .proxy(emitter, {events: ['error', 'sql']})
          .success(function(currentAssociatedObjects) {
            if (currentAssociatedObjects.length === 0 || Object(self.through) === self.through) {
              var Class = Object(self.through) === self.through ? HasManyDoubleLinked : HasManySingleLinked
              new Class(self, instance).injectAdder(emitter, newInstance, additionalAttributes, !!currentAssociatedObjects.length)
            } else {
              emitter.emit('success', newInstance);
            }
          })
      }).run()
    }

    obj[this.accessors.remove] = function(oldAssociatedObject, options) {
      var instance = this
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({
          transaction: (options || {}).transaction
        }).success(function(currentAssociatedObjects) {
          var newAssociations = []
            , oldAssociations = []

          currentAssociatedObjects.forEach(function(association) {
            if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
              newAssociations.push(association)
            }
          })

          var tick = 0
          var next = function(err, i) {
            if (!!err || i >= oldAssociations.length) {
              return run(err)
            }

            oldAssociations[i].destroy().error(function(err) {
              next(err)
            })
            .success(function() {
              tick++
              next(null, tick)
            })
          }

          var run = function(err) {
            if (!!err) {
              return emitter.emit('error', err)
            }

            instance[self.accessors.set](newAssociations).proxy(emitter)
          }

          if (oldAssociations.length > 0) {
            next(null, tick)
          } else {
            run()
          }
        })
      }).run()
    }

    return this
  }

  HasMany.prototype.injectCreator = function(obj) {
    var self = this

    obj[this.accessors.create] = function(values, fieldsOrOptions) {
      var instance = this
        , options = {}

      if ((fieldsOrOptions || {}).transaction instanceof Transaction) {
        options.transaction = fieldsOrOptions.transaction
      }

      if (Object(self.through) === self.through) {
        return new Utils.CustomEventEmitter(function(emitter) {
          // Create the related model instance
          self.target
            .create(values, fieldsOrOptions)
            .proxy(emitter, { events: ['error', 'sql'] })
            .success(function(newAssociatedObject) {
              instance[self.accessors.add](newAssociatedObject, options)
                .proxy(emitter)
            })
        }).run()
      } else {
        values[self.identifier] = instance.get(self.source.primaryKeyAttribute);
        return self.target.create(values, fieldsOrOptions)
      }
    }

    return this
  };

  /**
   * The method checks if it is ok to delete the previously defined foreign key.
   * This is done because we need to keep the foreign key if another association
   * is depending on it.
   *
   * @param  {DaoFactory}  daoFactory The source or target DaoFactory of this assocation
   * @param  {[type]}  identifier     The name of the foreign key identifier
   * @return {Boolean}                Whether or not the deletion of the foreign key is ok.
   */
  var isForeignKeyDeletionAllowedFor = function(daoFactory, identifier) {
    var isAllowed        = true
      , associationNames = Utils._.without(Object.keys(daoFactory.associations), this.associationAccessor)

    associationNames.forEach(function(associationName) {
      if (daoFactory.associations[associationName].identifier === identifier) {
        isAllowed = false
      }
    })

    return isAllowed
  }

  return HasMany
})()
<|endoftext|>


<commit_before>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import {CompositeDisposable} from 'event-kit';

import RefHolder from '../models/ref-holder';
import {RefHolderPropType} from '../prop-types';
import {autobind, extractProps} from '../helpers';

const editorProps = {
  autoIndent: PropTypes.bool,
  autoIndentOnPaste: PropTypes.bool,
  undoGroupingInterval: PropTypes.number,
  scrollSensitivity: PropTypes.number,
  encoding: PropTypes.string,
  softTabs: PropTypes.bool,
  atomicSoftTabs: PropTypes.bool,
  tabLength: PropTypes.number,
  softWrapped: PropTypes.bool,
  softWrapHangingIndentLenth: PropTypes.number,
  softWrapAtPreferredLineLength: PropTypes.bool,
  preferredLineLength: PropTypes.number,
  maxScreenLineLength: PropTypes.number,
  mini: PropTypes.bool,
  readOnly: PropTypes.bool,
  placeholderText: PropTypes.string,
  lineNumberGutterVisible: PropTypes.bool,
  showIndentGuide: PropTypes.bool,
  showLineNumbers: PropTypes.bool,
  showInvisibles: PropTypes.bool,
  invisibles: PropTypes.string,
  editorWidthInChars: PropTypes.number,
  width: PropTypes.number,
  scrollPastEnd: PropTypes.bool,
  autoHeight: PropTypes.bool,
  autoWidth: PropTypes.bool,
  showCursorOnSelection: PropTypes.bool,
};

export const TextEditorContext = React.createContext();

export default class AtomTextEditor extends React.PureComponent {
  static propTypes = {
    ...editorProps,
    text: PropTypes.string,
    didChange: PropTypes.func,
    didChangeCursorPosition: PropTypes.func,
    didAddSelection: PropTypes.func,
    didChangeSelectionRange: PropTypes.func,
    didDestroySelection: PropTypes.func,
    observeSelections: PropTypes.func,
    preserveMarkers: PropTypes.bool,
    refModel: RefHolderPropType,
    children: PropTypes.node,
  }

  static defaultProps = {
    text: '',
    didChange: () => {},
    didChangeCursorPosition: () => {},
    didAddSelection: () => {},
    didChangeSelectionRange: () => {},
    didDestroySelection: () => {},
  }

  constructor(props, context) {
    super(props, context);
    autobind(this, 'didChange', 'didChangeCursorPosition', 'observeSelections');

    this.subs = new CompositeDisposable();
    this.suppressChange = false;

    this.refElement = new RefHolder();
    this.refModel = null;
  }

  render() {
    this.getRefModel().map(() => this.quietlySetText(this.props.text));

    return (
      <Fragment>
        <atom-text-editor ref={this.refElement.setter} />
        <TextEditorContext.Provider value={this.getRefModel()}>
          {this.props.children}
        </TextEditorContext.Provider>
      </Fragment>
    );
  }

  componentDidMount() {
    this.setAttributesOnElement(this.props);

    this.refElement.map(element => {
      const editor = element.getModel();
      // editor.setText(this.props.text, {bypassReadOnly: true});

      this.getRefModel().setter(editor);

      this.subs.add(
        editor.onDidChange(this.didChange),
        editor.onDidChangeCursorPosition(this.didChangeCursorPosition),
        editor.observeSelections(this.observeSelections),
      );

      // shhh, eslint. shhhh
      return null;
    });
  }

  componentDidUpdate(prevProps) {
    this.setAttributesOnElement(this.props);
  }

  componentWillUnmount() {
    this.subs.dispose();
  }

  quietlySetText(text) {
    try {
      const editor = this.getModel();
      if (editor.getText() === text) {
        return;
      }

      this.suppressChange = true;
      if (this.props.preserveMarkers) {
        editor.getBuffer().setTextViaDiff(text);
      } else {
        editor.setText(text, {bypassReadOnly: true});
      }
    } finally {
      this.suppressChange = false;
    }
  }

  setAttributesOnElement(theProps) {
    const modelProps = extractProps(this.props, editorProps);
    this.getModel().update(modelProps);
  }

  didChange() {
    if (this.suppressChange) {
      return;
    }

    this.props.didChange(this.getModel());
  }

  didChangeCursorPosition() {
    if (!this.suppressChange) {
      this.props.didChangeCursorPosition(this.getModel());
    }
  }

  observeSelections(selection) {
    const selectionSubs = new CompositeDisposable(
      selection.onDidChangeRange(event => {
        if (!this.suppressChange) {
          this.props.didChangeSelectionRange(event);
        }
      }),
      selection.onDidDestroy(() => {
        selectionSubs.dispose();
        this.subs.remove(selectionSubs);

        if (!this.suppressChange) {
          this.props.didDestroySelection(selection);
        }
      }),
    );
    this.subs.add(selectionSubs);

    if (!this.suppressChange) {
      this.props.didAddSelection(selection);
    }
  }

  contains(element) {
    return this.refElement.map(e => e.contains(element)).getOr(false);
  }

  focus() {
    this.refElement.map(e => e.focus());
  }

  getModel() {
    return this.refElement.map(e => e.getModel()).getOr(null);
  }

  getRefModel() {
    if (this.props.refModel) {
      return this.props.refModel;
    }

    if (!this.refModel) {
      this.refModel = new RefHolder();
    }

    return this.refModel;
  }
}
<commit_msg>Oh, right. This is here so that markers don't render on row 0<commit_after>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';
import {CompositeDisposable} from 'event-kit';

import RefHolder from '../models/ref-holder';
import {RefHolderPropType} from '../prop-types';
import {autobind, extractProps} from '../helpers';

const editorProps = {
  autoIndent: PropTypes.bool,
  autoIndentOnPaste: PropTypes.bool,
  undoGroupingInterval: PropTypes.number,
  scrollSensitivity: PropTypes.number,
  encoding: PropTypes.string,
  softTabs: PropTypes.bool,
  atomicSoftTabs: PropTypes.bool,
  tabLength: PropTypes.number,
  softWrapped: PropTypes.bool,
  softWrapHangingIndentLenth: PropTypes.number,
  softWrapAtPreferredLineLength: PropTypes.bool,
  preferredLineLength: PropTypes.number,
  maxScreenLineLength: PropTypes.number,
  mini: PropTypes.bool,
  readOnly: PropTypes.bool,
  placeholderText: PropTypes.string,
  lineNumberGutterVisible: PropTypes.bool,
  showIndentGuide: PropTypes.bool,
  showLineNumbers: PropTypes.bool,
  showInvisibles: PropTypes.bool,
  invisibles: PropTypes.string,
  editorWidthInChars: PropTypes.number,
  width: PropTypes.number,
  scrollPastEnd: PropTypes.bool,
  autoHeight: PropTypes.bool,
  autoWidth: PropTypes.bool,
  showCursorOnSelection: PropTypes.bool,
};

export const TextEditorContext = React.createContext();

export default class AtomTextEditor extends React.PureComponent {
  static propTypes = {
    ...editorProps,
    text: PropTypes.string,
    didChange: PropTypes.func,
    didChangeCursorPosition: PropTypes.func,
    didAddSelection: PropTypes.func,
    didChangeSelectionRange: PropTypes.func,
    didDestroySelection: PropTypes.func,
    observeSelections: PropTypes.func,
    preserveMarkers: PropTypes.bool,
    refModel: RefHolderPropType,
    children: PropTypes.node,
  }

  static defaultProps = {
    text: '',
    didChange: () => {},
    didChangeCursorPosition: () => {},
    didAddSelection: () => {},
    didChangeSelectionRange: () => {},
    didDestroySelection: () => {},
  }

  constructor(props, context) {
    super(props, context);
    autobind(this, 'didChange', 'didChangeCursorPosition', 'observeSelections');

    this.subs = new CompositeDisposable();
    this.suppressChange = false;

    this.refElement = new RefHolder();
    this.refModel = null;
  }

  render() {
    this.getRefModel().map(() => this.quietlySetText(this.props.text));

    return (
      <Fragment>
        <atom-text-editor ref={this.refElement.setter} />
        <TextEditorContext.Provider value={this.getRefModel()}>
          {this.props.children}
        </TextEditorContext.Provider>
      </Fragment>
    );
  }

  componentDidMount() {
    this.setAttributesOnElement(this.props);

    this.refElement.map(element => {
      const editor = element.getModel();
      editor.setText(this.props.text, {bypassReadOnly: true});

      this.getRefModel().setter(editor);

      this.subs.add(
        editor.onDidChange(this.didChange),
        editor.onDidChangeCursorPosition(this.didChangeCursorPosition),
        editor.observeSelections(this.observeSelections),
      );

      // shhh, eslint. shhhh
      return null;
    });
  }

  componentDidUpdate(prevProps) {
    this.setAttributesOnElement(this.props);
  }

  componentWillUnmount() {
    this.subs.dispose();
  }

  quietlySetText(text) {
    try {
      const editor = this.getModel();
      if (editor.getText() === text) {
        return;
      }

      this.suppressChange = true;
      if (this.props.preserveMarkers) {
        editor.getBuffer().setTextViaDiff(text);
      } else {
        editor.setText(text, {bypassReadOnly: true});
      }
    } finally {
      this.suppressChange = false;
    }
  }

  setAttributesOnElement(theProps) {
    const modelProps = extractProps(this.props, editorProps);
    this.getModel().update(modelProps);
  }

  didChange() {
    if (this.suppressChange) {
      return;
    }

    this.props.didChange(this.getModel());
  }

  didChangeCursorPosition() {
    if (!this.suppressChange) {
      this.props.didChangeCursorPosition(this.getModel());
    }
  }

  observeSelections(selection) {
    const selectionSubs = new CompositeDisposable(
      selection.onDidChangeRange(event => {
        if (!this.suppressChange) {
          this.props.didChangeSelectionRange(event);
        }
      }),
      selection.onDidDestroy(() => {
        selectionSubs.dispose();
        this.subs.remove(selectionSubs);

        if (!this.suppressChange) {
          this.props.didDestroySelection(selection);
        }
      }),
    );
    this.subs.add(selectionSubs);

    if (!this.suppressChange) {
      this.props.didAddSelection(selection);
    }
  }

  contains(element) {
    return this.refElement.map(e => e.contains(element)).getOr(false);
  }

  focus() {
    this.refElement.map(e => e.focus());
  }

  getModel() {
    return this.refElement.map(e => e.getModel()).getOr(null);
  }

  getRefModel() {
    if (this.props.refModel) {
      return this.props.refModel;
    }

    if (!this.refModel) {
      this.refModel = new RefHolder();
    }

    return this.refModel;
  }
}
<|endoftext|>


<commit_before>/* @flow */

import _ from "lodash";
import { log } from "./utils";
import store from "./store";

type Autocomplete = {
  editor: atom$TextEditor,
  bufferPosition: atom$Point,
  prefix: string
};

type CompleteReply = {
  matches: Array<string>,
  cursor_start: number,
  cursor_end: number,
  metadata?: {
    _jupyter_types_experimental?: Array<{
      start?: number,
      end?: number,
      text?: string,
      type?: string
    }>
  }
};

const iconHTML = `<img src='${__dirname}/../static/logo.svg' style='width: 100%;'>`;

const regexes = {
  // pretty dodgy, adapted from http://stackoverflow.com/a/8396658
  r: /([^\d\W]|[.])[\w.$]*$/,

  // adapted from http://stackoverflow.com/q/5474008
  python: /([^\d\W]|[\u00A0-\uFFFF])[\w.\u00A0-\uFFFF]*$/,

  // adapted from http://php.net/manual/en/language.variables.basics.php
  php: /[$a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/
};

function parseCompletions(results: CompleteReply, prefix: string) {
  const { matches, cursor_start, cursor_end, metadata } = results;

  if (metadata && metadata._jupyter_types_experimental) {
    const comps = metadata._jupyter_types_experimental;
    if (
      comps.length > 0 &&
      comps[0].text != null &&
      comps[0].start != null &&
      comps[0].end != null
    ) {
      return _.map(comps, match => ({
        text: match.text,
        replacementPrefix: prefix.slice(match.start, match.end),
        type: match.type,
        iconHTML:
          !match.type || match.type === "<unknown>" ? iconHTML : undefined
      }));
    }
  }

  const replacementPrefix = prefix.slice(cursor_start, cursor_end);

  return _.map(matches, match => ({
    text: match,
    replacementPrefix,
    iconHTML
  }));
}

export default function() {
  const autocompleteProvider = {
    selector: ".source",
    disableForSelector: ".comment, .string",

    // `excludeLowerPriority: false` won't suppress providers with lower
    // priority.
    // The default provider has a priority of 0.
    inclusionPriority: 1,
    excludeLowerPriority: false,

    // Required: Return a promise, an array of suggestions, or null.
    getSuggestions({
      editor,
      bufferPosition,
      prefix
    }: Autocomplete): Promise<Array<Object>> | null {
      const kernel = store.kernel;

      if (!kernel || kernel.executionState !== "idle") {
        return null;
      }

      const line = editor.getTextInBufferRange([
        [bufferPosition.row, 0],
        bufferPosition
      ]);

      const regex = regexes[kernel.language];
      if (regex) {
        prefix = _.head(line.match(regex)) || "";
      } else {
        prefix = line;
      }

      // return if cursor is at whitespace
      if (prefix.trimRight().length < prefix.length) {
        return null;
      }

      if (prefix.trim().length < 3) {
        return null;
      }

      log("autocompleteProvider: request:", line, bufferPosition, prefix);

      return new Promise(resolve =>
        kernel.complete(prefix, (results: CompleteReply) => {
          return resolve(parseCompletions(results, prefix));
        })
      );
    }
  };

  return autocompleteProvider;
}
<commit_msg>Pr/autocomplete (#1054)<commit_after>/* @flow */

import _ from "lodash";
import { log } from "./utils";
import store from "./store";

type Autocomplete = {
  editor: atom$TextEditor,
  bufferPosition: atom$Point,
  prefix: string
};

type CompleteReply = {
  matches: Array<string>,
  cursor_start: number,
  cursor_end: number,
  metadata?: {
    _jupyter_types_experimental?: Array<{
      start?: number,
      end?: number,
      text?: string,
      type?: string
    }>
  }
};

const iconHTML = `<img src='${__dirname}/../static/logo.svg' style='width: 100%;'>`;

const regexes = {
  // pretty dodgy, adapted from http://stackoverflow.com/a/8396658
  r: /([^\d\W]|[.])[\w.$]*$/,

  // adapted from http://stackoverflow.com/q/5474008
  python: /([^\d\W]|[\u00A0-\uFFFF])[\w.\u00A0-\uFFFF]*$/,

  // adapted from http://php.net/manual/en/language.variables.basics.php
  php: /[$a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/
};

function parseCompletions(results: CompleteReply, prefix: string) {
  const { matches, cursor_start, cursor_end, metadata } = results;

  if (metadata && metadata._jupyter_types_experimental) {
    const comps = metadata._jupyter_types_experimental;
    if (
      comps.length > 0 &&
      comps[0].text != null &&
      comps[0].start != null &&
      comps[0].end != null
    ) {
      return _.map(comps, match => ({
        text: match.text,
        replacementPrefix: prefix.slice(match.start, match.end),
        type: match.type,
        iconHTML:
          !match.type || match.type === "<unknown>" ? iconHTML : undefined
      }));
    }
  }

  const replacementPrefix = prefix.slice(cursor_start, cursor_end);

  return _.map(matches, match => ({
    text: match,
    replacementPrefix,
    iconHTML
  }));
}

export default function() {
  const autocompleteProvider = {
    selector: ".source",
    disableForSelector: ".comment, .string",

    // `excludeLowerPriority: false` won't suppress providers with lower
    // priority.
    // The default provider has a priority of 0.
    inclusionPriority: 1,
    excludeLowerPriority: false,

    // Required: Return a promise, an array of suggestions, or null.
    getSuggestions({
      editor,
      bufferPosition,
      prefix
    }: Autocomplete): Promise<Array<Object>> | null {
      const kernel = store.kernel;

      if (!kernel || kernel.executionState !== "idle") {
        return null;
      }

      const line = editor.getTextInBufferRange([
        [bufferPosition.row, 0],
        bufferPosition
      ]);

      const regex = regexes[kernel.language];
      if (regex) {
        prefix = _.head(line.match(regex)) || "";
      } else {
        prefix = line;
      }

      // return if cursor is at whitespace
      if (prefix.trimRight().length < prefix.length) {
        return null;
      }

      let minimumWordLength = atom.config.get(
        "autocomplete-plus.minimumWordLength"
      );
      if (typeof minimumWordLength !== "number") {
        minimumWordLength = 3;
      }

      if (prefix.trim().length < minimumWordLength) {
        return null;
      }

      log("autocompleteProvider: request:", line, bufferPosition, prefix);

      return new Promise(resolve =>
        kernel.complete(prefix, (results: CompleteReply) => {
          return resolve(parseCompletions(results, prefix));
        })
      );
    }
  };

  return autocompleteProvider;
}
<|endoftext|>


<commit_before>var path = require('path');
var Concat = require('broccoli-concat');
var configReplace = require('./broccoli-config-replace');
var compileWithTypescript = require('./broccoli-typescript').default;
var fs = require('fs');
var Funnel = require('broccoli-funnel');
var mergeTrees = require('broccoli-merge-trees');
var Project = require('ember-cli/lib/models/project');

module.exports = Angular2App;

function Angular2App(defaults, options, additionalPaths) {
    this._initProject();
    this._notifyAddonIncluded();
    this.options = options;
    this.additionalPaths = additionalPaths || [];
}

Angular2App.prototype.toTree = function() {
    var sourceTree = 'src';
    var vendorNpmFiles = [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'es6-shim/es6-shim.js',
      'angular2/bundles/angular2-polyfills.js',
      'rxjs/bundles/Rx.js',
      'angular2/bundles/angular2.dev.js',
      'angular2/bundles/http.dev.js',
      'angular2/bundles/router.dev.js',
      'angular2/bundles/upgrade.dev.js'
    ];

    if (this.options.vendorNpmFiles) {
      vendorNpmFiles = vendorNpmFiles.concat(this.options.vendorNpmFiles);
    }

    var tsConfigCompilerOptions = JSON.parse(fs.readFileSync('src/tsconfig.json', 'utf-8')).compilerOptions;
    tsConfigCompilerOptions.rootFilePaths = ['typings.d.ts'].concat(this.additionalPaths)
      .map(function(name) {
        return path.join(process.cwd(), sourceTree, name)
      });

    var tsTree = compileWithTypescript(sourceTree, tsConfigCompilerOptions);
    var tsSrcTree = new Funnel(sourceTree, {
      include: ['**/*.ts'],
      allowEmpty: true
    });

    var jsTree = new Funnel(sourceTree, {
        include: ['**/*.js'],
        allowEmpty: true
    });

    var assetTree = new Funnel(sourceTree, {
        include: ['**/*.*'],
        exclude: ['**/*.ts', '**/*.js'],
        allowEmpty: true
    });

    var vendorNpmTree = new Funnel('node_modules', {
      include: vendorNpmFiles,
      destDir: 'vendor'
    });

    var vendorNpmInputFiles = vendorNpmFiles.map(function(file) {
      return '**/' + file.substr(file.lastIndexOf('/') + 1);
    });

    var vendorNpmJs = new Concat(vendorNpmTree, {
      inputFiles: vendorNpmInputFiles,
      outputFile: '/thirdparty/vendor.js'
    });

    var thirdPartyJsTree = new Funnel('node_modules', {
      include: ['ng2*/bundles/*.js'],
      exclude: ['ng2*/bundles/*.min.js', 'ng2*/bundles/*.standalone.js'],
    });

    var thirdPartyJs = new Concat(thirdPartyJsTree, {
      inputFiles: ['**/*.js'],
      outputFile: '/thirdparty/libs.js',
      allowNone: true
    });

    return mergeTrees([
      assetTree,
      tsSrcTree,
      tsTree,
      jsTree,
      this.index(),
      vendorNpmJs,
      thirdPartyJs
    ], { overwrite: true });
};

/**
 @private
 @method _initProject
 @param {Object} options
 */
Angular2App.prototype._initProject = function() {
    this.project = Project.closestSync(process.cwd());

    /*if (options.configPath) {
        this.project.configPath = function() { return options.configPath; };
    }*/
};

/**
 @private
 @method _notifyAddonIncluded
 */
Angular2App.prototype._notifyAddonIncluded = function() {
    this.initializeAddons();
    this.project.addons = this.project.addons.filter(function(addon) {
        addon.app = this;

        if (!addon.isEnabled || addon.isEnabled()) {
            if (addon.included) {
                addon.included(this);
            }

            return addon;
        }
    }, this);
};

/**
 Loads and initializes addons for this project.
 Calls initializeAddons on the Project.

 @private
 @method initializeAddons
 */
Angular2App.prototype.initializeAddons = function() {
    this.project.initializeAddons();
};

/**
 Returns the content for a specific type (section) for index.html.

 Currently supported types:
 - 'head'
 //- 'config-module'
 //- 'app'
 //- 'head-footer'
 //- 'test-header-footer'
 //- 'body-footer'
 //- 'test-body-footer'

 Addons can also implement this method and could also define additional
 types (eg. 'some-addon-section').

 @private
 @method contentFor
 @param  {RegExP} match  Regular expression to match against
 @param  {String} type   Type of content
 @return {String}        The content.
 */
Angular2App.prototype.contentFor = function(match, type) {
    var content = [];

    /*switch (type) {
        case 'head':          this._contentForHead(content, config);         break;
        case 'config-module': this._contentForConfigModule(content, config); break;
        case 'app-boot':      this._contentForAppBoot(content, config);      break;
    }*/

    content = this.project.addons.reduce(function(content, addon) {
        var addonContent = addon.contentFor ? addon.contentFor(type) : null;
        if (addonContent) {
            return content.concat(addonContent);
        }

        return content;
    }, content);

    return content.join('\n');
};

/**
 @private
 @method _configReplacePatterns
 @return
 */
Angular2App.prototype._configReplacePatterns = function() {
    return [/*{
        match: /\{\{EMBER_ENV\}\}/g,
        replacement: calculateEmberENV
    }, */{
        match: /\{\{content-for ['"](.+)["']\}\}/g,
        replacement: this.contentFor.bind(this)
    }/*, {
        match: /\{\{MODULE_PREFIX\}\}/g,
        replacement: calculateModulePrefix
    }*/];
};

/**
 Returns the tree for app/index.html

 @private
 @method index
 @return {Tree} Tree for app/index.html
 */
Angular2App.prototype.index = function() {
    var htmlName = 'index.html';
    var files = [
        'index.html'
    ];

    var index = new Funnel('src', {
        files: files,
        description: 'Funnel: index.html'
    });

    return configReplace(index, {
        files: [ htmlName ],
        patterns: this._configReplacePatterns()
    });
};
<commit_msg>fix(build): if no options were passed in, do not throw an exception.<commit_after>var path = require('path');
var Concat = require('broccoli-concat');
var configReplace = require('./broccoli-config-replace');
var compileWithTypescript = require('./broccoli-typescript').default;
var fs = require('fs');
var Funnel = require('broccoli-funnel');
var mergeTrees = require('broccoli-merge-trees');
var Project = require('ember-cli/lib/models/project');

module.exports = Angular2App;

function Angular2App(defaults, options, additionalPaths) {
    this._initProject();
    this._notifyAddonIncluded();
    this.options = options;
    this.additionalPaths = additionalPaths || [];
}

Angular2App.prototype.toTree = function() {
    var sourceTree = 'src';
    var vendorNpmFiles = [
      'systemjs/dist/system-polyfills.js',
      'systemjs/dist/system.src.js',
      'es6-shim/es6-shim.js',
      'angular2/bundles/angular2-polyfills.js',
      'rxjs/bundles/Rx.js',
      'angular2/bundles/angular2.dev.js',
      'angular2/bundles/http.dev.js',
      'angular2/bundles/router.dev.js',
      'angular2/bundles/upgrade.dev.js'
    ];

    if (this.options && this.options.vendorNpmFiles) {
      vendorNpmFiles = vendorNpmFiles.concat(this.options.vendorNpmFiles);
    }

    var tsConfigCompilerOptions = JSON.parse(fs.readFileSync('src/tsconfig.json', 'utf-8')).compilerOptions;
    tsConfigCompilerOptions.rootFilePaths = ['typings.d.ts'].concat(this.additionalPaths)
      .map(function(name) {
        return path.join(process.cwd(), sourceTree, name)
      });

    var tsTree = compileWithTypescript(sourceTree, tsConfigCompilerOptions);
    var tsSrcTree = new Funnel(sourceTree, {
      include: ['**/*.ts'],
      allowEmpty: true
    });

    var jsTree = new Funnel(sourceTree, {
        include: ['**/*.js'],
        allowEmpty: true
    });

    var assetTree = new Funnel(sourceTree, {
        include: ['**/*.*'],
        exclude: ['**/*.ts', '**/*.js'],
        allowEmpty: true
    });

    var vendorNpmTree = new Funnel('node_modules', {
      include: vendorNpmFiles,
      destDir: 'vendor'
    });

    var vendorNpmInputFiles = vendorNpmFiles.map(function(file) {
      return '**/' + file.substr(file.lastIndexOf('/') + 1);
    });

    var vendorNpmJs = new Concat(vendorNpmTree, {
      inputFiles: vendorNpmInputFiles,
      outputFile: '/thirdparty/vendor.js'
    });

    var thirdPartyJsTree = new Funnel('node_modules', {
      include: ['ng2*/bundles/*.js'],
      exclude: ['ng2*/bundles/*.min.js', 'ng2*/bundles/*.standalone.js'],
    });

    var thirdPartyJs = new Concat(thirdPartyJsTree, {
      inputFiles: ['**/*.js'],
      outputFile: '/thirdparty/libs.js',
      allowNone: true
    });

    return mergeTrees([
      assetTree,
      tsSrcTree,
      tsTree,
      jsTree,
      this.index(),
      vendorNpmJs,
      thirdPartyJs
    ], { overwrite: true });
};

/**
 @private
 @method _initProject
 @param {Object} options
 */
Angular2App.prototype._initProject = function() {
    this.project = Project.closestSync(process.cwd());

    /*if (options.configPath) {
        this.project.configPath = function() { return options.configPath; };
    }*/
};

/**
 @private
 @method _notifyAddonIncluded
 */
Angular2App.prototype._notifyAddonIncluded = function() {
    this.initializeAddons();
    this.project.addons = this.project.addons.filter(function(addon) {
        addon.app = this;

        if (!addon.isEnabled || addon.isEnabled()) {
            if (addon.included) {
                addon.included(this);
            }

            return addon;
        }
    }, this);
};

/**
 Loads and initializes addons for this project.
 Calls initializeAddons on the Project.

 @private
 @method initializeAddons
 */
Angular2App.prototype.initializeAddons = function() {
    this.project.initializeAddons();
};

/**
 Returns the content for a specific type (section) for index.html.

 Currently supported types:
 - 'head'
 //- 'config-module'
 //- 'app'
 //- 'head-footer'
 //- 'test-header-footer'
 //- 'body-footer'
 //- 'test-body-footer'

 Addons can also implement this method and could also define additional
 types (eg. 'some-addon-section').

 @private
 @method contentFor
 @param  {RegExP} match  Regular expression to match against
 @param  {String} type   Type of content
 @return {String}        The content.
 */
Angular2App.prototype.contentFor = function(match, type) {
    var content = [];

    /*switch (type) {
        case 'head':          this._contentForHead(content, config);         break;
        case 'config-module': this._contentForConfigModule(content, config); break;
        case 'app-boot':      this._contentForAppBoot(content, config);      break;
    }*/

    content = this.project.addons.reduce(function(content, addon) {
        var addonContent = addon.contentFor ? addon.contentFor(type) : null;
        if (addonContent) {
            return content.concat(addonContent);
        }

        return content;
    }, content);

    return content.join('\n');
};

/**
 @private
 @method _configReplacePatterns
 @return
 */
Angular2App.prototype._configReplacePatterns = function() {
    return [/*{
        match: /\{\{EMBER_ENV\}\}/g,
        replacement: calculateEmberENV
    }, */{
        match: /\{\{content-for ['"](.+)["']\}\}/g,
        replacement: this.contentFor.bind(this)
    }/*, {
        match: /\{\{MODULE_PREFIX\}\}/g,
        replacement: calculateModulePrefix
    }*/];
};

/**
 Returns the tree for app/index.html

 @private
 @method index
 @return {Tree} Tree for app/index.html
 */
Angular2App.prototype.index = function() {
    var htmlName = 'index.html';
    var files = [
        'index.html'
    ];

    var index = new Funnel('src', {
        files: files,
        description: 'Funnel: index.html'
    });

    return configReplace(index, {
        files: [ htmlName ],
        patterns: this._configReplacePatterns()
    });
};
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'gatsby'

import { rhythm, scale } from '../utils/typography'

class Layout extends React.Component {
  render() {
    const { location, title, children } = this.props
    const rootPath = `${__PATH_PREFIX__}/`
    let header

    if (location.pathname === rootPath) {
      header = (

          <Link
            style={{
              boxShadow: `none`,
              textDecoration: `none`,
              color: `inherit`,
            }}
            to={`/`}
          >
            {title}
          </Link>

      )
    } else {
      header = (
        <h3
          style={{
            fontFamily: `Montserrat, sans-serif`,
            marginTop: 0,
            marginBottom: rhythm(-1),
          }}
        >
          <Link
            style={{
              boxShadow: `none`,
              textDecoration: `none`,
              color: `inherit`,
            }}
            to={`/`}
          >
            {title}
          </Link>

      )
    }
    return (
      <div
        style={{
          marginLeft: `auto`,
          marginRight: `auto`,
          maxWidth: rhythm(24),
          padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`,
        }}
      >
        {header}
        {children}

          © {new Date().getFullYear()}, Built with
          {` `}
          Gatsby

    )
  }
}

export default Layout
<commit_msg>Remove negative margin in starter blog layout (#10758)<commit_after>import React from 'react'
import { Link } from 'gatsby'

import { rhythm, scale } from '../utils/typography'

class Layout extends React.Component {
  render() {
    const { location, title, children } = this.props
    const rootPath = `${__PATH_PREFIX__}/`
    let header

    if (location.pathname === rootPath) {
      header = (

          <Link
            style={{
              boxShadow: `none`,
              textDecoration: `none`,
              color: `inherit`,
            }}
            to={`/`}
          >
            {title}
          </Link>

      )
    } else {
      header = (
        <h3
          style={{
            fontFamily: `Montserrat, sans-serif`,
            marginTop: 0,
          }}
        >
          <Link
            style={{
              boxShadow: `none`,
              textDecoration: `none`,
              color: `inherit`,
            }}
            to={`/`}
          >
            {title}
          </Link>

      )
    }
    return (
      <div
        style={{
          marginLeft: `auto`,
          marginRight: `auto`,
          maxWidth: rhythm(24),
          padding: `${rhythm(1.5)} ${rhythm(3 / 4)}`,
        }}
      >
        {header}
        {children}

          © {new Date().getFullYear()}, Built with
          {` `}
          Gatsby

    )
  }
}

export default Layout
<|endoftext|>


<commit_before>'use strict';

var _get = require('babel-runtime/helpers/get')['default'];

var _inherits = require('babel-runtime/helpers/inherits')['default'];

var _createClass = require('babel-runtime/helpers/create-class')['default'];

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _radium = require('radium');

var _radium2 = _interopRequireDefault(_radium);

var _velocityReact = require('velocity-react');

var Loading = function Loading(props) {
    return _react2['default'].createElement(
        'div',
        { style: props.style },
        'loading...'
    );
};

Loading.propTypes = {
    style: _react2['default'].PropTypes.object
};

var Toggle = function Toggle(props) {
    var style = props.style;
    var height = style.height;
    var width = style.width;
    var midHeight = height * 0.5;
    // var points = '0,0 0,' + height + ' ' + width + ',' + midHeight;
    var points = '0.59 10.6464 5.17 6.24 0.59 1.8336 2 0.48 8 6.24 2 12';
    return _react2['default'].createElement(
        'div',
        { style: style.base },
        _react2['default'].createElement(
            'div',
            { style: style.wrapper },
            _react2['default'].createElement(
                'svg',
                { height: height, width: width },
                _react2['default'].createElement('polygon', {
                    points: points,
                    style: style.arrow
                })
            )
        )
    );
};

Toggle.propTypes = {
    style: _react2['default'].PropTypes.object
};

var Header = function Header(props) {
    var style = props.style;
    return _react2['default'].createElement(
        'div',
        { style: style.base },
        _react2['default'].createElement(
            'div',
            { style: style.title },
            props.node.name
        )
    );
};

Header.propTypes = {
    style: _react2['default'].PropTypes.object,
    node: _react2['default'].PropTypes.object.isRequired
};

var Container = (function (_React$Component) {
    _inherits(Container, _React$Component);

    function Container(props) {
        _classCallCheck(this, _Container);

        _get(Object.getPrototypeOf(_Container.prototype), 'constructor', this).call(this, props);
    }

    _createClass(Container, [{
        key: 'render',
        value: function render() {
            var _props = this.props;
            var style = _props.style;
            var decorators = _props.decorators;
            var terminal = _props.terminal;
            var onClick = _props.onClick;
            var onContextMenu = _props.onContextMenu;
            var node = _props.node;

            return _react2['default'].createElement(
                'div',
                {
                    ref: 'clickable',
                    onClick: onClick,
                    onContextMenu: onContextMenu,
                    style: style.container },
                !terminal ? this.renderToggle() : null,
                _react2['default'].createElement(decorators.Header, {
                    node: node,
                    style: style.header
                })
            );
        }
    }, {
        key: 'renderToggle',
        value: function renderToggle() {
            var animations = this.props.animations;
            if (!animations) {
                return this.renderToggleDecorator();
            }
            return _react2['default'].createElement(
                _velocityReact.VelocityComponent,
                { ref: 'velocity',
                    duration: animations.toggle.duration,
                    animation: animations.toggle.animation },
                this.renderToggleDecorator()
            );
        }
    }, {
        key: 'renderToggleDecorator',
        value: function renderToggleDecorator() {
            var _props2 = this.props;
            var style = _props2.style;
            var decorators = _props2.decorators;
            return _react2['default'].createElement(decorators.Toggle, { node: _props2.node, style: style.toggle });
        }
    }]);

    var _Container = Container;
    Container = (0, _radium2['default'])(Container) || Container;
    return Container;
})(_react2['default'].Component);

Container.propTypes = {
    style: _react2['default'].PropTypes.object.isRequired,
    decorators: _react2['default'].PropTypes.object.isRequired,
    terminal: _react2['default'].PropTypes.bool.isRequired,
    onClick: _react2['default'].PropTypes.func.isRequired,
    animations: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.object, _react2['default'].PropTypes.bool]).isRequired,
    node: _react2['default'].PropTypes.object.isRequired
};

exports['default'] = {
    Loading: Loading,
    Toggle: Toggle,
    Header: Header,
    Container: Container
};
module.exports = exports['default'];<commit_msg>Added functionality for SVG hide / unhide<commit_after>'use strict';

var _get = require('babel-runtime/helpers/get')['default'];

var _inherits = require('babel-runtime/helpers/inherits')['default'];

var _createClass = require('babel-runtime/helpers/create-class')['default'];

var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default'];

var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _radium = require('radium');

var _radium2 = _interopRequireDefault(_radium);

var _velocityReact = require('velocity-react');

var Loading = function Loading(props) {
    return _react2['default'].createElement(
        'div',
        { style: props.style },
        'loading...'
    );
};

Loading.propTypes = {
    style: _react2['default'].PropTypes.object
};

var Toggle = function Toggle(props) {
    var style = props.style;
    var svgDisplay = props.node.hideArrow ? 'none' : '';
    var height = style.height;
    var width = style.width;
    var midHeight = height * 0.5;
    // var points = '0,0 0,' + height + ' ' + width + ',' + midHeight;
    var points = '0.59 10.6464 5.17 6.24 0.59 1.8336 2 0.48 8 6.24 2 12';
    return _react2['default'].createElement(
        'div',
        { style: style.base },
        _react2['default'].createElement(
            'div',
            { style: style.wrapper },
            _react2['default'].createElement(
                'svg',
                { height: height, width: width, display: svgDisplay },
                _react2['default'].createElement('polygon', {
                    points: points,
                    style: style.arrow
                })
            )
        )
    );
};

Toggle.propTypes = {
    style: _react2['default'].PropTypes.object
};

var Header = function Header(props) {
    var style = props.style;
    return _react2['default'].createElement(
        'div',
        { style: style.base },
        _react2['default'].createElement(
            'div',
            { style: style.title },
            props.node.name
        )
    );
};

Header.propTypes = {
    style: _react2['default'].PropTypes.object,
    node: _react2['default'].PropTypes.object.isRequired
};

var Container = (function (_React$Component) {
    _inherits(Container, _React$Component);

    function Container(props) {
        _classCallCheck(this, _Container);

        _get(Object.getPrototypeOf(_Container.prototype), 'constructor', this).call(this, props);
    }

    _createClass(Container, [{
        key: 'render',
        value: function render() {
            var _props = this.props;
            var style = _props.style;
            var decorators = _props.decorators;
            var terminal = _props.terminal;
            var onClick = _props.onClick;
            var onContextMenu = _props.onContextMenu;
            var node = _props.node;

            return _react2['default'].createElement(
                'div',
                {
                    ref: 'clickable',
                    onClick: onClick,
                    onContextMenu: onContextMenu,
                    style: style.container },
                !terminal ? this.renderToggle() : null,
                _react2['default'].createElement(decorators.Header, {
                    node: node,
                    style: style.header
                })
            );
        }
    }, {
        key: 'renderToggle',
        value: function renderToggle() {
            var animations = this.props.animations;
            if (!animations) {
                return this.renderToggleDecorator();
            }
            return _react2['default'].createElement(
                _velocityReact.VelocityComponent,
                { ref: 'velocity',
                    duration: animations.toggle.duration,
                    animation: animations.toggle.animation },
                this.renderToggleDecorator()
            );
        }
    }, {
        key: 'renderToggleDecorator',
        value: function renderToggleDecorator() {
            var _props2 = this.props;
            var style = _props2.style;
            var decorators = _props2.decorators;
            return _react2['default'].createElement(decorators.Toggle, { node: _props2.node, style: style.toggle });
        }
    }]);

    var _Container = Container;
    Container = (0, _radium2['default'])(Container) || Container;
    return Container;
})(_react2['default'].Component);

Container.propTypes = {
    style: _react2['default'].PropTypes.object.isRequired,
    decorators: _react2['default'].PropTypes.object.isRequired,
    terminal: _react2['default'].PropTypes.bool.isRequired,
    onClick: _react2['default'].PropTypes.func.isRequired,
    animations: _react2['default'].PropTypes.oneOfType([_react2['default'].PropTypes.object, _react2['default'].PropTypes.bool]).isRequired,
    node: _react2['default'].PropTypes.object.isRequired
};

exports['default'] = {
    Loading: Loading,
    Toggle: Toggle,
    Header: Header,
    Container: Container
};
module.exports = exports['default'];
<|endoftext|>


<commit_before>$( function() {
	// view for the floating toolbar

	// create an event aggregator
	var eventBus = _.extend( {}, Backbone.Events );

	// the current article
	var article = new mw.pageTriage.Article( {
		eventBus: eventBus,
		pageId: mw.config.get( 'wgArticleId' ),
		includeHistory: true
	} );
	article.fetch(
		{
			'success' : function() {
				// array of tool instances
				var tools;

				// overall toolbar view
				// currently, this is the main application view.
				mw.pageTriage.ToolbarView = Backbone.View.extend( {
					template: mw.pageTriage.viewUtil.template( { 'view': 'toolbar', 'template': 'toolbarView.html' } ),
					// token for setting user options
					optionsToken: '',

					initialize: function() {
						// An array of tool instances to put on the bar, ordered top-to-bottom
						tools = new Array;

						// article information
						if ( this.isFlyoutEnabled( 'articleInfo' ) ) {
							tools.push( new mw.pageTriage.ArticleInfoView( { eventBus: eventBus, model: article } ) );
						}

						// wikilove
						if ( this.isFlyoutEnabled( 'wikiLove' ) ) {
							tools.push( new mw.pageTriage.WikiLoveView( { eventBus: eventBus, model: article } ) );
						}

						// mark as reviewed
						if ( this.isFlyoutEnabled( 'mark' ) ) {
							tools.push( new mw.pageTriage.MarkView( { eventBus: eventBus, model: article } ) );
						}

						// add tags
						if ( this.isFlyoutEnabled( 'tags' ) ) {
							tools.push( new mw.pageTriage.TagsView( { eventBus: eventBus, model: article } ) );
						}

						// delete
						if ( this.isFlyoutEnabled( 'delete' ) ) {
							tools.push( new mw.pageTriage.DeleteView( { eventBus: eventBus, model: article } ) );
						}

						// next article, should be always on
						tools.push( new mw.pageTriage.NextView( { eventBus: eventBus, model: article } ) );
					},

					/**
					 * Check if the flyout is enabled for the current namespace
					 */
					isFlyoutEnabled: function( flyout ) {
						var modules = mw.config.get( 'wgPageTriageCurationModules' );

						// this flyout is disabled for curation toolbar
						if ( typeof modules[flyout] === 'undefined' ) {
							return false;
						}

						// this flyout is disabled for current namespace
						return $.inArray( mw.config.get( 'wgNamespaceNumber' ), modules[flyout].namespace ) !== -1;
					},

					render: function() {
						var _this = this;
						// build the bar and insert into the page.

						// insert the empty toolbar into the document.
						$('body').append( this.template() );

						_.each( tools, function( tool ) {
							// append the individual tool template to the toolbar's big tool div part
							// this is the icon and hidden div. (the actual tool content)
							$( '#mwe-pt-toolbar-main' ).append( tool.place() );
						} );

						// make it draggable
						$( '#mwe-pt-toolbar' ).draggable( {
							containment: 'window',  // keep the curation bar inside the window
							delay: 200,  // these options prevent unwanted drags when attempting to click buttons
							distance: 10,
							cancel: '.mwe-pt-tool-content'
						} );

						// make the minimize button do something
						$( '.mwe-pt-toolbar-minimize-button').click( function() {
							_this.minimize( true );
						} );

						// make clicking on the minimized toolbar expand to normal size
						$( '#mwe-pt-toolbar-vertical' ).click( function() {
							_this.maximize( true );
						} );

						// since transform only works in IE 9 and higher, use writing-mode
						// to rotate the minimized toolbar content in older versions
						if ( $.browser.msie && $.browser.version < 9.0 ) {
							$( '#mwe-pt-toolbar-vertical' ).css( 'writing-mode', 'tb-rl' );
						}

						// make the close button do something
						$( '.mwe-pt-toolbar-close-button').click( function() {
							_this.hide( true );
						} );

						// lastUse expires, hide curation toolbar
						if ( mw.config.get( 'wgPageTriagelastUseExpired' ) ) {
							this.hide();
						// show the toolbar based on user preference
						} else {
							switch ( mw.user.options.get( 'curationtoolbar' ) ) {
								case 'hidden':
									this.hide();
									break;
								case 'minimized':
									this.minimize();
									break;
								case 'maximized':
								default:
									this.maximize();
									break;
							}
						}
					},

					hide: function( savePref ) {
						// hide everything
						$( '#mwe-pt-toolbar' ).hide();
						// reset the curation toolbar to original state
						$( '#mwe-pt-toolbar-inactive' ).css( 'display', 'none' );
						$( '#mwe-pt-toolbar-active' ).css( 'display', 'block' );
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-small' ).addClass( 'mwe-pt-toolbar-big' );
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'hidden' );
						}
						// insert link to reopen into the toolbox (if it doesn't already exist)
						if ( $( '#t-curationtoolbar' ).length === 0 ) {
							this.insertLink();
						}
					},

					minimize: function ( savePref ) {
						// close any open tools by triggering showTool with empty tool param
						eventBus.trigger( 'showTool', '' );
						// hide the regular toolbar content
						$( '#mwe-pt-toolbar-active' ).hide();
						// show the minimized toolbar content
						$( '#mwe-pt-toolbar-inactive' ).show();
						// switch to smaller size
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-big' ).addClass( 'mwe-pt-toolbar-small' )
							// dock to the side of the screen
							.css( 'left', 'auto' ).css( 'right', '0' );
						// set a pref for the user so the minimize state is remembered
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'minimized' );
						}
					},

					maximize: function ( savePref ) {
						// hide the minimized toolbar content
						$( '#mwe-pt-toolbar-inactive' ).hide();
						// show the regular toolbar content
						$( '#mwe-pt-toolbar-active' ).show();
						// switch to larger size
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-small' ).addClass( 'mwe-pt-toolbar-big' )
							// reset alignment to the side of the screen (since the toolbar is wider now)
							.css( 'left', 'auto' ).css( 'right', '0' );
						// set a pref for the user so the minimize state is remembered
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'maximized' );
						}
					},

					setToolbarPreference: function( state, lastUse ) {
						// if we have a token, go ahead and use it
						if ( this.optionsToken ) {
							this.finishSetToolbarPreference( state, lastUse );
						// otherwise request an options token first
						} else {
							var _this = this;
							var tokenRequest = {
								'action': 'tokens',
								'type' : 'options',
								'format': 'json'
							};
							$.ajax( {
								type: 'get',
								url: mw.util.wikiScript( 'api' ),
								data: tokenRequest,
								dataType: 'json',
								success: function( data ) {
									try {
										_this.optionsToken = data.tokens.optionstoken;
									} catch ( e ) {
										throw new Error( 'Could not get token (requires MediaWiki 1.20).' );
									}
									_this.finishSetToolbarPreference( state, lastUse );
								}
							} );
						}
					},

					finishSetToolbarPreference: function( state, lastUse ) {
						var change = 'curationtoolbar=' + state;
						if ( typeof lastUse !== 'undefined' ) {
							change += '|pagetriage-lastuse=' + lastUse;
						}
						var prefRequest = {
							'action': 'options',
							'change': change,
							'token': this.optionsToken,
							'format': 'json'
						};
						$.ajax( {
							type: 'post',
							url: mw.util.wikiScript( 'api' ),
							data: prefRequest,
							dataType: 'json'
						} );
					},

					insertLink: function () {
						var _this = this, $link = $( '

' );
						$link.find( 'a' )
							.text( mw.msg( 'pagetriage-toolbar-linktext' ) )
							.click( function ( e ) {
								if ( $( '#mwe-pt-toolbar' ).is( ':hidden' ) ) {
									var now = new Date();
									now = new Date(
										now.getUTCFullYear(),
										now.getUTCMonth(),
										now.getUTCDate(),
										now.getUTCHours(),
										now.getUTCMinutes(),
										now.getUTCSeconds()
									);

									var mwFormat = now.toString( 'yyyyMMddHHmmss' );

									$( '#mwe-pt-toolbar' ).show();
									_this.setToolbarPreference( 'maximized', mwFormat );
								}
								this.blur();
								return false;
							} );
						$( '#p-tb' ).find( 'ul' ).append( $link );
						return true;
					}
				} );

				// create an instance of the toolbar view
				var toolbar = new mw.pageTriage.ToolbarView( { eventBus: eventBus } );
				toolbar.render();
				article.set( 'successfulModelLoading', 1 );
			}
		}
	);
} );
<commit_msg>Another userjs- API fix<commit_after>$( function() {
	// view for the floating toolbar

	// create an event aggregator
	var eventBus = _.extend( {}, Backbone.Events );

	// the current article
	var article = new mw.pageTriage.Article( {
		eventBus: eventBus,
		pageId: mw.config.get( 'wgArticleId' ),
		includeHistory: true
	} );
	article.fetch(
		{
			'success' : function() {
				// array of tool instances
				var tools;

				// overall toolbar view
				// currently, this is the main application view.
				mw.pageTriage.ToolbarView = Backbone.View.extend( {
					template: mw.pageTriage.viewUtil.template( { 'view': 'toolbar', 'template': 'toolbarView.html' } ),
					// token for setting user options
					optionsToken: '',

					initialize: function() {
						// An array of tool instances to put on the bar, ordered top-to-bottom
						tools = new Array;

						// article information
						if ( this.isFlyoutEnabled( 'articleInfo' ) ) {
							tools.push( new mw.pageTriage.ArticleInfoView( { eventBus: eventBus, model: article } ) );
						}

						// wikilove
						if ( this.isFlyoutEnabled( 'wikiLove' ) ) {
							tools.push( new mw.pageTriage.WikiLoveView( { eventBus: eventBus, model: article } ) );
						}

						// mark as reviewed
						if ( this.isFlyoutEnabled( 'mark' ) ) {
							tools.push( new mw.pageTriage.MarkView( { eventBus: eventBus, model: article } ) );
						}

						// add tags
						if ( this.isFlyoutEnabled( 'tags' ) ) {
							tools.push( new mw.pageTriage.TagsView( { eventBus: eventBus, model: article } ) );
						}

						// delete
						if ( this.isFlyoutEnabled( 'delete' ) ) {
							tools.push( new mw.pageTriage.DeleteView( { eventBus: eventBus, model: article } ) );
						}

						// next article, should be always on
						tools.push( new mw.pageTriage.NextView( { eventBus: eventBus, model: article } ) );
					},

					/**
					 * Check if the flyout is enabled for the current namespace
					 */
					isFlyoutEnabled: function( flyout ) {
						var modules = mw.config.get( 'wgPageTriageCurationModules' );

						// this flyout is disabled for curation toolbar
						if ( typeof modules[flyout] === 'undefined' ) {
							return false;
						}

						// this flyout is disabled for current namespace
						return $.inArray( mw.config.get( 'wgNamespaceNumber' ), modules[flyout].namespace ) !== -1;
					},

					render: function() {
						var _this = this;
						// build the bar and insert into the page.

						// insert the empty toolbar into the document.
						$('body').append( this.template() );

						_.each( tools, function( tool ) {
							// append the individual tool template to the toolbar's big tool div part
							// this is the icon and hidden div. (the actual tool content)
							$( '#mwe-pt-toolbar-main' ).append( tool.place() );
						} );

						// make it draggable
						$( '#mwe-pt-toolbar' ).draggable( {
							containment: 'window',  // keep the curation bar inside the window
							delay: 200,  // these options prevent unwanted drags when attempting to click buttons
							distance: 10,
							cancel: '.mwe-pt-tool-content'
						} );

						// make the minimize button do something
						$( '.mwe-pt-toolbar-minimize-button').click( function() {
							_this.minimize( true );
						} );

						// make clicking on the minimized toolbar expand to normal size
						$( '#mwe-pt-toolbar-vertical' ).click( function() {
							_this.maximize( true );
						} );

						// since transform only works in IE 9 and higher, use writing-mode
						// to rotate the minimized toolbar content in older versions
						if ( $.browser.msie && $.browser.version < 9.0 ) {
							$( '#mwe-pt-toolbar-vertical' ).css( 'writing-mode', 'tb-rl' );
						}

						// make the close button do something
						$( '.mwe-pt-toolbar-close-button').click( function() {
							_this.hide( true );
						} );

						// lastUse expires, hide curation toolbar
						if ( mw.config.get( 'wgPageTriagelastUseExpired' ) ) {
							this.hide();
						// show the toolbar based on user preference
						} else {
							switch ( mw.user.options.get( 'userjs-curationtoolbar' ) ) {
								case 'hidden':
									this.hide();
									break;
								case 'minimized':
									this.minimize();
									break;
								case 'maximized':
								default:
									this.maximize();
									break;
							}
						}
					},

					hide: function( savePref ) {
						// hide everything
						$( '#mwe-pt-toolbar' ).hide();
						// reset the curation toolbar to original state
						$( '#mwe-pt-toolbar-inactive' ).css( 'display', 'none' );
						$( '#mwe-pt-toolbar-active' ).css( 'display', 'block' );
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-small' ).addClass( 'mwe-pt-toolbar-big' );
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'hidden' );
						}
						// insert link to reopen into the toolbox (if it doesn't already exist)
						if ( $( '#t-curationtoolbar' ).length === 0 ) {
							this.insertLink();
						}
					},

					minimize: function ( savePref ) {
						// close any open tools by triggering showTool with empty tool param
						eventBus.trigger( 'showTool', '' );
						// hide the regular toolbar content
						$( '#mwe-pt-toolbar-active' ).hide();
						// show the minimized toolbar content
						$( '#mwe-pt-toolbar-inactive' ).show();
						// switch to smaller size
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-big' ).addClass( 'mwe-pt-toolbar-small' )
							// dock to the side of the screen
							.css( 'left', 'auto' ).css( 'right', '0' );
						// set a pref for the user so the minimize state is remembered
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'minimized' );
						}
					},

					maximize: function ( savePref ) {
						// hide the minimized toolbar content
						$( '#mwe-pt-toolbar-inactive' ).hide();
						// show the regular toolbar content
						$( '#mwe-pt-toolbar-active' ).show();
						// switch to larger size
						$( '#mwe-pt-toolbar' ).removeClass( 'mwe-pt-toolbar-small' ).addClass( 'mwe-pt-toolbar-big' )
							// reset alignment to the side of the screen (since the toolbar is wider now)
							.css( 'left', 'auto' ).css( 'right', '0' );
						// set a pref for the user so the minimize state is remembered
						if ( typeof savePref !== 'undefined' && savePref === true ) {
							this.setToolbarPreference( 'maximized' );
						}
					},

					setToolbarPreference: function( state, lastUse ) {
						// if we have a token, go ahead and use it
						if ( this.optionsToken ) {
							this.finishSetToolbarPreference( state, lastUse );
						// otherwise request an options token first
						} else {
							var _this = this;
							var tokenRequest = {
								'action': 'tokens',
								'type' : 'options',
								'format': 'json'
							};
							$.ajax( {
								type: 'get',
								url: mw.util.wikiScript( 'api' ),
								data: tokenRequest,
								dataType: 'json',
								success: function( data ) {
									try {
										_this.optionsToken = data.tokens.optionstoken;
									} catch ( e ) {
										throw new Error( 'Could not get token (requires MediaWiki 1.20).' );
									}
									_this.finishSetToolbarPreference( state, lastUse );
								}
							} );
						}
					},

					finishSetToolbarPreference: function( state, lastUse ) {
						var change = 'userjs-curationtoolbar=' + state;
						if ( typeof lastUse !== 'undefined' ) {
							change += '|pagetriage-lastuse=' + lastUse;
						}
						var prefRequest = {
							'action': 'options',
							'change': change,
							'token': this.optionsToken,
							'format': 'json'
						};
						$.ajax( {
							type: 'post',
							url: mw.util.wikiScript( 'api' ),
							data: prefRequest,
							dataType: 'json'
						} );
					},

					insertLink: function () {
						var _this = this, $link = $( '

' );
						$link.find( 'a' )
							.text( mw.msg( 'pagetriage-toolbar-linktext' ) )
							.click( function ( e ) {
								if ( $( '#mwe-pt-toolbar' ).is( ':hidden' ) ) {
									var now = new Date();
									now = new Date(
										now.getUTCFullYear(),
										now.getUTCMonth(),
										now.getUTCDate(),
										now.getUTCHours(),
										now.getUTCMinutes(),
										now.getUTCSeconds()
									);

									var mwFormat = now.toString( 'yyyyMMddHHmmss' );

									$( '#mwe-pt-toolbar' ).show();
									_this.setToolbarPreference( 'maximized', mwFormat );
								}
								this.blur();
								return false;
							} );
						$( '#p-tb' ).find( 'ul' ).append( $link );
						return true;
					}
				} );

				// create an instance of the toolbar view
				var toolbar = new mw.pageTriage.ToolbarView( { eventBus: eventBus } );
				toolbar.render();
				article.set( 'successfulModelLoading', 1 );
			}
		}
	);
} );
<|endoftext|>