<commit_before>/**
 * @license
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Prototype for original proto-Models.
 * Used during bootstrapping to create the real Model
 * and PropertyModel.
 *
 * TODO: The handling of the various property types (properties,
 *   templates, listeners, etc.) shouldn't be handled here because
 *   it isn't extensible.  The handling should be defined in the
 *   properties property (so meta).
 *
 * TODO: Is still used by a few views in view.js.  Those views
 * should be fixed and then ModelProto should be deleted at
 * the end of metamodel.js once the real Model is created.
 **/

this.Method = null;
this.Action = null;
this.Relationship = null;

/**
 * Override a method, making calling the overridden method possible by
 * calling this.SUPER();
 **/
/* Begin Rhino Compatible Version */
// The try/catch prevents JIT-ing of this method, but it is still faster than
// the alternate version
function override(cls, methodName, method) {
  var super_ = cls[methodName];

  var SUPER = function() { return super_.apply(this, arguments); };

  var f = function() {
    var OLD_SUPER = this.SUPER;
    this.SUPER = SUPER;
    try {
      return method.apply(this, arguments);
    } finally {
      this.SUPER = OLD_SUPER;
    }
  };

  f.super_ = super_;

  cls[methodName] = f;
}
/* End Rhino Compatible Version */

/* Begin Non-Rhino Version */
/*
function override(cls, methodName, method) {
  method.super_ = cls[methodName];
  cls[methodName] = method;
}

Object.defineProperty(AbstractPrototype, 'SUPER', {
  get: function() {
    return arguments.callee.caller.super_.bind(this);
  }
});
*/
/* End Non-Rhino Version */

var ModelProto = {

  __proto__: PropertyChangeSupport,

  TYPE: 'ModelProto <startup only, error if you see this>',

  buildPrototype: function() {
    var extendsModel = this.extendsModel && GLOBAL[this.extendsModel];
    var cls = Object.create(extendsModel ? extendsModel.getPrototype() : AbstractPrototype);
    cls.model_    = this;
    cls.name_     = this.name;
    cls.TYPE      = this.name + "Prototype";

    /** Add a method to 'cls' and set it's name. **/
    function addMethod(name, method) {
      if ( cls.__proto__[name] ) {
        override(cls, name, method);
      } else {
        cls[name] = method;
      }
    }

    // add sub-models
    //        this.models && this.models.forEach(function(m) {
    //          cls[m.name] = JSONUtil.mapToObj(m);
    //        });
    // Workaround for crbug.com/258552
    this.models && Object_forEach(this.models, function(m) {
      cls.model_[m.name] = cls[m.name] = JSONUtil.mapToObj(m, Model);
    });

    // build properties
    if ( this.properties ) {
      for ( var i = 0 ; i < this.properties.length ; i++ ) {
        var p = this.properties[i];
        if ( extendsModel ) {
          var superProp = extendsModel.getProperty(p.name);
          if ( superProp ) {
            p = superProp.clone().copyFrom(p);
            this.properties[i] = p;
            this[p.name.constantize()] = p;
          }
        }
        cls.defineProperty(p);
      }
      this.propertyMap_ = null;
    }

    // Copy parent Model's Property Contants to this Model.
    if ( extendsModel ) {
      for ( var i = 0 ; i < extendsModel.properties.length ; i++ ) {
        var p = extendsModel.properties[i];
        var name = p.name.constantize();

        if ( ! this[name] ) this[name] = p;
      }
    }

    // templates
    this.templates && Object_forEach(this.templates, function(t) {
      addMethod(t.name, TemplateUtil.lazyCompile(t));
    });

    // mix-in mixins
    // Workaround for crbug.com/258522
    // this.mixins && Object_forEach(this.mixins, function(m) { /* TODO: something */ });

    // add action
    if ( this.actions ) {
      for ( var i = 0 ; i < this.actions.length ; i++ ) {
        (function(a) {
          if ( extendsModel ) {
            var superAction = extendsModel.getAction(a.name);
            if ( superAction ) {
              console.log('superAction: ', a.name, a.model_.name);
              a = superAction.clone().copyFrom(a);
              this.actions[i] = a;
            }
          }
          addMethod(a.name, function() { a.callIfEnabled(this); });
        }.bind(this))(this.actions[i]);
      }
    }

    // add methods
    for ( var key in this.methods ) {
      var m = this.methods[key];
      if ( Method && Method.isInstance(m) ) {
        addMethod(m.name, m.generateFunction());
      } else {
        addMethod(key, m);
      }
    }

    // add relationships
    for ( var key in this.relationships ) {
      var r = this.relationships[key];

      // console.log('************** rel: ', r, r.name, r.label, r.relatedModel, r.relatedProperty);

      //           this[r.name.constantize()] = r;

      Object.defineProperty(cls, r.name, {
        get: (function (r) {
          return function() {
            return GLOBAL[r.relatedModel].where(EQ(r.relatedProperty, this.id));
          };
        })(r),
        configurable: false
      });
    }

    // todo: move this somewhere better
    var createListenerTrampoline = function(cls, name, fn, isMerged, isAnimated) {
      // bind a trampoline to the function which
      // re-binds a bound version of the function
      // when first called
      fn.name = name;

      Object.defineProperty(cls, name, {
        get: function () {
          var l = fn.bind(this);
          /*
          if ( ( isAnimated || isMerged ) && this.X.isBackground ) {
            console.log('*********************** ', this.model_.name);
            debugger;
          }
          */
          if ( isAnimated )
            l = EventService.animate(l, this.X);
          else if ( isMerged ) {
            l = EventService.merged(
              l,
              (isMerged === true) ? undefined : isMerged, this.X);
          }

          Object.defineProperty(this, name, { value: l});

          return l;
        },
        configurable: true
      });
    };

    // add listeners
    if ( Array.isArray(this.listeners) ) {
      for ( var i = 0 ; i < this.listeners.length ; i++ ) {
        var l = this.listeners[i];
        createListenerTrampoline(cls, l.name, l.code, l.isMerged, l.isAnimated);
      }
    } else if ( this.listeners )
      //          this.listeners.forEach(function(l, key) {
      // Workaround for crbug.com/258522
      Object_forEach(this.listeners, function(l, key) {
        createListenerTrampoline(cls, key, l);
      });

    // add topics
    //        this.topics && this.topics.forEach(function(t) {
    // Workaround for crbug.com/258522
    this.topics && Object_forEach(this.topics, function(t) {
      // TODO: something
    });

    // copy parent model's properties and actions into this model
    if ( extendsModel ) {
      for ( var i = extendsModel.properties.length-1 ; i >= 0 ; i-- ) {
        var p = extendsModel.properties[i];
        if ( ! ( this.getProperty && this.getPropertyWithoutCache_(p.name) ) )
          this.properties.unshift(p);
      }
      this.propertyMap_ = null;
      this.actions = extendsModel.actions.concat(this.actions);
    }

    // build primary key getter and setter
    if ( this.properties.length > 0 && ! cls.__lookupGetter__('id') ) {
      var primaryKey = this.ids;

      if ( primaryKey.length == 1 ) {
        cls.__defineGetter__('id', function() { return this[primaryKey[0]]; });
        cls.__defineSetter__('id', function(val) { this[primaryKey[0]] = val; });
      } else if (primaryKey.length > 1) {
        cls.__defineGetter__('id', function() {
          return primaryKey.map(function(key) { return this[key]; }); });
        cls.__defineSetter__('id', function(val) {
          primaryKey.map(function(key, i) { this[key] = val[i]; }); });
      }
    }

    return cls;
  },

  getPrototype: function() {
    return this.prototype_ && this.prototype_.model_ == this ?
      this.prototype_ :
      ( this.prototype_ = this.buildPrototype() );
  },

  create: function(args, X) { return this.getPrototype().create(args, X); },

  isSubModel: function(model) {
    try {
      return model && ( model === this || this.isSubModel(model.getPrototype().__proto__.model_) );
    } catch (x) {
      return false;
    }
  },

  getPropertyWithoutCache_: function(name) {
    for ( var i = 0 ; i < this.properties.length ; i++ ) {
      var p = this.properties[i];

      if ( p.name === name ) return p;
    }

    return null;
  },

  getProperty: function(name) {
    // NOTE: propertyMap_ is invalidated in a few places
    // when properties[] is updated.
    if ( ! this.propertyMap_ ) {
      if ( ! this.properties.length ) return undefined;

      var m = {};

      for ( var i = 0 ; i < this.properties.length ; i++ ) {
        var prop = this.properties[i];
        m[prop.name] = prop;
      }

      this.propertyMap_ = m;
    }

    return this.propertyMap_[name];
  },

  getAction: function(name) {
    for ( var i = 0 ; i < this.actions.length ; i++ )
      if ( this.actions[i].name === name ) return this.actions[i];
  },

  hashCode: function() {
    var string = "";
    for ( var key in this.properties ) {
      string += this.properties[key].toString();
    }
    return string.hashCode();
  },

  isInstance: function(obj) { return obj && obj.model_ && this.isSubModel(obj.model_); },

  toString: function() { return "ModelProto(" + this.name + ")"; }
};

/*
 * Ex.
 * OR(EQ(Issue.ASSIGNED_TO, 'kgr'), EQ(Issue.SEVERITY, 'Minor')).toSQL();
 *   -> "(assignedTo = 'kgr' OR severity = 'Minor')"
 * OR(EQ(Issue.ASSIGNED_TO, 'kgr'), EQ(Issue.SEVERITY, 'Minor')).f(Issue.create({assignedTo: 'kgr'}));
 *   -> true
 */
<commit_msg>Fixes #266 bad value for extendsModel doesn't generate error.<commit_after>/**
 * @license
 * Copyright 2012 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * Prototype for original proto-Models.
 * Used during bootstrapping to create the real Model
 * and PropertyModel.
 *
 * TODO: The handling of the various property types (properties,
 *   templates, listeners, etc.) shouldn't be handled here because
 *   it isn't extensible.  The handling should be defined in the
 *   properties property (so meta).
 *
 * TODO: Is still used by a few views in view.js.  Those views
 * should be fixed and then ModelProto should be deleted at
 * the end of metamodel.js once the real Model is created.
 **/

this.Method = null;
this.Action = null;
this.Relationship = null;

/**
 * Override a method, making calling the overridden method possible by
 * calling this.SUPER();
 **/
/* Begin Rhino Compatible Version */
// The try/catch prevents JIT-ing of this method, but it is still faster than
// the alternate version
function override(cls, methodName, method) {
  var super_ = cls[methodName];

  var SUPER = function() { return super_.apply(this, arguments); };

  var f = function() {
    var OLD_SUPER = this.SUPER;
    this.SUPER = SUPER;
    try {
      return method.apply(this, arguments);
    } finally {
      this.SUPER = OLD_SUPER;
    }
  };

  f.super_ = super_;

  cls[methodName] = f;
}
/* End Rhino Compatible Version */

/* Begin Non-Rhino Version */
/*
function override(cls, methodName, method) {
  method.super_ = cls[methodName];
  cls[methodName] = method;
}

Object.defineProperty(AbstractPrototype, 'SUPER', {
  get: function() {
    return arguments.callee.caller.super_.bind(this);
  }
});
*/
/* End Non-Rhino Version */

var ModelProto = {

  __proto__: PropertyChangeSupport,

  TYPE: 'ModelProto <startup only, error if you see this>',

  buildPrototype: function() {
    if ( this.extendsModel && ! GLOBAL[this.extendsModel] ) throw 'Unknown Model in extendsModel: ' + this.extendsModel;

    var extendsModel = this.extendsModel && GLOBAL[this.extendsModel];
    var cls = Object.create(extendsModel ? extendsModel.getPrototype() : AbstractPrototype);
    cls.model_    = this;
    cls.name_     = this.name;
    cls.TYPE      = this.name + "Prototype";

    /** Add a method to 'cls' and set it's name. **/
    function addMethod(name, method) {
      if ( cls.__proto__[name] ) {
        override(cls, name, method);
      } else {
        cls[name] = method;
      }
    }

    // add sub-models
    //        this.models && this.models.forEach(function(m) {
    //          cls[m.name] = JSONUtil.mapToObj(m);
    //        });
    // Workaround for crbug.com/258552
    this.models && Object_forEach(this.models, function(m) {
      cls.model_[m.name] = cls[m.name] = JSONUtil.mapToObj(m, Model);
    });

    // build properties
    if ( this.properties ) {
      for ( var i = 0 ; i < this.properties.length ; i++ ) {
        var p = this.properties[i];
        if ( extendsModel ) {
          var superProp = extendsModel.getProperty(p.name);
          if ( superProp ) {
            p = superProp.clone().copyFrom(p);
            this.properties[i] = p;
            this[p.name.constantize()] = p;
          }
        }
        cls.defineProperty(p);
      }
      this.propertyMap_ = null;
    }

    // Copy parent Model's Property Contants to this Model.
    if ( extendsModel ) {
      for ( var i = 0 ; i < extendsModel.properties.length ; i++ ) {
        var p = extendsModel.properties[i];
        var name = p.name.constantize();

        if ( ! this[name] ) this[name] = p;
      }
    }

    // templates
    this.templates && Object_forEach(this.templates, function(t) {
      addMethod(t.name, TemplateUtil.lazyCompile(t));
    });

    // mix-in mixins
    // Workaround for crbug.com/258522
    // this.mixins && Object_forEach(this.mixins, function(m) { /* TODO: something */ });

    // add action
    if ( this.actions ) {
      for ( var i = 0 ; i < this.actions.length ; i++ ) {
        (function(a) {
          if ( extendsModel ) {
            var superAction = extendsModel.getAction(a.name);
            if ( superAction ) {
              console.log('superAction: ', a.name, a.model_.name);
              a = superAction.clone().copyFrom(a);
              this.actions[i] = a;
            }
          }
          addMethod(a.name, function() { a.callIfEnabled(this); });
        }.bind(this))(this.actions[i]);
      }
    }

    // add methods
    for ( var key in this.methods ) {
      var m = this.methods[key];
      if ( Method && Method.isInstance(m) ) {
        addMethod(m.name, m.generateFunction());
      } else {
        addMethod(key, m);
      }
    }

    // add relationships
    for ( var key in this.relationships ) {
      var r = this.relationships[key];

      // console.log('************** rel: ', r, r.name, r.label, r.relatedModel, r.relatedProperty);

      //           this[r.name.constantize()] = r;

      Object.defineProperty(cls, r.name, {
        get: (function (r) {
          return function() {
            return GLOBAL[r.relatedModel].where(EQ(r.relatedProperty, this.id));
          };
        })(r),
        configurable: false
      });
    }

    // todo: move this somewhere better
    var createListenerTrampoline = function(cls, name, fn, isMerged, isAnimated) {
      // bind a trampoline to the function which
      // re-binds a bound version of the function
      // when first called
      fn.name = name;

      Object.defineProperty(cls, name, {
        get: function () {
          var l = fn.bind(this);
          /*
          if ( ( isAnimated || isMerged ) && this.X.isBackground ) {
            console.log('*********************** ', this.model_.name);
            debugger;
          }
          */
          if ( isAnimated )
            l = EventService.animate(l, this.X);
          else if ( isMerged ) {
            l = EventService.merged(
              l,
              (isMerged === true) ? undefined : isMerged, this.X);
          }

          Object.defineProperty(this, name, { value: l});

          return l;
        },
        configurable: true
      });
    };

    // add listeners
    if ( Array.isArray(this.listeners) ) {
      for ( var i = 0 ; i < this.listeners.length ; i++ ) {
        var l = this.listeners[i];
        createListenerTrampoline(cls, l.name, l.code, l.isMerged, l.isAnimated);
      }
    } else if ( this.listeners )
      //          this.listeners.forEach(function(l, key) {
      // Workaround for crbug.com/258522
      Object_forEach(this.listeners, function(l, key) {
        createListenerTrampoline(cls, key, l);
      });

    // add topics
    //        this.topics && this.topics.forEach(function(t) {
    // Workaround for crbug.com/258522
    this.topics && Object_forEach(this.topics, function(t) {
      // TODO: something
    });

    // copy parent model's properties and actions into this model
    if ( extendsModel ) {
      for ( var i = extendsModel.properties.length-1 ; i >= 0 ; i-- ) {
        var p = extendsModel.properties[i];
        if ( ! ( this.getProperty && this.getPropertyWithoutCache_(p.name) ) )
          this.properties.unshift(p);
      }
      this.propertyMap_ = null;
      this.actions = extendsModel.actions.concat(this.actions);
    }

    // build primary key getter and setter
    if ( this.properties.length > 0 && ! cls.__lookupGetter__('id') ) {
      var primaryKey = this.ids;

      if ( primaryKey.length == 1 ) {
        cls.__defineGetter__('id', function() { return this[primaryKey[0]]; });
        cls.__defineSetter__('id', function(val) { this[primaryKey[0]] = val; });
      } else if (primaryKey.length > 1) {
        cls.__defineGetter__('id', function() {
          return primaryKey.map(function(key) { return this[key]; }); });
        cls.__defineSetter__('id', function(val) {
          primaryKey.map(function(key, i) { this[key] = val[i]; }); });
      }
    }

    return cls;
  },

  getPrototype: function() {
    return this.prototype_ && this.prototype_.model_ == this ?
      this.prototype_ :
      ( this.prototype_ = this.buildPrototype() );
  },

  create: function(args, X) { return this.getPrototype().create(args, X); },

  isSubModel: function(model) {
    try {
      return model && ( model === this || this.isSubModel(model.getPrototype().__proto__.model_) );
    } catch (x) {
      return false;
    }
  },

  getPropertyWithoutCache_: function(name) {
    for ( var i = 0 ; i < this.properties.length ; i++ ) {
      var p = this.properties[i];

      if ( p.name === name ) return p;
    }

    return null;
  },

  getProperty: function(name) {
    // NOTE: propertyMap_ is invalidated in a few places
    // when properties[] is updated.
    if ( ! this.propertyMap_ ) {
      if ( ! this.properties.length ) return undefined;

      var m = {};

      for ( var i = 0 ; i < this.properties.length ; i++ ) {
        var prop = this.properties[i];
        m[prop.name] = prop;
      }

      this.propertyMap_ = m;
    }

    return this.propertyMap_[name];
  },

  getAction: function(name) {
    for ( var i = 0 ; i < this.actions.length ; i++ )
      if ( this.actions[i].name === name ) return this.actions[i];
  },

  hashCode: function() {
    var string = "";
    for ( var key in this.properties ) {
      string += this.properties[key].toString();
    }
    return string.hashCode();
  },

  isInstance: function(obj) { return obj && obj.model_ && this.isSubModel(obj.model_); },

  toString: function() { return "ModelProto(" + this.name + ")"; }
};

/*
 * Ex.
 * OR(EQ(Issue.ASSIGNED_TO, 'kgr'), EQ(Issue.SEVERITY, 'Minor')).toSQL();
 *   -> "(assignedTo = 'kgr' OR severity = 'Minor')"
 * OR(EQ(Issue.ASSIGNED_TO, 'kgr'), EQ(Issue.SEVERITY, 'Minor')).f(Issue.create({assignedTo: 'kgr'}));
 *   -> true
 */
<|endoftext|>


<commit_before>'use strict';

var self = put;
module.exports = self;

var async = require('async');
var path = require('path');
var _ = require('underscore');
var spawn = require('child_process').spawn;
var fs = require('fs');

var APIAdapter = require('../../common/APIAdapter.js');
var configHandler = require('../../common/configHandler.js');

function put(req, res) {
  var bag = {
    inputParams: req.params,
    reqBody: req.body,
    resBody: {},
    apiAdapter: new APIAdapter(req.headers.authorization.split(' ')[1]),
    params: {},
    tmpScript: '/tmp/update_service.sh',
    isSwarmClusterInitialized: false
  };

  bag.who = util.format('services|%s', self.name);
  logger.info(bag.who, 'Starting');

  async.series([
      _checkInputParams.bind(null, bag),
      _getServiceConfig.bind(null, bag),
      _getMaster.bind(null, bag),
      _getWorkers.bind(null, bag),
      _generateEnvs.bind(null, bag),
      _generateScript.bind(null, bag),
      _writeScriptToFile.bind(null, bag),
      _deleteService.bind(null, bag),
      _delete.bind(null, bag),
    ],
    function (err) {
      logger.info(bag.who, 'Completed');
      if (err)
        return respondWithError(res, err);

      sendJSONResponse(res, bag.resBody);
    }
  );
}

function _checkInputParams(bag, next) {
  var who = bag.who + '|' + _checkInputParams.name;
  logger.verbose(who, 'Inside');

  if (!bag.inputParams.serviceName)
    return next(
      new ActErr(who, ActErr.ParamNotFound,
        'Data not found :serviceName')
    );
  bag.name = bag.inputParams.serviceName;

  if (!bag.reqBody)
    return next(
      new ActErr(who, ActErr.BodyNotFound, 'Missing body')
    );

  if (!_.isString(bag.reqBody.replicas) && !_.isNumber(bag.reqBody.replicas))
    return next(
      new ActErr(who, ActErr.ParamNotFound,
        'Data not found :replicas')
    );

  bag.replicas = bag.reqBody.replicas;

  return next();
}

function _getServiceConfig(bag, next) {
  var who = bag.who + '|' + _getServiceConfig.name;
  logger.verbose(who, 'Inside');

  configHandler.get('services',
    function (err, config) {
      if (err)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Failed to get services', err)
        );

      if (_.isEmpty(config))
        return next(
          new ActErr(who, ActErr.DataNotFound, 'No service configs present'));

      if (!_.has(config, bag.name))
        return next(
          new ActErr(who, ActErr.DataNotFound, 'No service configs present ' +
          ' for :' + bag.name)
        );

      bag.services = config;
      bag.serviceConfig = config[bag.name];
      return next();
    }
  );
}

function _getMaster(bag, next) {
  var who = bag.who + '|' + _getMaster.name;
  logger.verbose(who, 'Inside');

  configHandler.get('master',
    function (err, master) {
      if (err)
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'Failed to get master', err)
        );

      if (_.isEmpty(master))
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'No configuration in database for master')
        );

      bag.master = master;
      return next();
    }
  );
}

function _getWorkers(bag, next) {
  var who = bag.who + '|' + _getWorkers.name;
  logger.verbose(who, 'Inside');

  configHandler.get('workers',
    function (err, workers) {
      if (err)
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'Failed to get ' + bag.component, err)
        );

      if (_.isEmpty(workers))
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'No configuration in database for ' + bag.component)
        );
      // if even one worker has external IP then swarm cluster is initialized
      bag.isSwarmClusterInitialized = _.some(workers,
        function (worker) {
          return worker.address !== bag.master.address;
        }
      );
      return next();
    }
  );
}

function _generateEnvs(bag, next) {
  var who = bag.who + '|' + _generateEnvs.name;
  logger.verbose(who, 'Inside');

  bag.scriptEnvs = {
    'RUNTIME_DIR': global.config.runtimeDir,
    'SCRIPTS_DIR': global.config.scriptsDir,
    'SERVICE_NAME': bag.serviceConfig.serviceName,
    'SERVICE_IMAGE': bag.serviceConfig.image,
    'IS_SWARM_INITIALIZED': bag.isSwarmClusterInitialized,
    'REPLICAS': bag.replicas
  };

  return next();
}

function _generateScript(bag, next) {
  var who = bag.who + '|' + _generateScript.name;
  logger.verbose(who, 'Inside');

  var script = '';
  //attach header
  var filePath = path.join(global.config.scriptsDir, '/lib/_logger.sh');
  script = script.concat(__applyTemplate(filePath, bag.params));

  filePath = path.join(global.config.scriptsDir, 'update_service.sh');
  script = script.concat(__applyTemplate(filePath, bag.params));

  bag.script = script;
  return next();
}

function _writeScriptToFile(bag, next) {
  var who = bag.who + '|' + _writeScriptToFile.name;
  logger.debug(who, 'Inside');

  fs.writeFile(bag.tmpScript,
    bag.script,
    function (err) {
      if (err) {
        var msg = util.format('%s, Failed with err:%s', who, err);
        return next(
          new ActErr(
            who, ActErr.OperationFailed, msg)
        );
      }
      fs.chmodSync(bag.tmpScript, '755');
      return next();
    }
  );
}

function _deleteService(bag, next) {
  var who = bag.who + '|' + _deleteService.name;
  logger.verbose(who, 'Inside');

  var exec = spawn('/bin/bash',
    ['-c', bag.script],
    {
      env: bag.scriptEnvs
    }
  );

  exec.stdout.on('data',
    function (data)  {
      logger.debug(who, data.toString());
    }
  );

  exec.stderr.on('data',
    function (data)  {
      logger.error(who, data.toString());
    }
  );

  exec.on('close',
    function (exitCode)  {
      if (exitCode > 0)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Script returned code: ' + exitCode)
        );
      return next();
    }
  );
}

function _delete(bag, next) {
  var who = bag.who + '|' + _delete.name;
  logger.verbose(who, 'Inside');

  bag.serviceConfig.isEnabled = bag.reqBody.isEnabled || false;
  bag.services[bag.name] = bag.serviceConfig;

  configHandler.put('services', bag.services,
    function (err) {
      if (err)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Failed to update services config', err)
        );

      bag.resBody = bag.serviceConfig;
      return next();
    }
  );
}

//local function to apply vars to template
function __applyTemplate(filePath, dataObj) {
  var fileContent = fs.readFileSync(filePath).toString();
  var template = _.template(fileContent);

  return template({obj: dataObj});
}
<commit_msg>fixing function names<commit_after>'use strict';

var self = put;
module.exports = self;

var async = require('async');
var path = require('path');
var _ = require('underscore');
var spawn = require('child_process').spawn;
var fs = require('fs');

var APIAdapter = require('../../common/APIAdapter.js');
var configHandler = require('../../common/configHandler.js');

function put(req, res) {
  var bag = {
    inputParams: req.params,
    reqBody: req.body,
    resBody: {},
    apiAdapter: new APIAdapter(req.headers.authorization.split(' ')[1]),
    params: {},
    tmpScript: '/tmp/update_service.sh',
    isSwarmClusterInitialized: false
  };

  bag.who = util.format('services|%s', self.name);
  logger.info(bag.who, 'Starting');

  async.series([
      _checkInputParams.bind(null, bag),
      _getServiceConfig.bind(null, bag),
      _getMaster.bind(null, bag),
      _getWorkers.bind(null, bag),
      _generateEnvs.bind(null, bag),
      _generateScript.bind(null, bag),
      _writeScriptToFile.bind(null, bag),
      _updateService.bind(null, bag),
      _put.bind(null, bag),
    ],
    function (err) {
      logger.info(bag.who, 'Completed');
      if (err)
        return respondWithError(res, err);

      sendJSONResponse(res, bag.resBody);
    }
  );
}

function _checkInputParams(bag, next) {
  var who = bag.who + '|' + _checkInputParams.name;
  logger.verbose(who, 'Inside');

  if (!bag.inputParams.serviceName)
    return next(
      new ActErr(who, ActErr.ParamNotFound,
        'Data not found :serviceName')
    );
  bag.name = bag.inputParams.serviceName;

  if (!bag.reqBody)
    return next(
      new ActErr(who, ActErr.BodyNotFound, 'Missing body')
    );

  if (!_.isString(bag.reqBody.replicas) && !_.isNumber(bag.reqBody.replicas))
    return next(
      new ActErr(who, ActErr.ParamNotFound,
        'Data not found :replicas')
    );

  bag.replicas = bag.reqBody.replicas;

  return next();
}

function _getServiceConfig(bag, next) {
  var who = bag.who + '|' + _getServiceConfig.name;
  logger.verbose(who, 'Inside');

  configHandler.get('services',
    function (err, config) {
      if (err)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Failed to get services', err)
        );

      if (_.isEmpty(config))
        return next(
          new ActErr(who, ActErr.DataNotFound, 'No service configs present'));

      if (!_.has(config, bag.name))
        return next(
          new ActErr(who, ActErr.DataNotFound, 'No service configs present ' +
          ' for :' + bag.name)
        );

      bag.services = config;
      bag.serviceConfig = config[bag.name];
      return next();
    }
  );
}

function _getMaster(bag, next) {
  var who = bag.who + '|' + _getMaster.name;
  logger.verbose(who, 'Inside');

  configHandler.get('master',
    function (err, master) {
      if (err)
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'Failed to get master', err)
        );

      if (_.isEmpty(master))
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'No configuration in database for master')
        );

      bag.master = master;
      return next();
    }
  );
}

function _getWorkers(bag, next) {
  var who = bag.who + '|' + _getWorkers.name;
  logger.verbose(who, 'Inside');

  configHandler.get('workers',
    function (err, workers) {
      if (err)
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'Failed to get ' + bag.component, err)
        );

      if (_.isEmpty(workers))
        return next(
          new ActErr(who, ActErr.DataNotFound,
            'No configuration in database for ' + bag.component)
        );
      // if even one worker has external IP then swarm cluster is initialized
      bag.isSwarmClusterInitialized = _.some(workers,
        function (worker) {
          return worker.address !== bag.master.address;
        }
      );
      return next();
    }
  );
}

function _generateEnvs(bag, next) {
  var who = bag.who + '|' + _generateEnvs.name;
  logger.verbose(who, 'Inside');

  bag.scriptEnvs = {
    'RUNTIME_DIR': global.config.runtimeDir,
    'SCRIPTS_DIR': global.config.scriptsDir,
    'SERVICE_NAME': bag.serviceConfig.serviceName,
    'SERVICE_IMAGE': bag.serviceConfig.image,
    'IS_SWARM_INITIALIZED': bag.isSwarmClusterInitialized,
    'REPLICAS': bag.replicas
  };

  return next();
}

function _generateScript(bag, next) {
  var who = bag.who + '|' + _generateScript.name;
  logger.verbose(who, 'Inside');

  var script = '';
  //attach header
  var filePath = path.join(global.config.scriptsDir, '/lib/_logger.sh');
  script = script.concat(__applyTemplate(filePath, bag.params));

  filePath = path.join(global.config.scriptsDir, 'update_service.sh');
  script = script.concat(__applyTemplate(filePath, bag.params));

  bag.script = script;
  return next();
}

function _writeScriptToFile(bag, next) {
  var who = bag.who + '|' + _writeScriptToFile.name;
  logger.debug(who, 'Inside');

  fs.writeFile(bag.tmpScript,
    bag.script,
    function (err) {
      if (err) {
        var msg = util.format('%s, Failed with err:%s', who, err);
        return next(
          new ActErr(
            who, ActErr.OperationFailed, msg)
        );
      }
      fs.chmodSync(bag.tmpScript, '755');
      return next();
    }
  );
}

function _updateService(bag, next) {
  var who = bag.who + '|' + _updateService.name;
  logger.verbose(who, 'Inside');

  var exec = spawn('/bin/bash',
    ['-c', bag.script],
    {
      env: bag.scriptEnvs
    }
  );

  exec.stdout.on('data',
    function (data)  {
      logger.debug(who, data.toString());
    }
  );

  exec.stderr.on('data',
    function (data)  {
      logger.error(who, data.toString());
    }
  );

  exec.on('close',
    function (exitCode)  {
      if (exitCode > 0)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Script returned code: ' + exitCode)
        );
      return next();
    }
  );
}

function _put(bag, next) {
  var who = bag.who + '|' + _put.name;
  logger.verbose(who, 'Inside');

  bag.serviceConfig.replicas = bag.replicas;
  bag.services[bag.name] = bag.serviceConfig;

  configHandler.put('services', bag.services,
    function (err) {
      if (err)
        return next(
          new ActErr(who, ActErr.OperationFailed,
            'Failed to update services config', err)
        );

      bag.resBody = bag.serviceConfig;
      return next();
    }
  );
}

//local function to apply vars to template
function __applyTemplate(filePath, dataObj) {
  var fileContent = fs.readFileSync(filePath).toString();
  var template = _.template(fileContent);

  return template({obj: dataObj});
}
<|endoftext|>


<commit_before>'use strict';

const events = require('events')
const cronParser = require('cron-parser')
const CronDate = require('cron-parser/lib/date')
const sorted = require('sorted-array-functions')

const { scheduleNextRecurrence, scheduleInvocation, cancelInvocation, RecurrenceRule, sorter, Invocation } = require('./Invocation')
const { isValidDate } = require('./utils/dateUtils')

const scheduledJobs = {};

let anonJobCounter = 0;
function resolveAnonJobName() {
  if (anonJobCounter === Number.MAX_SAFE_INTEGER) {
    anonJobCounter = 0
  }
  anonJobCounter++

  return `<Anonymous Job ${anonJobCounter}>`
}

function Job(name, job, callback) {
  // setup a private pendingInvocations variable
  this.pendingInvocations = [];

  //setup a private number of invocations variable
  let triggeredJobs = 0;

  // Set scope vars
  const jobName = name && typeof name === 'string' ? name : resolveAnonJobName();
  this.job = name && typeof name === 'function' ? name : job;

  // Make sure callback is actually a callback
  if (this.job === name) {
    // Name wasn't provided and maybe a callback is there
    this.callback = typeof job === 'function' ? job : false;
  } else {
    // Name was provided, and maybe a callback is there
    this.callback = typeof callback === 'function' ? callback : false;
  }

  // Check for generator
  if (typeof this.job === 'function' &&
    this.job.prototype &&
    this.job.prototype.next) {
    this.job = function() {
      return this.next().value;
    }.bind(this.job.call(this));
  }

  // define properties
  Object.defineProperty(this, 'name', {
    value: jobName,
    writable: false,
    enumerable: true
  });

  // method that require private access
  this.trackInvocation = function(invocation) {
    // add to our invocation list
    sorted.add(this.pendingInvocations, invocation, sorter);
    return true;
  };
  this.stopTrackingInvocation = function(invocation) {
    const invIdx = this.pendingInvocations.indexOf(invocation);
    if (invIdx > -1) {
      this.pendingInvocations.splice(invIdx, 1);
      return true;
    }

    return false;
  };
  this.triggeredJobs = function() {
    return triggeredJobs;
  };
  this.setTriggeredJobs = function(triggeredJob) {
    triggeredJobs = triggeredJob;
  };
  this.deleteFromSchedule = function() {
    deleteScheduledJob(this.name)
  };
  this.cancel = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : false;

    let inv, newInv;
    const newInvs = [];
    for (let j = 0; j < this.pendingInvocations.length; j++) {
      inv = this.pendingInvocations[j];

      cancelInvocation(inv);

      if (reschedule && (inv.recurrenceRule.recurs || inv.recurrenceRule.next)) {
        newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
        if (newInv !== null) {
          newInvs.push(newInv);
        }
      }
    }

    this.pendingInvocations = [];

    for (let k = 0; k < newInvs.length; k++) {
      this.trackInvocation(newInvs[k]);
    }

    // remove from scheduledJobs if reschedule === false
    if (!reschedule) {
      this.deleteFromSchedule()
    }

    return true;
  };
  this.cancelNext = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : true;

    if (!this.pendingInvocations.length) {
      return false;
    }

    let newInv;
    const nextInv = this.pendingInvocations.shift();

    cancelInvocation(nextInv);

    if (reschedule && (nextInv.recurrenceRule.recurs || nextInv.recurrenceRule.next)) {
      newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
      if (newInv !== null) {
        this.trackInvocation(newInv);
      }
    }

    return true;
  };
  this.reschedule = function(spec) {
    let inv;
    const invocationsToCancel = this.pendingInvocations.slice();

    for (let j = 0; j < invocationsToCancel.length; j++) {
      inv = invocationsToCancel[j];

      cancelInvocation(inv);
    }

    this.pendingInvocations = [];

    if (this.schedule(spec)) {
      this.setTriggeredJobs(0);
      return true;
    } else {
      this.pendingInvocations = invocationsToCancel;
      return false;
    }
  };
  this.nextInvocation = function() {
    if (!this.pendingInvocations.length) {
      return null;
    }
    return this.pendingInvocations[0].fireDate;
  };
}

Object.setPrototypeOf(Job.prototype, events.EventEmitter.prototype);

Job.prototype.invoke = function(fireDate) {
  this.setTriggeredJobs(this.triggeredJobs() + 1);
  return this.job(fireDate);
};

Job.prototype.runOnDate = function(date) {
  return this.schedule(date);
};

Job.prototype.schedule = function(spec) {
  const self = this;
  let success = false;
  let inv;
  let start;
  let end;
  let tz;

  // save passed-in value before 'spec' is replaced
  if (typeof spec === 'object' && 'tz' in spec) {
    tz = spec.tz;
  }

  if (typeof spec === 'object' && spec.rule) {
    start = spec.start || undefined;
    end = spec.end || undefined;
    spec = spec.rule;

    if (start) {
      if (!(start instanceof Date)) {
        start = new Date(start);
      }

      start = new CronDate(start, tz);
      if (!isValidDate(start) || start.getTime() < Date.now()) {
        start = undefined;
      }
    }

    if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
      end = undefined;
    }

    if (end) {
      end = new CronDate(end, tz);
    }
  }

  try {
    const res = cronParser.parseExpression(spec, {currentDate: start, tz: tz});
    inv = scheduleNextRecurrence(res, self, start, end);
    if (inv !== null) {
      success = self.trackInvocation(inv);
    }
  } catch (err) {
    const type = typeof spec;
    if ((type === 'string') || (type === 'number')) {
      spec = new Date(spec);
    }

    if ((spec instanceof Date) && (isValidDate(spec))) {
      spec = new CronDate(spec);
      self.isOneTimeJob = true;
      if (spec.getTime() >= Date.now()) {
        inv = new Invocation(self, spec);
        scheduleInvocation(inv);
        success = self.trackInvocation(inv);
      }
    } else if (type === 'object') {
      self.isOneTimeJob = false;
      if (!(spec instanceof RecurrenceRule)) {
        const r = new RecurrenceRule();
        if ('year' in spec) {
          r.year = spec.year;
        }
        if ('month' in spec) {
          r.month = spec.month;
        }
        if ('date' in spec) {
          r.date = spec.date;
        }
        if ('dayOfWeek' in spec) {
          r.dayOfWeek = spec.dayOfWeek;
        }
        if ('hour' in spec) {
          r.hour = spec.hour;
        }
        if ('minute' in spec) {
          r.minute = spec.minute;
        }
        if ('second' in spec) {
          r.second = spec.second;
        }

        spec = r;
      }

      spec.tz = tz;
      inv = scheduleNextRecurrence(spec, self, start, end);
      if (inv !== null) {
        success = self.trackInvocation(inv);
      }
    }
  }

  scheduledJobs[this.name] = this;
  return success;
};

function deleteScheduledJob(name) {
  if (name) {
    delete scheduledJobs[name];
  }
}

module.exports = {
  Job,
  deleteScheduledJob,
  scheduledJobs
}
<commit_msg>Ensure that anonymous job id collision is pretty much impossible (#582)<commit_after>'use strict';

const events = require('events')
const cronParser = require('cron-parser')
const CronDate = require('cron-parser/lib/date')
const sorted = require('sorted-array-functions')

const { scheduleNextRecurrence, scheduleInvocation, cancelInvocation, RecurrenceRule, sorter, Invocation } = require('./Invocation')
const { isValidDate } = require('./utils/dateUtils')

const scheduledJobs = {};

let anonJobCounter = 0;
function resolveAnonJobName() {
  const now = new Date()
  if (anonJobCounter === Number.MAX_SAFE_INTEGER) {
    anonJobCounter = 0
  }
  anonJobCounter++

  return `<Anonymous Job ${anonJobCounter} ${now.toISOString()}>`
}

function Job(name, job, callback) {
  // setup a private pendingInvocations variable
  this.pendingInvocations = [];

  //setup a private number of invocations variable
  let triggeredJobs = 0;

  // Set scope vars
  const jobName = name && typeof name === 'string' ? name : resolveAnonJobName();
  this.job = name && typeof name === 'function' ? name : job;

  // Make sure callback is actually a callback
  if (this.job === name) {
    // Name wasn't provided and maybe a callback is there
    this.callback = typeof job === 'function' ? job : false;
  } else {
    // Name was provided, and maybe a callback is there
    this.callback = typeof callback === 'function' ? callback : false;
  }

  // Check for generator
  if (typeof this.job === 'function' &&
    this.job.prototype &&
    this.job.prototype.next) {
    this.job = function() {
      return this.next().value;
    }.bind(this.job.call(this));
  }

  // define properties
  Object.defineProperty(this, 'name', {
    value: jobName,
    writable: false,
    enumerable: true
  });

  // method that require private access
  this.trackInvocation = function(invocation) {
    // add to our invocation list
    sorted.add(this.pendingInvocations, invocation, sorter);
    return true;
  };
  this.stopTrackingInvocation = function(invocation) {
    const invIdx = this.pendingInvocations.indexOf(invocation);
    if (invIdx > -1) {
      this.pendingInvocations.splice(invIdx, 1);
      return true;
    }

    return false;
  };
  this.triggeredJobs = function() {
    return triggeredJobs;
  };
  this.setTriggeredJobs = function(triggeredJob) {
    triggeredJobs = triggeredJob;
  };
  this.deleteFromSchedule = function() {
    deleteScheduledJob(this.name)
  };
  this.cancel = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : false;

    let inv, newInv;
    const newInvs = [];
    for (let j = 0; j < this.pendingInvocations.length; j++) {
      inv = this.pendingInvocations[j];

      cancelInvocation(inv);

      if (reschedule && (inv.recurrenceRule.recurs || inv.recurrenceRule.next)) {
        newInv = scheduleNextRecurrence(inv.recurrenceRule, this, inv.fireDate, inv.endDate);
        if (newInv !== null) {
          newInvs.push(newInv);
        }
      }
    }

    this.pendingInvocations = [];

    for (let k = 0; k < newInvs.length; k++) {
      this.trackInvocation(newInvs[k]);
    }

    // remove from scheduledJobs if reschedule === false
    if (!reschedule) {
      this.deleteFromSchedule()
    }

    return true;
  };
  this.cancelNext = function(reschedule) {
    reschedule = (typeof reschedule == 'boolean') ? reschedule : true;

    if (!this.pendingInvocations.length) {
      return false;
    }

    let newInv;
    const nextInv = this.pendingInvocations.shift();

    cancelInvocation(nextInv);

    if (reschedule && (nextInv.recurrenceRule.recurs || nextInv.recurrenceRule.next)) {
      newInv = scheduleNextRecurrence(nextInv.recurrenceRule, this, nextInv.fireDate, nextInv.endDate);
      if (newInv !== null) {
        this.trackInvocation(newInv);
      }
    }

    return true;
  };
  this.reschedule = function(spec) {
    let inv;
    const invocationsToCancel = this.pendingInvocations.slice();

    for (let j = 0; j < invocationsToCancel.length; j++) {
      inv = invocationsToCancel[j];

      cancelInvocation(inv);
    }

    this.pendingInvocations = [];

    if (this.schedule(spec)) {
      this.setTriggeredJobs(0);
      return true;
    } else {
      this.pendingInvocations = invocationsToCancel;
      return false;
    }
  };
  this.nextInvocation = function() {
    if (!this.pendingInvocations.length) {
      return null;
    }
    return this.pendingInvocations[0].fireDate;
  };
}

Object.setPrototypeOf(Job.prototype, events.EventEmitter.prototype);

Job.prototype.invoke = function(fireDate) {
  this.setTriggeredJobs(this.triggeredJobs() + 1);
  return this.job(fireDate);
};

Job.prototype.runOnDate = function(date) {
  return this.schedule(date);
};

Job.prototype.schedule = function(spec) {
  const self = this;
  let success = false;
  let inv;
  let start;
  let end;
  let tz;

  // save passed-in value before 'spec' is replaced
  if (typeof spec === 'object' && 'tz' in spec) {
    tz = spec.tz;
  }

  if (typeof spec === 'object' && spec.rule) {
    start = spec.start || undefined;
    end = spec.end || undefined;
    spec = spec.rule;

    if (start) {
      if (!(start instanceof Date)) {
        start = new Date(start);
      }

      start = new CronDate(start, tz);
      if (!isValidDate(start) || start.getTime() < Date.now()) {
        start = undefined;
      }
    }

    if (end && !(end instanceof Date) && !isValidDate(end = new Date(end))) {
      end = undefined;
    }

    if (end) {
      end = new CronDate(end, tz);
    }
  }

  try {
    const res = cronParser.parseExpression(spec, {currentDate: start, tz: tz});
    inv = scheduleNextRecurrence(res, self, start, end);
    if (inv !== null) {
      success = self.trackInvocation(inv);
    }
  } catch (err) {
    const type = typeof spec;
    if ((type === 'string') || (type === 'number')) {
      spec = new Date(spec);
    }

    if ((spec instanceof Date) && (isValidDate(spec))) {
      spec = new CronDate(spec);
      self.isOneTimeJob = true;
      if (spec.getTime() >= Date.now()) {
        inv = new Invocation(self, spec);
        scheduleInvocation(inv);
        success = self.trackInvocation(inv);
      }
    } else if (type === 'object') {
      self.isOneTimeJob = false;
      if (!(spec instanceof RecurrenceRule)) {
        const r = new RecurrenceRule();
        if ('year' in spec) {
          r.year = spec.year;
        }
        if ('month' in spec) {
          r.month = spec.month;
        }
        if ('date' in spec) {
          r.date = spec.date;
        }
        if ('dayOfWeek' in spec) {
          r.dayOfWeek = spec.dayOfWeek;
        }
        if ('hour' in spec) {
          r.hour = spec.hour;
        }
        if ('minute' in spec) {
          r.minute = spec.minute;
        }
        if ('second' in spec) {
          r.second = spec.second;
        }

        spec = r;
      }

      spec.tz = tz;
      inv = scheduleNextRecurrence(spec, self, start, end);
      if (inv !== null) {
        success = self.trackInvocation(inv);
      }
    }
  }

  scheduledJobs[this.name] = this;
  return success;
};

function deleteScheduledJob(name) {
  if (name) {
    delete scheduledJobs[name];
  }
}

module.exports = {
  Job,
  deleteScheduledJob,
  scheduledJobs
}
<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 * @ignore
 */
const PathesConfiguration = require('../../model/configuration/PathesConfiguration.js').PathesConfiguration;
const BuildConfiguration = require('../../model/configuration/BuildConfiguration.js').BuildConfiguration;
const SassRepository = require('../model/SassRepository.js').SassRepository;
const SitesRepository = require('../../model/site/SitesRepository.js').SitesRepository;
const Site = require('../../model/site/Site.js').Site;
const Context = require('../../application/Context.js').Context;
const CliLogger = require('../../cli/CliLogger.js').CliLogger;
const BaseMap = require('../../base/BaseMap.js').BaseMap;
const ModelSynchronizer = require('../../watch/ModelSynchronizer.js').ModelSynchronizer;
const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const postcss = require('gulp-postcss');
const flexibility = require('postcss-flexibility');
const mqpacker = require('css-mqpacker');
const cssnano = require('cssnano');
const cssnext = require('postcss-cssnext');
const discardComments = require('postcss-discard-comments');
const urlrewrite = require('postcss-urlrewrite');
const cli = require('../plugin/cli.js');
const synchronize = require('../../utils/synchronize.js');
const doiuse = require('doiuse');
const sass = require('node-sass');
const through2 = require('through2');
const co = require('co');
const Stream = require('stream');
const File = require('vinyl');

/**
 * Gets all compilable scss files for given site query.
 * If query is falsy or "all" then all sites will be generated.
 *
 * @param {String} query
 * @param {CliLogger} cliLogger
 */
function src(query, cliLogger)
{
    const stream = through2({ objectMode: true });
    const context = Context.instance;
    if (!cliLogger)
    {
        cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    }
    co(function *()
    {
        const sitesRepository = context.di.create(SitesRepository);
        let sites = [];
        if (typeof query == 'string' && query !== '*')
        {
            const site = yield sitesRepository.findBy(Site.ANY, query);
            sites.push(site);
        }
        else
        {
            sites = yield sitesRepository.getItems();
        }

        const sassRepository = context.di.create(SassRepository);
        for (const site of sites)
        {
            const files = yield sassRepository.getBySite(site);
            for (const file in files)
            {
                stream.write(files[file]);
            }
        }

        return true;
    })
    .catch(function(e)
    {
        cliLogger.error(e);
        stream.end();
    })
    .then(function()
    {
        stream.end();
    });

    return stream;
}

/**
 * Compiles a sass stream
 *
 * @param {CliLogger} cliLogger
 */
function compileSass(cliLogger)
{
    // Context & Logger
    const context = Context.instance;
    if (!cliLogger)
    {
        cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    }

    // Prepare
    const pathes = context.di.create(PathesConfiguration);
    const includePathes = [pathes.sites];
    if (pathes.bower)
    {
        includePathes.push(pathes.bower);
    }

    // Render stream
    const stream = new Stream.Transform({ objectMode: true });
    let work;
    stream._transform = function (file, encoding, callback)
    {
        if (!file.isNull)
        {
            callback();
            return;
        }

        work = cliLogger.work('Compiling file <' + file.path + '>');

        // Prepare
        const compiledFile = new File({ path: file.path.replace(/\.scss/, '.css'), contents: false });
        const options =
        {
            data: file.contents.toString(),
            includePaths: includePathes,
            outputStyle: 'expanded'
        };

        // Render
        let compiled = false;
        try
        {
            compiled = sass.renderSync(options);
        }
        catch(error)
        {
            compiled = false;
            cliLogger.error('Error compiling Sass');
            cliLogger.error(error.message);
            cliLogger.error(error.file + '@' + error.line);
        }
        if (compiled)
        {
            compiledFile.contents = new Buffer(compiled.css);
            this.push(compiledFile);
            cliLogger.end(work);
        }
        else
        {
            cliLogger.end(work, true);
        }

        callback();
    };

    return stream;
}

/**
 * Compiles sass files for sites
 *
 * @param {String} query
 * @param {CliLogger} cliLogger
 */
function compile(query, streamFiles)
{
    // Context & Logger
    const context = Context.instance;
    const cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    const section = cliLogger.section('Compiling sass');

    // Prepare
    const buildConfiguration = context.di.create(BuildConfiguration);
    const pathesConfiguration = context.di.create(PathesConfiguration);
    const basePath = synchronize.execute(pathesConfiguration, 'resolveCache', ['/css']);
    const browsers = buildConfiguration.get('sass.browsers', ['ie >= 9', '> 2%']);
    const sourceMaps = buildConfiguration.get('sass.sourceMaps', true);
    const comments = buildConfiguration.get('sass.comments', true);
    const optimize = buildConfiguration.get('sass.optimize', false);
    const minimize = buildConfiguration.get('sass.minimize', false);
    const check = buildConfiguration.get('sass.check', false);
    const urlRewrite = buildConfiguration.get('sass.urlRewrite', false);

    // Show config
    cliLogger.options(
        {
            environment: buildConfiguration.environment,
            query: query,
            basePath: basePath,
            sourceMaps: sourceMaps,
            comments: comments,
            urlRewrite: urlRewrite,
            optimize: optimize,
            minimize: minimize,
            check: check
        });

    // Prepare postcss
    const postcssProcessors = [];
    try
    {
        if (urlRewrite !== false)
        {
            postcssProcessors.push(urlrewrite(
                {
                    properties: ['background', 'src'],
                    rules: urlRewrite
                }));
        }
        if (comments === false)
        {
            postcssProcessors.push(discardComments());
        }
        if (check === true)
        {
            postcssProcessors.push(doiuse(
                {
                    browsers: browsers,
                    noWarnings: true,
                    onFeatureUsage: function(usageInfo)
                    {
                        cliLogger.error(usageInfo.message);
                    }
                }));
        }
        postcssProcessors.push(cssnext({ browsers: browsers }));
        postcssProcessors.push(flexibility);
        if (optimize === true)
        {
            postcssProcessors.push(mqpacker());
        }
        if (minimize === true)
        {
            postcssProcessors.push(cssnano());
        }
    }
    catch(e)
    {
        cliLogger.error(e);
    }

    // Create task
    let stream = src(query, cliLogger);
    stream = stream.pipe(compileSass(cliLogger));
    if (sourceMaps === true)
    {
        stream = stream.pipe(sourcemaps.init());
    }
    stream = stream.pipe(cli.work(cliLogger, 'Postprocessing <%s>'));
    stream = stream.pipe(postcss(postcssProcessors));
    if (sourceMaps === true)
    {
        stream = stream.pipe(sourcemaps.write());
    }
    stream = stream.pipe(cli.end(cliLogger));
    if (!streamFiles)
    {
        stream = stream.pipe(gulp.dest(basePath));
    }

    // Listen for end
    stream.on('end', function()
    {
        cliLogger.end(section);
    });

    return stream;
}

/**
 * Watches for changes and compiles sass files
 */
function watch()
{
    // Context & Logger
    const context = Context.instance;
    const logger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    logger.section('Watching sass files');

    // Prepare
    const pathes = context.di.create(PathesConfiguration);
    const basePath = pathes.sites;
    const synchronizer = context.di.create(ModelSynchronizer);
    logger.options(
        {
            basePath: basePath
        });

    // Run
    synchronizer.signals.invalidated.add(function(synchronizer, invalidations)
    {
        if (!invalidations.extensions.length || invalidations.extensions.indexOf('.scss') > -1)
        {
            gulp.parallel(['sass.compile'])(function(error)
            {
            });
        }
    });
    synchronizer.start();
    gulp.parallel(['sass.compile'])(function(error)
    {
    });
}

/**
 * Register Tasks
 */
gulp.task('sass.compile', (cb) => compile(false, false, cb));
gulp.task('sass.watch', watch);

/**
 * Public api
 */
module.exports =
{
    compile: compile,
    compileTask: 'sass.compile',
    watch: watch,
    watchTask: 'sass.watch'
};
<commit_msg>Removed flexibility<commit_after>'use strict';

/**
 * Requirements
 * @ignore
 */
const PathesConfiguration = require('../../model/configuration/PathesConfiguration.js').PathesConfiguration;
const BuildConfiguration = require('../../model/configuration/BuildConfiguration.js').BuildConfiguration;
const SassRepository = require('../model/SassRepository.js').SassRepository;
const SitesRepository = require('../../model/site/SitesRepository.js').SitesRepository;
const Site = require('../../model/site/Site.js').Site;
const Context = require('../../application/Context.js').Context;
const CliLogger = require('../../cli/CliLogger.js').CliLogger;
const BaseMap = require('../../base/BaseMap.js').BaseMap;
const ModelSynchronizer = require('../../watch/ModelSynchronizer.js').ModelSynchronizer;
const gulp = require('gulp');
const sourcemaps = require('gulp-sourcemaps');
const postcss = require('gulp-postcss');
const mqpacker = require('css-mqpacker');
const cssnano = require('cssnano');
const cssnext = require('postcss-cssnext');
const discardComments = require('postcss-discard-comments');
const urlrewrite = require('postcss-urlrewrite');
const cli = require('../plugin/cli.js');
const synchronize = require('../../utils/synchronize.js');
const doiuse = require('doiuse');
const sass = require('node-sass');
const through2 = require('through2');
const co = require('co');
const Stream = require('stream');
const File = require('vinyl');

/**
 * Gets all compilable scss files for given site query.
 * If query is falsy or "all" then all sites will be generated.
 *
 * @param {String} query
 * @param {CliLogger} cliLogger
 */
function src(query, cliLogger)
{
    const stream = through2({ objectMode: true });
    const context = Context.instance;
    if (!cliLogger)
    {
        cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    }
    co(function *()
    {
        const sitesRepository = context.di.create(SitesRepository);
        let sites = [];
        if (typeof query == 'string' && query !== '*')
        {
            const site = yield sitesRepository.findBy(Site.ANY, query);
            sites.push(site);
        }
        else
        {
            sites = yield sitesRepository.getItems();
        }

        const sassRepository = context.di.create(SassRepository);
        for (const site of sites)
        {
            const files = yield sassRepository.getBySite(site);
            for (const file in files)
            {
                stream.write(files[file]);
            }
        }

        return true;
    })
    .catch(function(e)
    {
        cliLogger.error(e);
        stream.end();
    })
    .then(function()
    {
        stream.end();
    });

    return stream;
}

/**
 * Compiles a sass stream
 *
 * @param {CliLogger} cliLogger
 */
function compileSass(cliLogger)
{
    // Context & Logger
    const context = Context.instance;
    if (!cliLogger)
    {
        cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    }

    // Prepare
    const pathes = context.di.create(PathesConfiguration);
    const includePathes = [pathes.sites];
    if (pathes.bower)
    {
        includePathes.push(pathes.bower);
    }

    // Render stream
    const stream = new Stream.Transform({ objectMode: true });
    let work;
    stream._transform = function (file, encoding, callback)
    {
        if (!file.isNull)
        {
            callback();
            return;
        }

        work = cliLogger.work('Compiling file <' + file.path + '>');

        // Prepare
        const compiledFile = new File({ path: file.path.replace(/\.scss/, '.css'), contents: false });
        const options =
        {
            data: file.contents.toString(),
            includePaths: includePathes,
            outputStyle: 'expanded'
        };

        // Render
        let compiled = false;
        try
        {
            compiled = sass.renderSync(options);
        }
        catch(error)
        {
            compiled = false;
            cliLogger.error('Error compiling Sass');
            cliLogger.error(error.message);
            cliLogger.error(error.file + '@' + error.line);
        }
        if (compiled)
        {
            compiledFile.contents = new Buffer(compiled.css);
            this.push(compiledFile);
            cliLogger.end(work);
        }
        else
        {
            cliLogger.end(work, true);
        }

        callback();
    };

    return stream;
}

/**
 * Compiles sass files for sites
 *
 * @param {String} query
 * @param {CliLogger} cliLogger
 */
function compile(query, streamFiles)
{
    // Context & Logger
    const context = Context.instance;
    const cliLogger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    const section = cliLogger.section('Compiling sass');

    // Prepare
    const buildConfiguration = context.di.create(BuildConfiguration);
    const pathesConfiguration = context.di.create(PathesConfiguration);
    const basePath = synchronize.execute(pathesConfiguration, 'resolveCache', ['/css']);
    const browsers = buildConfiguration.get('sass.browsers', ['ie >= 9', '> 2%']);
    const sourceMaps = buildConfiguration.get('sass.sourceMaps', true);
    const comments = buildConfiguration.get('sass.comments', true);
    const optimize = buildConfiguration.get('sass.optimize', false);
    const minimize = buildConfiguration.get('sass.minimize', false);
    const check = buildConfiguration.get('sass.check', false);
    const urlRewrite = buildConfiguration.get('sass.urlRewrite', false);

    // Show config
    cliLogger.options(
        {
            environment: buildConfiguration.environment,
            query: query,
            basePath: basePath,
            sourceMaps: sourceMaps,
            comments: comments,
            urlRewrite: urlRewrite,
            optimize: optimize,
            minimize: minimize,
            check: check
        });

    // Prepare postcss
    const postcssProcessors = [];
    try
    {
        if (urlRewrite !== false)
        {
            postcssProcessors.push(urlrewrite(
                {
                    properties: ['background', 'src'],
                    rules: urlRewrite
                }));
        }
        if (comments === false)
        {
            postcssProcessors.push(discardComments());
        }
        if (check === true)
        {
            postcssProcessors.push(doiuse(
                {
                    browsers: browsers,
                    noWarnings: true,
                    onFeatureUsage: function(usageInfo)
                    {
                        cliLogger.error(usageInfo.message);
                    }
                }));
        }
        postcssProcessors.push(cssnext({ browsers: browsers }));
        if (optimize === true)
        {
            postcssProcessors.push(mqpacker());
        }
        if (minimize === true)
        {
            postcssProcessors.push(cssnano());
        }
    }
    catch(e)
    {
        cliLogger.error(e);
    }

    // Create task
    let stream = src(query, cliLogger);
    stream = stream.pipe(compileSass(cliLogger));
    if (sourceMaps === true)
    {
        stream = stream.pipe(sourcemaps.init());
    }
    stream = stream.pipe(cli.work(cliLogger, 'Postprocessing <%s>'));
    stream = stream.pipe(postcss(postcssProcessors));
    if (sourceMaps === true)
    {
        stream = stream.pipe(sourcemaps.write());
    }
    stream = stream.pipe(cli.end(cliLogger));
    if (!streamFiles)
    {
        stream = stream.pipe(gulp.dest(basePath));
    }

    // Listen for end
    stream.on('end', function()
    {
        cliLogger.end(section);
    });

    return stream;
}

/**
 * Watches for changes and compiles sass files
 */
function watch()
{
    // Context & Logger
    const context = Context.instance;
    const logger = context.di.create(CliLogger, new BaseMap({ 'cli/CliLogger.prefix' : 'gulp.sass' }));
    logger.section('Watching sass files');

    // Prepare
    const pathes = context.di.create(PathesConfiguration);
    const basePath = pathes.sites;
    const synchronizer = context.di.create(ModelSynchronizer);
    logger.options(
        {
            basePath: basePath
        });

    // Run
    synchronizer.signals.invalidated.add(function(synchronizer, invalidations)
    {
        if (!invalidations.extensions.length || invalidations.extensions.indexOf('.scss') > -1)
        {
            gulp.parallel(['sass.compile'])(function(error)
            {
            });
        }
    });
    synchronizer.start();
    gulp.parallel(['sass.compile'])(function(error)
    {
    });
}

/**
 * Register Tasks
 */
gulp.task('sass.compile', (cb) => compile(false, false, cb));
gulp.task('sass.watch', watch);

/**
 * Public api
 */
module.exports =
{
    compile: compile,
    compileTask: 'sass.compile',
    watch: watch,
    watchTask: 'sass.watch'
};
<|endoftext|>


<commit_before>import $ from 'jquery';
import noop from '../common/noop';
import { CursorUp, CursorDown, Escape, Enter, Control } from '../common/keys';

export const CHANNEL_FOCUS = 'focus';
export const CHANNEL_FILTER = 'change';
export const CHANNEL_NEXT = 'activate_next_item';
export const CHANNEL_PREVIOUS = 'activate_previous_item';
export const CHANNEL_SELECT_ACTIVE = 'select_active_item';
export const CHANNEL_CANCEL = 'cancel_selection';
export const CHANNEL_TOGGLE_EXTENDED_MODE = 'toggle_extended_mode';

function SearchInput(options) {
  this.options = options;
  this.channels = {
    [CHANNEL_FOCUS]: noop,
    [CHANNEL_FILTER]: noop,
    [CHANNEL_NEXT]: noop,
    [CHANNEL_PREVIOUS]: noop,
    [CHANNEL_SELECT_ACTIVE]: noop,
    [CHANNEL_CANCEL]: noop,
    [CHANNEL_TOGGLE_EXTENDED_MODE]: noop,
  };
  this.renderedState = {};
  this.init();
}

SearchInput.prototype.on = function on(channel, callback) {
  this.channels[channel] = callback ? callback : noop;
  return this;
};

SearchInput.prototype.init = function init() {
  const keydownHandlers = {
    [CursorUp]: CHANNEL_PREVIOUS,
    [CursorDown]: CHANNEL_NEXT,
    [Enter]: CHANNEL_SELECT_ACTIVE,
    [Escape]: CHANNEL_CANCEL,
    [Control]: CHANNEL_TOGGLE_EXTENDED_MODE,
  };

  this.element = this.$searchInputElement = $('<input type="text"/>')
    .addClass('ui-virtual-select--search-input')
    .on('focus', () => {
      this.channels[CHANNEL_FOCUS]();
    })
    .on('keydown', event => {
      const key = event.which;
      const channel = keydownHandlers[key];
      if (channel) {
        this.channels[channel]();
      }
    })
    .on('blur', () => {
      this.channels[CHANNEL_CANCEL]();
    })
    .on('keyup', event => {
      const query = $(event.target).val();
      if (query !== this.renderedState.query) {
        this.channels[CHANNEL_FILTER](query);
      }
    });
};

SearchInput.prototype.render = function render(state) {
  // update placeholder
  const dataProvider = this.options.dataProvider;
  const displayText = state.selectedItem ?
    dataProvider.displayText(state.selectedItem) :
    dataProvider.noSelectionText();
  if (displayText !== this.$searchInputElement.attr('placeholder')) {
    console.debug(`updating placeholder: '${displayText}'`);
    this.$searchInputElement.attr('placeholder', displayText);
  }

  // update value
  if (state.query !== this.$searchInputElement.val()) {
    console.debug(`updating query: '${state.query}'`);
    this.$searchInputElement.val(state.query);
  }

  if (this.$searchInputElement.is(':focus') && !state.open && this.renderedState.open) {
    console.debug(`blurring search input`);
    this.$searchInputElement.trigger('blur');
  }

  // FIXME: Not sure why I need the copy here
  this.renderedState = $.extend({}, state);
};

SearchInput.prototype.focus = function focus() {
  this.$searchInputElement.focus();
};

export default SearchInput;
<commit_msg>Reduced reliance on the placeholder attribute<commit_after>import $ from 'jquery';
import noop from '../common/noop';
import { CursorUp, CursorDown, Escape, Enter, Control } from '../common/keys';

export const CHANNEL_FOCUS = 'focus';
export const CHANNEL_FILTER = 'change';
export const CHANNEL_NEXT = 'activate_next_item';
export const CHANNEL_PREVIOUS = 'activate_previous_item';
export const CHANNEL_SELECT_ACTIVE = 'select_active_item';
export const CHANNEL_CANCEL = 'cancel_selection';
export const CHANNEL_TOGGLE_EXTENDED_MODE = 'toggle_extended_mode';

function SearchInput(options) {
  this.options = options;
  this.channels = {
    [CHANNEL_FOCUS]: noop,
    [CHANNEL_FILTER]: noop,
    [CHANNEL_NEXT]: noop,
    [CHANNEL_PREVIOUS]: noop,
    [CHANNEL_SELECT_ACTIVE]: noop,
    [CHANNEL_CANCEL]: noop,
    [CHANNEL_TOGGLE_EXTENDED_MODE]: noop,
  };
  this.renderedState = {};
  this.init();
}

SearchInput.prototype.on = function on(channel, callback) {
  this.channels[channel] = callback ? callback : noop;
  return this;
};

SearchInput.prototype.init = function init() {
  const keydownHandlers = {
    [CursorUp]: CHANNEL_PREVIOUS,
    [CursorDown]: CHANNEL_NEXT,
    [Enter]: CHANNEL_SELECT_ACTIVE,
    [Escape]: CHANNEL_CANCEL,
    [Control]: CHANNEL_TOGGLE_EXTENDED_MODE,
  };

  this.element = this.$searchInputElement = $('<input type="text"/>')
    .addClass('ui-virtual-select--search-input')
    .on('focus', () => {
      this.channels[CHANNEL_FOCUS]();
    })
    .on('keydown', event => {
      const key = event.which;
      const channel = keydownHandlers[key];
      if (channel) {
        this.channels[channel]();
      }
    })
    .on('blur', () => {
      this.channels[CHANNEL_CANCEL]();
    })
    .on('keyup', event => {
      const query = $(event.target).val();
      if (query !== this.renderedState.query) {
        this.channels[CHANNEL_FILTER](query);
      }
    });
};

SearchInput.prototype.render = function render(state) {
  // update placeholder
  const dataProvider = this.options.dataProvider;
  const displayText = state.selectedItem ?
    dataProvider.displayText(state.selectedItem) :
    dataProvider.noSelectionText();
  if (displayText !== this.$searchInputElement.attr('placeholder')) {
    console.debug(`updating placeholder: '${displayText}'`);
    this.$searchInputElement.attr('placeholder', displayText);
    this.$searchInputElement.val(displayText);
  }

  // update value
  const query = state.open ? state.query : displayText;
  if (query !== this.$searchInputElement.val()) {
    console.debug(`updating query: '${query}'`);
    this.$searchInputElement.val(query);
  }

  if (this.$searchInputElement.is(':focus') && !state.open && this.renderedState.open) {
    console.debug(`blurring search input`);
    this.$searchInputElement.trigger('blur');
    this.$searchInputElement.val(displayText);
  }

  // FIXME: Not sure why I need the copy here
  this.renderedState = $.extend({}, state);
};

SearchInput.prototype.focus = function focus() {
  this.$searchInputElement.focus();
};

export default SearchInput;
<|endoftext|>


<commit_before>// Made for YogaScript by Douglas Bumby
var count, yogaCount;

count = 2;

yogaCount = function() {
  count = count + 1;
  document.getElementById("yoga-tally").innerHTML = count;
  return 0;
};
<commit_msg>Update script.js<commit_after>// Made for YogaScript by Douglas Bumby
var count = parseInt(localStorage.getItem("yogapantcount"))||0;

count = 2;

yogaCount = function() {
  count = count++;
  document.getElementById("yoga-tally").innerHTML = count;
  return 0;
};
<|endoftext|>


<commit_before>/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * TERMS OF REPRODUCTION USE
 *
 * 1. Provide a link back to the original repository (this repository), as
 * 		in, https://github.com/ConnerGDavis/Plugbot, that is well-visible
 * 		wherever the source is being reproduced.  For example, should you
 * 		display it on a website, you should provide a link above/below that
 *		which the users use, titled something such as "ORIGINAL AUTHOR".
 *
 * 2. Retain these three comments:  the GNU GPL license statement, this comment,
 * 		and that below it, that details the author and purpose.
 */

/**
 * NOTE:  This is all procedural as hell because prototypes and any
 * 			OOP techniques in Javascript are stupid and confusing.
 *
 * @author 	Conner Davis ([VIP] ♫Łŏġïç®)
 * 			Harrison Schneidman ([VIDJ] EXƎ)
 */

/**
 * Whether the user has currently enabled auto-woot.
 */
var autowoot = true;
/**
 * Whether the user has currently enabled auto-queueing.
 */
var autoqueue = false;
/**
 * Whether or not the user has enabled hiding this video.
 */
var hideVideo = false;
/**
 * Whether or not the user has enabled the userlist.
 */
var userList = true;

// TODO:  DJ battle-related.
var points = 0;
var highScore = 0;

/**
 * Initialise all of the Plug.dj API listeners which we use
 * to asynchronously intercept specific events and the data
 * attached with them.
 */
function initAPIListeners()
{
	/**
	 * This listens in for whenever a new DJ starts playing.
	 */
	API.addEventListener(API.DJ_ADVANCE, djAdvanced);

	/**
	 * This listens for whenever a user in the room either WOOT!s
	 * or Mehs the current song.
	 */
	API.addEventListener(API.VOTE_UPDATE, function(obj) {
		if (userList)
			populateUserlist();
	});

	/**
	 * Whenever a user joins, this listener is called.
	 */
	API.addEventListener(API.USER_JOIN, function(user) {
		if (userList)
			populateUserlist();
	});

	/**
	 * Called upon a user exiting the room.
	 */
	API.addEventListener(API.USER_LEAVE, function(user) {
		if (userList)
			populateUserlist();
	})
}

/**
 * Renders all of the Plug.bot "UI" that is visible beneath the video
 * player.
 */
function displayUI()
{
	/*
	 * Be sure to remove any old instance of the UI, in case the user
	 * reloads the script without refreshing the page (updating.)
	 */
	$('#plugbot-ui').remove();

	/*
	 * This is a necessary hack (of sorts) that allows the UI to actually
	 * be visible and interactive.
	 */
	$('#playback').css('z-index', '8');

	/*
	 * Generate the HTML code for the UI.
	 */
	$('#playback').append('

');
		$("#plugbot-ui").animate({"height": "64px"}, {duration: "slow" });
		$('#plugbot-ui').append(
			'' +
			'' +
			'' +
			''
		);
}

/**
 * For every button on the Plug.bot UI, we have listeners backing them
 * that are built to intercept the user's clicking each button.  Based
 * on the button that they clicked, we can execute some logic that will
 * in some way affect their experience.
 */
function initUIListeners()
{
	$("#plugbot-btn-userlist").on("click", function() {
		userList = !userList;
		$(this).attr("src", userList ? 'http://i.imgur.com/IGcMP.png' : 'http://i.imgur.com/pAFJS.png');
		$("#plugbot-userlist").css("visibility", userList ? ("visible") : ("hidden"));
		if (!userList) {
			$("#plugbot-userlist").empty();
		} else {
			populateUserlist();
		}
		console.log('Userlist is now ' + (userList ? 'enabled' : 'disabled'));
	});

	$("#plugbot-btn-woot").on("click", function() {
		autowoot = !autowoot;
		$(this).attr("src", autowoot ? 'http://i.imgur.com/fWb8n.png' : 'http://i.imgur.com/uyUtA.png');
		if (autowoot)
			$("#button-vote-positive").click();
		console.log('Auto-woot is now ' + (autowoot ? 'enabled' : 'disabled'));
	});

	$("#plugbot-btn-hidevideo").on("click", function() {
		hideVideo = !hideVideo;
		$(this).attr("src", hideVideo ? 'http://i.imgur.com/lwpfH.png' : 'http://i.imgur.com/jbJDe.png');
		$("#yt-frame").animate({"height": (hideVideo ? "0px" : "271px")}, {duration: "fast"});
	});

	$("#plugbot-btn-queue").on("click", function() {
		autoqueue = !autoqueue;
		$(this).attr("src", autoqueue ? 'http://i.imgur.com/IxK27.png' : 'http://i.imgur.com/W5ncS.png');
		$("#button-dj-waitlist-" + (autoqueue ? "join" : "leave")).click();
		console.log('Auto-queue is now ' + (autoqueue ? 'enabled' : 'disabled'));
	});
}

/**
 * Called whenever a new DJ begins playing in the room.
 *
 * @param {Object} obj
 * 				This contains the user (current DJ)'s data.
 */
function djAdvanced(obj)
{
	/*
	 * If auto-woot is enabled, WOOT! the song.
	 */
	if (autowoot)
		$("#button-vote-positive").click();

	/*
	 * If auto-queueing has been enabled, and they have just recently
	 * left the waitlist, join them back.
	 */
	if ($("#button-dj-waitlist-join").css("display") === "block" && autoqueue)
		$("#button-dj-waitlist-join").click();

	/*
	 * Reset all hide video-related matters.
	 */
	$("#yt-frame").css("height", "271px");
	$('#plugbot-btn-hidevideo').attr('src', 'http://i.imgur.com/jbJDe.png');
	hideVideo = false;

	// TODO: DJ battle-related
	points = 0;
	highScore = 0;

	/*
	 * If the userlist is enabled, re-populate it.
	 */
	if (userList)
		populateUserlist();
}

/**
 * Generates every user in the room and their current vote as
 * colour-coded text.  Also, moderators get the star next to
 * their name.
 */
function populateUserlist()
{
	/*
	 * Destroy the old userlist DIV and replace it with a fresh
	 * empty one to work with.
	 */
	$("#plugbot-userlist").remove();
	$('body').append('

');

	/*
	 * If the user is in the waitlist, show them their current spot.
	 */
	if ($('#button-dj-waitlist-view').attr('title') !== '') {
		if ($('#button-dj-waitlist-leave').css('display') === 'block' && ($.inArray(API.getDJs(), API.getSelf()) == -1)) {
			var spot = $('#button-dj-waitlist-view').attr('title').split('(')[1];
				spot = spot.substring(0, spot.indexOf(')'));
			$('#plugbot-userlist').append('
Waitlist: ' + spot + '

');
		}
	}

	// TODO:  DJ battle-related
	points = 0;

	/*
	 * An array of all of the room's users.
	 */
	var users = new Array();

	/*
	 * Populate the users array with the next user
	 * in the room (this is stored alphabetically.)
	 */
	for (user in API.getUsers())
	{
		users.push(API.getUsers()[user]);
	}

	/*
	 * For every user, call the #appendUser(username, vote) method
	 * which will display their username with any colour coding that
	 * they match.
	 */
	for (user in users)
	{
		var user = users[user];
		appendUser(user.username, user.vote)
	}

	// TODO: DJ battle-related
	if (points > highScore)
		highScore = points;
}

/**
 * Appends another user's username to the userlist.
 *
 * @param {Object} username
 * 				The username of this user.
 * @param {Object} vote
 * 				Their current 'vote', which may be:
 * 					-1 	: Meh
 *					0	: 'undecided' (hasn't voted yet)
 * 					1	: WOOT!
 */
function appendUser(username, vote)
{
	/*
	 * Some variables that we'll either be setting as true/false
	 * (some conditionals that do major changes to their look in the userlist)
	 * or setting a value to.
	 */
	var colour;
	var currentDj = false;
	var moderator = false;
	var host = username == API.getHost().username;
	var img;

	/*
	 * Loop through the room's moderators to detect a match
	 * for this user, in which case we'll prepend the mod
	 * star to their name.
	 */
	for (var i = 0; i < API.getModerators().length; i++)
	{
		if (API.getModerators()[i].username == username && !host)
		{
			moderator = true;
		}
	}

	/*
	 * Based on their vote, apply the colour coding.
	 */
	switch (vote)
	{
		case 1:		// WOOT!
			colour = "3FFF00";
			points++;
			if (moderator)
				img = "http://i.imgur.com/T5G4f.png";
			if (host)
				img = "http://i.imgur.com/Lu1qo.png";
			break;
		case 0:		// Undecided
			colour = "FFFFFF";
			if (moderator)
				img = "http://i.imgur.com/sRsU0.png";
			if (host)
				img = "http://i.imgur.com/6Bq5W.png";
			break;
		case -1:	// Meh
			colour = "ED1C24";
			if (moderator)
				img = "http://i.imgur.com/JPA1Q.png";
			if (host)
				img = "http://i.imgur.com/wVLR3.png";
			break;
	}

	/*
	 * If they're the current DJ, apply some more special
	 * changes.
	 */
	if (API.getDJs().length > 0 && API.getDJs()[0].username == username) {
		currentDj = true;
		colour = "42A5DC";
		if (moderator)
			img = "http://i.imgur.com/CsK3d.png";
	}

	/*
	 * Sometimes undecided mod star breaks.  This fixes that.
	 */
	if (img == undefined && (moderator || host))
	{
		colour = "FFFFFF";
		img = moderator ? "http://i.imgur.com/sRsU0.png" : "http://i.imgur.com/6Bq5W.png";
	}

	/*
	 * Apply the HTML code to the page that actually displays them
	 * inside the userlist.
	 */
	$('#plugbot-userlist').append(
		((moderator || host) ? '' : '')
		+ '
'
		+ username + '
'
	);
}

///////////////////////////////////////////////////////////
////////// EVERYTHING FROM HERE ON OUT IS INIT ////////////
///////////////////////////////////////////////////////////

/*
 * Clear the old code so we can properly update everything.
 */
$('#plugbot-css').remove();

/*
 * Write the CSS rules that are used for components of the
 * Plug.bot UI.
 */
$('body').prepend('


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'react-apollo'
import update from 'immutability-helper'
import { addToLocalCache, isDuplicate, ALERT_DEFAULT } from '../utils/helpers'
require('smoothscroll-polyfill').polyfill()
import Alert from 'react-s-alert'

import CREATE_QUESTION_MUTATION from '../graphql/CreateQuestion.mutation.gql'
const MAX_CHAR = 140

class AddQuestion extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      chars_left: MAX_CHAR,
    }
  }

  onSubmit(event) {
    event.preventDefault()
    if (!this.input.value || this.input.value.length === 0) {
      return
    }
    if (scrollTo) {
      // let elem = document.getElementById('app')
      // elem.scrollTop = elem.scrollHeight
      let elem = document.querySelector('#bottom')
      elem.scrollIntoView({ behavior: 'smooth' })
    }
    this.props
      .addQuestion(this.input.value, this.props.auth.userId)
      .then(() => {
        Alert.info('Question sent! ✨🚀', ALERT_DEFAULT)
        this.input.value = ''
        this.setState({
          chars_left: MAX_CHAR,
        })
      })
  }

  handleChange(event) {
    let input = event.target.value
    input = input.substring(0, MAX_CHAR)
    this.button.disabled = (input.length === 0)
    if (input.length === MAX_CHAR) {
      this.input.value = input
    }
    this.setState({
      chars_left: MAX_CHAR - input.length,
    })
  }

  render() {
    return (

          <form onSubmit={e => this.onSubmit(e)}>
            <input
              className='message_input'
              placeholder='Type your question here...'
              ref={node => (this.input = node)}
              onChange={e => this.handleChange(e)}
            />

{this.state.chars_left}/{MAX_CHAR}

            <button className='send_message' type='submit' ref={(button) => (this.button= button)} disabled>Send</button>
          </form>

    )
  }
}

const withAddQuestion = graphql(CREATE_QUESTION_MUTATION,
  {
    props: ({ mutate }) => ({
      addQuestion(body, id) {
        return mutate({
          variables: { body: body, user: id },
          updateQueries: {
            questions: (state, { mutationResult }) => {
              let newQuestion = mutationResult.data.createQuestion
              if (!isDuplicate(newQuestion)) {
                addToLocalCache(newQuestion)
                return update(state, {
                  allQuestions: {
                    $push: [mutationResult.data.createQuestion],
                  },
                })
              }
            },
          },
        }).catch(error => {
          Alert.error(error.message, ALERT_DEFAULT)
        })
      },
    }),
  },
)

AddQuestion.propTypes = {
  auth: PropTypes.object.isRequired,
}

export default withAddQuestion(AddQuestion)
<commit_msg>Small refactor<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { graphql } from 'react-apollo'
import update from 'immutability-helper'
import { addToLocalCache, isDuplicate, ALERT_DEFAULT } from '../utils/helpers'
require('smoothscroll-polyfill').polyfill()
import Alert from 'react-s-alert'

import CREATE_QUESTION_MUTATION from '../graphql/CreateQuestion.mutation.gql'
const MAX_CHAR = 140

class AddQuestion extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      chars_left: MAX_CHAR,
    }
  }

  onSubmit(event) {
    event.preventDefault()
    if (!this.input.value || this.input.value.length === 0) {
      return
    }
    if (scrollTo) {
      // let elem = document.getElementById('app')
      // elem.scrollTop = elem.scrollHeight
      let elem = document.querySelector('#bottom')
      elem.scrollIntoView({ behavior: 'smooth' })
    }
    this.props
      .addQuestion(this.input.value, this.props.auth.userId)
      .then(() => {
        Alert.info('Question sent! ✨🚀', ALERT_DEFAULT)
        this.input.value = ''
        this.setState({
          chars_left: MAX_CHAR,
        })
      })
  }

  handleChange(event) {
    let input = event.target.value
    input = input.substring(0, MAX_CHAR)
    this.button.disabled = (input.length === 0)
    if (input.length === MAX_CHAR) {
      this.input.value = input
    }
    this.setState({
      chars_left: MAX_CHAR - input.length,
    })
  }

  render() {
    return (

          <form onSubmit={e => this.onSubmit(e)}>
            <input
              className='message_input'
              placeholder='Type your question here...'
              ref={node => (this.input = node)}
              onChange={e => this.handleChange(e)}
            />

{this.state.chars_left}/{MAX_CHAR}

            <button className='send_message' type='submit' ref={(button) => (this.button= button)} disabled>Send</button>
          </form>

    )
  }
}

const withAddQuestion = graphql(CREATE_QUESTION_MUTATION,
  {
    props: ({ mutate }) => ({
      addQuestion(body, id) {
        return mutate({
          variables: { body: body, user: id },
          updateQueries: {
            questions: (state, { mutationResult }) => {
              let newQuestion = mutationResult.data.createQuestion
              if (!isDuplicate(newQuestion)) {
                addToLocalCache(newQuestion)
                return update(state, {
                  allQuestions: {
                    $push: [newQuestion],
                  },
                })
              }
            },
          },
        }).catch(error => {
          Alert.error(error.message, ALERT_DEFAULT)
        })
      },
    }),
  },
)

AddQuestion.propTypes = {
  auth: PropTypes.object.isRequired,
}

export default withAddQuestion(AddQuestion)
<|endoftext|>