<commit_before>var isNode = typeof module != 'undefined' && module.exports;

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(canvas) {
   if (isNode) {
      var FS = require('fs');
      var Fabric = require('fabric').fabric;
   } else {
      var Fabric = fabric;
   }

   var colorValues = {
      'red': 'rgb(193,40,11)',
      'orange': 'rgb(355,144,36)',
      'yellow': 'rgb(243,224,14)',
      'green': 'rgb(22,220,129)',
      'blue': 'rgb(35,67,232)',
      'violet': 'rgb(220,84,183)',
      'black': 'rgb(0,0,0)'
   };

   var imageOffset;
   var resizeRatio = 1;

   var markupObjects = new Array();

   function clone(obj) {
      var newobj = {};
      for (property in obj) {
         if (typeof obj[property] == 'object') {
            newobj[property] = clone(property);
         } else {
            newobj[property] = obj[property];
         }
      }
      return newobj;
   }

   /**
    * Cycles through an object and changes all numeric fields to ints
    * where necessary. 'context' is used for exception reporting and can be
    * left unset upon invocation.
    */
   function cleanJSON(json, context) {
      if (!context)
         context = "root";

      for (property in json) {
         if (typeof(json[property]) == 'object') {
            cleanJSON(json[property], context + '.' + property);
         }
         else if (property == 'x' || property == 'y' ||
          property == 'width' || property == 'height' ||
          property == 'radius') {
            if (typeof(json[property]) == 'string') {
             json[property] = parseInt(json[property]);
               if (isNaN(json[property])) {
                  var msg = "In '" + context + "': property '" + property +
                   "' is not a number.";
                  throw msg;
               }
            }
         }
      }
   }

   function applyBackground(json, canvas, callback) {
      if (!json['sourceFile']) {
         if (!json['finalDimensions']) {
            var msg = "Need source file or final dimensions to create canvas";
            throw Exception(msg);
         }

         //Apply markup to blank canvas
         applyMarkup(json, canvas, callback);
      } else {
         if (isNode) {
            FS.readFile(json['sourceFile'], function (err, blob) {
               if (err) throw err;

               var dimensions = json['dimensions'];
               var finalDimensions = json['finalDimensions'];
               img = {
                  'width': dimensions['width'],
                  'height': dimensions['height'],
                  'src':blob
               };

               Fabric.Image.fromObject(img, function(fimg) {
                  top = img.height/2 - imageOffset['y'];
                  if (top % 1 != 0) {
                     top -= 0.5;
                  }
                  left = img.width / 2 - imageOffset['x'];
                  if (left % 1 != 0) {
                     left -= 0.5;
                  }

                  canvas.add(fimg.set('top', top).set('left', left));

                  applyMarkup(json, canvas, callback);
               });
            });
         } else {
            throw Exception('Source files not supported on frontend');
         }
      }
   }

   function applyMarkup(json, canvas, callback) {
      for (instruction in json['instructions']) {
         switch (instruction) {
            case 'draw':
               json['instructions']['draw'].forEach(function (e) {
               for (shapeName in e) {
                  shape = e[shapeName];
                  shape['shapeName'] = shapeName;

                  switch (shapeName) {
                     case 'rectangle':
                        drawRectangle(json['finalDimensions']['width'],
                         canvas, shape, imageOffset);
                        break;
                     case 'circle':
                        drawCircle(json['finalDimensions']['width'], canvas,
                         shape, imageOffset);
                        break;
                     default:
                        console.error('Unsupported Shape: ' + shapeName);
                  }
               }
            });
            break;
            case 'crop':
               //もう処理していたから無視する
               break;
            default:
               console.error('Unsupported Instruction: ' + instruction);
         }
      }

      if (isNode) {
         writeCanvas(json, canvas, callback);
      } else {
         callback(canvas);
      }
   }

   function writeCanvas(json, canvas, callback) {
      canvas.renderAll();
      var outstream = FS.createWriteStream(json['destinationFile']),
      stream = canvas.createJPEGStream({
         quality: 93
      });

      stream.on('data', function(chunk) {
         outstream.write(chunk);
      });
      stream.on('end', function () {
         callback(canvas);
      });
   }

   function drawRectangle(finalWidth, canvas, shape, imageOffset) {
      shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
      whiteStroke = 2;

      var rect = {
         shapeName: shape['shapeName'],
         left: shape['from']['x']-imageOffset['x'],
         top: shape['from']['y']-imageOffset['y'],
         width: shape['size']['width'],
         height: shape['size']['height'],
         rx: 1,
         ry: 1,
         strokeWidth: shape['stroke'],
         stroke: colorValues[shape['color']],
         fill: 'transparent'
      }

      //Fabric調整
      rect['top'] = rect['top'] + rect['height'] / 2
       + rect['strokeWidth'] / 2;
      rect['top'] *= resizeRatio;
      rect['left'] = rect['left'] + rect['width'] / 2
       + rect['strokeWidth'] / 2;
      rect['left'] *= resizeRatio;
      rect['width'] *= resizeRatio;
      rect['height'] *= resizeRatio;

      var rectBorder = clone(rect);
      rectBorder['width'] = rectBorder['width'] + rect['strokeWidth']
       + whiteStroke;
      rectBorder['height'] = rectBorder['height'] + rect['strokeWidth']
       + whiteStroke;
      rectBorder['rx'] = rect['strokeWidth'] - whiteStroke;
      rectBorder['ry'] = rect['strokeWidth'] - whiteStroke;
      rectBorder['strokeWidth'] = whiteStroke;
      rectBorder['stroke'] = 'white';

      var rectInline = clone(rect);
      rectInline['width'] = rectInline['width'] - rect['strokeWidth']
       + whiteStroke;
      rectInline['height'] = rectInline['height'] - rect['strokeWidth']
       + whiteStroke;
      rectInline['rx'] = Math.max(shape['stroke'] - rectBorder['rx'],4);
      rectInline['ry'] = Math.max(shape['stroke'] - rectBorder['ry'],4);
      rectInline['strokeWidth'] = whiteStroke;
      rectInline['stroke'] = 'white';

      if (isNode && shadows == true) {
         drawShadow(canvas, rect, shadowStep);
      }

      var fabricRect = new Fabric.Rect(rect),
      fabricBorder = new Fabric.Rect(rectBorder),
      fabricInline = new Fabric.Rect(rectInline);

      fabricRect.lockUniScaling = fabricBorder.lockUniScaling =
         fabricInline.lockUniScaling = true;

      var group = new Fabric.Group([fabricRect, fabricBorder, fabricInline],
       {left: rect['left'], top: rect['top']});

      group.shapeName = 'Rectangle';

      markupObjects[] = group;
      canvas.add(group);
   }

   function drawCircle(finalWidth, canvas, shape, imageOffset) {
      shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
      whiteStroke = 2;

      var circle = {
         shapeName: shape['shapeName'],
         left: shape['from']['x']-imageOffset['x'],
         top: shape['from']['y']-imageOffset['y'],
         radius: shape['radius'],
         strokeWidth: shape['stroke'],
         stroke: colorValues[shape['color']],
         fill: 'transparent'
      };
      circle.left *= resizeRatio;
      circle.top *= resizeRatio;
      circle.radius *= resizeRatio;

      var circleBorder = clone(circle);
      circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
      circleBorder['strokeWidth'] = whiteStroke;
      circleBorder['stroke'] = 'white';

      var circleInline = clone(circleBorder);
      circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

      if (isNode && shadows == true) {
         drawShadow(canvas, circle, shadowStep);
      }

      var fabricCircle = new Fabric.Circle(circle),
      fabricBorder = new Fabric.Circle(circleBorder),
      fabricInline = new Fabric.Circle(circleInline);

      fabricCircle.lockRotation = fabricBorder.lockRotation =
         fabricInline.lockRotation = true;
      fabricCircle.lockUniScaling = fabricBorder.lockUniScaling =
         fabricInline.lockUniScaling = true;

      var group = new Fabric.Group([fabricCircle, fabricBorder, fabricInline],
       {left: fabricCircle['left'], top: fabricCircle['top']});

      group.shapeName = 'Circle';

      markupObjects[] = group;
      canvas.add(group);
   }

   /**
    * Draw a fuzzy shadow for the shape given.
    * Take care to draw the shadow before the shape.
    */
   function drawShadow(canvas, shape, step) {
      if (step < 1) {
         step = 1;
      }
      var shadow = Cloner.clone(shape);
      if (step > shadow['strokeWidth']) {
         step = shadow['strokeWidth'];
      }

      var offsetX = 8;
      var offsetY = offsetX;

      var shadow = Cloner.clone(shape);
      shadow['left'] += offsetX;
      shadow['top'] += offsetY;
      shadow['rx'] = 5;
      shadow['ry'] = 5;
      shadow['stroke'] = 'rgba(0,0,0,0.5)';
      shadow['strokeWidth'] = shadow['strokeWidth'] / step;
      var stepWidth = shadow['strokeWidth'];

      //Empirically-derived pixel tweaks to line up shadow sizes with their
      //parents. TODO: Base these numbers on something real.
      var circleTweak = 0.6875;
      var rectangleTweak = 1.3125;

      //Adjust shadow outlines to outer edge, to work towards inside later.
      switch (shape['shapeName']) {
         case 'circle':
            shadow['radius'] += shape['strokeWidth'] * circleTweak;
            shadow['strokeWidth'] *= 2;
            break;
         case 'rectangle':
            shadow['width'] += shape['strokeWidth'] * rectangleTweak;
            shadow['height'] += shape['strokeWidth'] * rectangleTweak;
            shadow['strokeWidth'] *= 2;
            break;
         default:
            console.error('実装されていない機能：' + shape['shapeName']);
            return;
      }

      var alpha;
      for (var i = 0; i < step; ++i) {
         if (i < (step / 2)) {
            alpha = (((i + 1) * 2) / (step));
         }
         else if (i == step / 2) {
            //Math ain't working to my likings
            alpha = 1;
         }
         else {
            alpha = (((step - (i + 1)) * 2) / (step));
         }

         shadow['stroke'] = 'rgba(0,0,0,' + alpha + ')';

         switch (shape['shapeName']) {
            case 'circle':
               canvas.add(new Fabric.Circle(shadow));
               shadow['radius'] = shadow['radius'] - stepWidth * 2 * 0.8;
               break;
            case 'rectangle':
               canvas.add(new Fabric.Rect(shadow));
               shadow['width'] = shadow['width'] - stepWidth * 4 * 0.8;
               shadow['height'] = shadow['height'] - stepWidth * 4 * 0.8;
               break;
            default:
               console.error('実装されてない機能：' + shape['shapeName']);
               return;
         }
      }
   }

   return {
      processJSON: function processJSON(json, callback) {
         //Make sure not to render every addition on server end
         canvas.renderOnAddition = !isNode;

         cleanJSON(json);

         var imagePath = json['sourceFile'];

         var crop = json['instructions']['crop'];
         imageOffset = (typeof crop != "undefined") ?
            {'x':crop['from']['x'],
               'y':crop['from']['y']} :
                  {'x':0,'y':0};

               if (json['previewInstructions']) {
                  imageOffset.x = json.previewInstructions.left;
                  imageOffset.y = json.previewInstructions.top;
                  resizeRatio = 1 / json.previewInstructions.ratio;
               }

               applyBackground(json, canvas, callback);
      },

      getMarkupObjects: function getMarkupObjects(callback) {
         return markupObjects;
      }
   };
}

if (isNode)
   module.exports = ImageMarkupBuilder;
else
   var createImageFromMarkup = ImageMarkupBuilder;
<commit_msg>That wasn't how array pushes work...<commit_after>var isNode = typeof module != 'undefined' && module.exports;

/**
 * Expects a Fabric.js Canvas
 */
function ImageMarkupBuilder(canvas) {
   if (isNode) {
      var FS = require('fs');
      var Fabric = require('fabric').fabric;
   } else {
      var Fabric = fabric;
   }

   var colorValues = {
      'red': 'rgb(193,40,11)',
      'orange': 'rgb(355,144,36)',
      'yellow': 'rgb(243,224,14)',
      'green': 'rgb(22,220,129)',
      'blue': 'rgb(35,67,232)',
      'violet': 'rgb(220,84,183)',
      'black': 'rgb(0,0,0)'
   };

   var imageOffset;
   var resizeRatio = 1;

   var markupObjects = new Array();

   function clone(obj) {
      var newobj = {};
      for (property in obj) {
         if (typeof obj[property] == 'object') {
            newobj[property] = clone(property);
         } else {
            newobj[property] = obj[property];
         }
      }
      return newobj;
   }

   /**
    * Cycles through an object and changes all numeric fields to ints
    * where necessary. 'context' is used for exception reporting and can be
    * left unset upon invocation.
    */
   function cleanJSON(json, context) {
      if (!context)
         context = "root";

      for (property in json) {
         if (typeof(json[property]) == 'object') {
            cleanJSON(json[property], context + '.' + property);
         }
         else if (property == 'x' || property == 'y' ||
          property == 'width' || property == 'height' ||
          property == 'radius') {
            if (typeof(json[property]) == 'string') {
             json[property] = parseInt(json[property]);
               if (isNaN(json[property])) {
                  var msg = "In '" + context + "': property '" + property +
                   "' is not a number.";
                  throw msg;
               }
            }
         }
      }
   }

   function applyBackground(json, canvas, callback) {
      if (!json['sourceFile']) {
         if (!json['finalDimensions']) {
            var msg = "Need source file or final dimensions to create canvas";
            throw Exception(msg);
         }

         //Apply markup to blank canvas
         applyMarkup(json, canvas, callback);
      } else {
         if (isNode) {
            FS.readFile(json['sourceFile'], function (err, blob) {
               if (err) throw err;

               var dimensions = json['dimensions'];
               var finalDimensions = json['finalDimensions'];
               img = {
                  'width': dimensions['width'],
                  'height': dimensions['height'],
                  'src':blob
               };

               Fabric.Image.fromObject(img, function(fimg) {
                  top = img.height/2 - imageOffset['y'];
                  if (top % 1 != 0) {
                     top -= 0.5;
                  }
                  left = img.width / 2 - imageOffset['x'];
                  if (left % 1 != 0) {
                     left -= 0.5;
                  }

                  canvas.add(fimg.set('top', top).set('left', left));

                  applyMarkup(json, canvas, callback);
               });
            });
         } else {
            throw Exception('Source files not supported on frontend');
         }
      }
   }

   function applyMarkup(json, canvas, callback) {
      for (instruction in json['instructions']) {
         switch (instruction) {
            case 'draw':
               json['instructions']['draw'].forEach(function (e) {
               for (shapeName in e) {
                  shape = e[shapeName];
                  shape['shapeName'] = shapeName;

                  switch (shapeName) {
                     case 'rectangle':
                        drawRectangle(json['finalDimensions']['width'],
                         canvas, shape, imageOffset);
                        break;
                     case 'circle':
                        drawCircle(json['finalDimensions']['width'], canvas,
                         shape, imageOffset);
                        break;
                     default:
                        console.error('Unsupported Shape: ' + shapeName);
                  }
               }
            });
            break;
            case 'crop':
               //もう処理していたから無視する
               break;
            default:
               console.error('Unsupported Instruction: ' + instruction);
         }
      }

      if (isNode) {
         writeCanvas(json, canvas, callback);
      } else {
         callback(canvas);
      }
   }

   function writeCanvas(json, canvas, callback) {
      canvas.renderAll();
      var outstream = FS.createWriteStream(json['destinationFile']),
      stream = canvas.createJPEGStream({
         quality: 93
      });

      stream.on('data', function(chunk) {
         outstream.write(chunk);
      });
      stream.on('end', function () {
         callback(canvas);
      });
   }

   function drawRectangle(finalWidth, canvas, shape, imageOffset) {
      shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
      whiteStroke = 2;

      var rect = {
         shapeName: shape['shapeName'],
         left: shape['from']['x']-imageOffset['x'],
         top: shape['from']['y']-imageOffset['y'],
         width: shape['size']['width'],
         height: shape['size']['height'],
         rx: 1,
         ry: 1,
         strokeWidth: shape['stroke'],
         stroke: colorValues[shape['color']],
         fill: 'transparent'
      }

      //Fabric調整
      rect['top'] = rect['top'] + rect['height'] / 2
       + rect['strokeWidth'] / 2;
      rect['top'] *= resizeRatio;
      rect['left'] = rect['left'] + rect['width'] / 2
       + rect['strokeWidth'] / 2;
      rect['left'] *= resizeRatio;
      rect['width'] *= resizeRatio;
      rect['height'] *= resizeRatio;

      var rectBorder = clone(rect);
      rectBorder['width'] = rectBorder['width'] + rect['strokeWidth']
       + whiteStroke;
      rectBorder['height'] = rectBorder['height'] + rect['strokeWidth']
       + whiteStroke;
      rectBorder['rx'] = rect['strokeWidth'] - whiteStroke;
      rectBorder['ry'] = rect['strokeWidth'] - whiteStroke;
      rectBorder['strokeWidth'] = whiteStroke;
      rectBorder['stroke'] = 'white';

      var rectInline = clone(rect);
      rectInline['width'] = rectInline['width'] - rect['strokeWidth']
       + whiteStroke;
      rectInline['height'] = rectInline['height'] - rect['strokeWidth']
       + whiteStroke;
      rectInline['rx'] = Math.max(shape['stroke'] - rectBorder['rx'],4);
      rectInline['ry'] = Math.max(shape['stroke'] - rectBorder['ry'],4);
      rectInline['strokeWidth'] = whiteStroke;
      rectInline['stroke'] = 'white';

      if (isNode && shadows == true) {
         drawShadow(canvas, rect, shadowStep);
      }

      var fabricRect = new Fabric.Rect(rect),
      fabricBorder = new Fabric.Rect(rectBorder),
      fabricInline = new Fabric.Rect(rectInline);

      fabricRect.lockUniScaling = fabricBorder.lockUniScaling =
         fabricInline.lockUniScaling = true;

      var group = new Fabric.Group([fabricRect, fabricBorder, fabricInline],
       {left: rect['left'], top: rect['top']});

      group.shapeName = 'Rectangle';

      markupObjects.push(group);
      canvas.add(group);
   }

   function drawCircle(finalWidth, canvas, shape, imageOffset) {
      shape['stroke'] = Math.max(Math.round(finalWidth / 300 * 2), 2);
      whiteStroke = 2;

      var circle = {
         shapeName: shape['shapeName'],
         left: shape['from']['x']-imageOffset['x'],
         top: shape['from']['y']-imageOffset['y'],
         radius: shape['radius'],
         strokeWidth: shape['stroke'],
         stroke: colorValues[shape['color']],
         fill: 'transparent'
      };
      circle.left *= resizeRatio;
      circle.top *= resizeRatio;
      circle.radius *= resizeRatio;

      var circleBorder = clone(circle);
      circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
      circleBorder['strokeWidth'] = whiteStroke;
      circleBorder['stroke'] = 'white';

      var circleInline = clone(circleBorder);
      circleInline['radius'] = circle['radius'] - circle['strokeWidth'] / 2;

      if (isNode && shadows == true) {
         drawShadow(canvas, circle, shadowStep);
      }

      var fabricCircle = new Fabric.Circle(circle),
      fabricBorder = new Fabric.Circle(circleBorder),
      fabricInline = new Fabric.Circle(circleInline);

      fabricCircle.lockRotation = fabricBorder.lockRotation =
         fabricInline.lockRotation = true;
      fabricCircle.lockUniScaling = fabricBorder.lockUniScaling =
         fabricInline.lockUniScaling = true;

      var group = new Fabric.Group([fabricCircle, fabricBorder, fabricInline],
       {left: fabricCircle['left'], top: fabricCircle['top']});

      group.shapeName = 'Circle';

      markupObjects.push(group);
      canvas.add(group);
   }

   /**
    * Draw a fuzzy shadow for the shape given.
    * Take care to draw the shadow before the shape.
    */
   function drawShadow(canvas, shape, step) {
      if (step < 1) {
         step = 1;
      }
      var shadow = Cloner.clone(shape);
      if (step > shadow['strokeWidth']) {
         step = shadow['strokeWidth'];
      }

      var offsetX = 8;
      var offsetY = offsetX;

      var shadow = Cloner.clone(shape);
      shadow['left'] += offsetX;
      shadow['top'] += offsetY;
      shadow['rx'] = 5;
      shadow['ry'] = 5;
      shadow['stroke'] = 'rgba(0,0,0,0.5)';
      shadow['strokeWidth'] = shadow['strokeWidth'] / step;
      var stepWidth = shadow['strokeWidth'];

      //Empirically-derived pixel tweaks to line up shadow sizes with their
      //parents. TODO: Base these numbers on something real.
      var circleTweak = 0.6875;
      var rectangleTweak = 1.3125;

      //Adjust shadow outlines to outer edge, to work towards inside later.
      switch (shape['shapeName']) {
         case 'circle':
            shadow['radius'] += shape['strokeWidth'] * circleTweak;
            shadow['strokeWidth'] *= 2;
            break;
         case 'rectangle':
            shadow['width'] += shape['strokeWidth'] * rectangleTweak;
            shadow['height'] += shape['strokeWidth'] * rectangleTweak;
            shadow['strokeWidth'] *= 2;
            break;
         default:
            console.error('実装されていない機能：' + shape['shapeName']);
            return;
      }

      var alpha;
      for (var i = 0; i < step; ++i) {
         if (i < (step / 2)) {
            alpha = (((i + 1) * 2) / (step));
         }
         else if (i == step / 2) {
            //Math ain't working to my likings
            alpha = 1;
         }
         else {
            alpha = (((step - (i + 1)) * 2) / (step));
         }

         shadow['stroke'] = 'rgba(0,0,0,' + alpha + ')';

         switch (shape['shapeName']) {
            case 'circle':
               canvas.add(new Fabric.Circle(shadow));
               shadow['radius'] = shadow['radius'] - stepWidth * 2 * 0.8;
               break;
            case 'rectangle':
               canvas.add(new Fabric.Rect(shadow));
               shadow['width'] = shadow['width'] - stepWidth * 4 * 0.8;
               shadow['height'] = shadow['height'] - stepWidth * 4 * 0.8;
               break;
            default:
               console.error('実装されてない機能：' + shape['shapeName']);
               return;
         }
      }
   }

   return {
      processJSON: function processJSON(json, callback) {
         //Make sure not to render every addition on server end
         canvas.renderOnAddition = !isNode;

         cleanJSON(json);

         var imagePath = json['sourceFile'];

         var crop = json['instructions']['crop'];
         imageOffset = (typeof crop != "undefined") ?
            {'x':crop['from']['x'],
               'y':crop['from']['y']} :
                  {'x':0,'y':0};

               if (json['previewInstructions']) {
                  imageOffset.x = json.previewInstructions.left;
                  imageOffset.y = json.previewInstructions.top;
                  resizeRatio = 1 / json.previewInstructions.ratio;
               }

               applyBackground(json, canvas, callback);
      },

      getMarkupObjects: function getMarkupObjects(callback) {
         return markupObjects;
      }
   };
}

if (isNode)
   module.exports = ImageMarkupBuilder;
else
   var createImageFromMarkup = ImageMarkupBuilder;
<|endoftext|>


<commit_before>var hbs = require('handlebars')
var fs = require('fs')
var request = require('request')
var btoa = require('btoa')

module.exports = function (callback) {
  if (process.env['CONTRIBUTORS']) {
    fs.readFile(process.env['CONTRIBUTORS'], function (err, data) {
      if (err) return callback(err, 'Error reading contribs file for building page.')
      organizeData(data)
    })
  } else {
    console.log('Making request for data...')
    var uri = 'http://reporobot.jlord.us/data'
    request({ url: uri, json: true }, function (err, res, body) {
      if (err) return callback(err, 'Fetching latest data for building page')
      organizeData(JSON.stringify(body))
    })
  }

  function organizeData (data) {
    var everyone = JSON.parse(data)
    var everyoneCommas = everyone.length.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    var newest = everyone[everyone.length - 1]
    var topHundred = everyone.reverse().slice(0, 100)
    var stats = { featured: newest, everyone: topHundred, total: everyoneCommas }
    return getTemplate(stats, everyone)
  }

  function getTemplate (stats, everyone) {
    fs.readFile('template.hbs', function (err, file) {
      if (err) return callback(err, 'Error reading template file.')
      file = file.toString()
      var template = hbs.compile(file)
      var HTML = template(stats)
      return writeRepo(HTML, stats, everyone)
    })
  }

  function writeRepo (HTML, stats, everyone) {
    var username = stats.featured.user
    var baseURL = 'https://api.github.com/repos/'

    var reqHeaders = {
      'User-Agent': 'request',
      'Authorization': 'token ' + process.env['REPOROBOT_TOKEN']
    }

    var options = {
      headers: reqHeaders,
      url: baseURL + 'jlord/patchwork/contents/index.html',
      json: true,
      body: {
        'branch': 'gh-pages',
        'committer': {
          'name': 'reporobot',
          'email': '60ebe73fdad8ee59d45c@cloudmailin.net'
        },
        'sha': '',
        'content': btoa(HTML),
        'message': 'Rebuilt index with ' + username
      }
    }

    request.get(options, function (err, res, body) {
      if (err) return callback(err, 'Error fetching SHA')
      options.body.sha = body.sha
      request.put(options, function (err, res, body) {
        if (err) return callback(err, 'Error writing new index to Patchwork')
        callback(null, 'Rebuilt index with ' + username)
      })
    })
  }
}
<commit_msg>H A C K Y<commit_after>var hbs = require('handlebars')
var fs = require('fs')
var request = require('request')
var btoa = require('btoa')

module.exports = function (callback) {
  if (process.env['CONTRIBUTORS']) {
    fs.readFile(process.env['CONTRIBUTORS'], function (err, data) {
      if (err) return callback(err, 'Error reading contribs file for building page.')
      organizeData(data)
    })
  } else {
    console.log('Making request for data...')
    var uri = 'http://reporobot.jlord.us/data'
    request({ url: uri, json: true }, function (err, res, body) {
      if (err) return callback(err, 'Fetching latest data for building page')
      organizeData(JSON.stringify(body))
    })
  }

  function organizeData (data) {
    var everyone = JSON.parse(data)
    // TODO Fix counting this
    var everyoneCount = everyone.length + 12425 // From archive
    var everyoneCommas = everyoneCount.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    var newest = everyone[everyone.length - 1]
    var topHundred = everyone.reverse().slice(0, 100)
    var stats = { featured: newest, everyone: topHundred, total: everyoneCommas }
    return getTemplate(stats, everyone)
  }

  function getTemplate (stats, everyone) {
    fs.readFile('template.hbs', function (err, file) {
      if (err) return callback(err, 'Error reading template file.')
      file = file.toString()
      var template = hbs.compile(file)
      var HTML = template(stats)
      return writeRepo(HTML, stats, everyone)
    })
  }

  function writeRepo (HTML, stats, everyone) {
    var username = stats.featured.user
    var baseURL = 'https://api.github.com/repos/'

    var reqHeaders = {
      'User-Agent': 'request',
      'Authorization': 'token ' + process.env['REPOROBOT_TOKEN']
    }

    var options = {
      headers: reqHeaders,
      url: baseURL + 'jlord/patchwork/contents/index.html',
      json: true,
      body: {
        'branch': 'gh-pages',
        'committer': {
          'name': 'reporobot',
          'email': '60ebe73fdad8ee59d45c@cloudmailin.net'
        },
        'sha': '',
        'content': btoa(HTML),
        'message': 'Rebuilt index with ' + username
      }
    }

    request.get(options, function (err, res, body) {
      if (err) return callback(err, 'Error fetching SHA')
      options.body.sha = body.sha
      request.put(options, function (err, res, body) {
        if (err) return callback(err, 'Error writing new index to Patchwork')
        callback(null, 'Rebuilt index with ' + username)
      })
    })
  }
}
<|endoftext|>


<commit_before>define(function (require, exports, module) {
    "use strict";

    var type = require("base/type");
    var assert = require("base/assert");
    var derive = require("base/derive");
    var isFunction = require("base/isFunction");
    var boost = require("boost/boost");
    var copyProperties = require("base/copyProperties");
    var Event = require("boost/Event");

    var emptyArray = [],
        noop = function () {},
        slice = emptyArray.slice,
        concat = emptyArray.concat;

    function isObject(obj) {
        return type(obj) == "object";
    }

    function likeArray(obj) {
        return typeof obj.length == 'number';
    }

    function flatten(array) {
        return array.length > 0 ? concat.apply([], array) : array;
    }

    function walkTree (node, handler) {
        handler(node);
        if (!node.firstChild) { return; }
        for (var cur = node.firstChild; cur; cur = cur.nextSibling) {
            walkTree(cur, handler);
        }
    }

    function updateStyle (element) {
        //TODO: change to update all children style
        walkTree(element, function (curElement) {
            var elementStyleRender = curElement.__styleRender__;
            if (!elementStyleRender) {
                //应对非xml-parser解析出的元素
                console.warn("to auto update element's style when it's class changes, element.__styleRender(added in xml-parser.js) is required.");
                return;
            }

            elementStyleRender.clearStyle(curElement);
            elementStyleRender.applyOnOne(curElement);
        });
    }

    function $(selector, context) {
        var dom;
        if (!selector) {
            dom = [];
        } else if (typeof selector == 'string') {
            // FIXME 不支持传HTML
            context = context || boost;
            dom = context.querySelectorAll(selector);
        } else if (selector instanceof $) {
            return selector;
        } else if (likeArray(selector)) {
            //dom = slice.call(selector);
            dom = selector;
        } else if (isObject(selector)) {
            dom = [selector];
        }
        dom.__proto__ = $.fn;
        return dom;
    }

    $.fn = $.prototype = {
        // Because a collection acts like an array
        // copy over these useful array functions.
        forEach: emptyArray.forEach,
        reduce: emptyArray.reduce,
        push: emptyArray.push,
        sort: emptyArray.sort,
        indexOf: emptyArray.indexOf,
        concat: emptyArray.concat,
        // `map` and `slice` in the jQuery API work differently
        // from their array counterparts
        map: function (fn) {
            return $($.map(this, function (el, i) {
                return fn.call(el, i, el);
            }));
        },
        slice: function () {
            return $(slice.apply(this, arguments));
        },
        get: function (idx) {
            return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length];
        },
        size: function () {
            return this.length;
        },
        remove: function () {
            return this.each(function () {
                if (this.parentNode != null)
                    this.parentNode.removeChild(this);
            });
        },
        each: function (callback) {
            emptyArray.every.call(this, function (el, idx) {
                return callback.call(el, idx, el) !== false;
            });
            return this;
        },
        eq: function (idx) {
            return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1);
        },
        find: function (selector) {
            var result;
            if (!selector) {
                result = $();
            } else if (this.length === 1) {
                result = $(this[0].querySelectorAll(selector));
            } else {
                result = this.map(function () {
                    return this.querySelectorAll(selector);
                });
            }
            return result;
        },
        empty: function () {
            return this.each(function () {
                var node = null;
                while ((node = this.firstChild) !== null) {
                    this.removeChild(node);
                }
            });
        },
        data: function (key, value) {
            var attrName = "data-" + key;
            return arguments.length > 1 ? this.attr(attrName, value) : this.attr(attrName);
        },
        attr: function (key, value) {
            if (arguments.length > 1) {
                this.each(function (idx, elem) {
                    elem.setAttribute(key, value);
                });
                return this;
            } else if (this.length > 0) {
                return this[0].getAttribute(key);
            } else {
                return null;
            }
        },

        /**
         * @param classStr {string} 可用空白分隔多个要添加的class
         */
        addClass: function (classStr) {
            var classesToAdd = classStr.split(/\s+/);
            return this.each(function (index, element) {
                var classList = element.classList;
                classesToAdd = classesToAdd.filter(function (item) { return classList.indexOf(item) === -1; });
                element.className = classList.concat(classesToAdd).join(' ');
                updateStyle(element);
            });
        },

        /**
         * @param classStr {string} 可用空白分隔多个要添加的class
         */
        removeClass: function (classStr) {
            var classesToRemove = classStr.split(/\s+/);
            return this.each(function (index, element) {
                var classList = element.classList;
                classList = classList.filter(function (item) { return classesToRemove.indexOf(item) === -1; });
                element.className = classList.join(' ');
                updateStyle(element);
            });
        },

        hasClass: function (classStr) {
            var has = false;
            this.each(function (index, element) {
                if (element.classList.indexOf(classStr) !== -1) {
                    has = true;
                }
            });
            return has;
        },

        on: function (type, callback) {
            return this.each(function (idx, element) {
                element.addEventListener(type, callback, false);
            });
        },

        off: function (type, callback) {
            return this.each(function (idx, element) {
                element.removeEventListener(type, callback);
            });
        },

        trigger: function (type, data) {
            return this.each(function (idx, element) {
                var event = new Event(element, type);
                event.data = data;
                element.dispatchEvent(event);
            });
        },

        val: function (value) {
            if (arguments.length > 0) {
                return this.each(function (idx, element) {
                    element.value = value;
                });
            } else if (this.length > 0) {
                return this[0].value;
            } else {
                return null;
            }
        },

        css: function (key, value) {
            assert(arguments.length > 1, "目前只支持 css(key, value)");
            return this.each(function (idx, element) {
                element.style[key] = value;
            });
        }
    };

    $.map = function (elements, callback) {
        var value, values = [],
            i, key;
        if (likeArray(elements))
            for (i = 0; i < elements.length; i++) {
                value = callback(elements[i], i);
                if (value != null) values.push(value);
            } else
                for (key in elements) {
                    value = callback(elements[key], key);
                    if (value != null) values.push(value);
                }
        return flatten(values);
    };

    $.Widget = function (options, element) {
        this.element = element;
        this.$element = $(element);
        this.options = copyProperties({}, this.options, options);
        this._create();
        this._trigger('create');
        this._init();
    };
    $.Widget.prototype = {
        options: {},
        _create: noop,
        _init: noop,
        _trigger: function (type, data) {
            type = this.widgetName + ':' + type;
            data = data || {};
            this.$element.trigger(type, data);
        }
    };
    $.widget = function (name, proto) {
        var dataKey = "__widget_" + name + "__";
        proto = copyProperties({}, proto, {
            widgetName: name
        });
        var constructor = derive($.Widget, proto);

        $.fn[name] = function (options) {
            var isMethodCall = typeof options === 'string';
            var args = slice.call(arguments, 1);
            var returnValue = this;

            if (isMethodCall) {
                this.each(function () {
                    var $this = $(this);
                    var instance = $this.data(dataKey);
                    var methodValue;

                    if (!instance) {
                        throw new Error('cannot call methods on ' + name + ' prior to initialization; ' +
                            'attempted to call method "' + options + '"');
                    }
                    if (!isFunction(instance[options]) || options.charAt(0) === '_') {
                        throw new Error('no such method "' + options + '" for ' + name + ' widget instance');
                    }

                    methodValue = instance[options].apply(instance, args);
                    if (methodValue !== instance && methodValue !== undefined) {
                        returnValue = methodValue;
                        return false;
                    }
                });
            } else {
                this.each(function () {
                    var $this = $(this);
                    $this.data(dataKey, new constructor(options, this));
                    /*
                    var instance = $this.data(dataKey);
                    if (instance) {
                        // 已经初始化过
                        instance.option(options || {});
                        if (instance._init) {
                            instance._init();
                        }
                    } else {
                        $this.data(dataKey, new constructor(options, this));
                    }
                    */
                });
            }
            return returnValue;
        };
    };

    module.exports = $;
});
<commit_msg>优化addClass与removeClass：没有变化时不更新样式<commit_after>define(function (require, exports, module) {
    "use strict";

    var type = require("base/type");
    var assert = require("base/assert");
    var derive = require("base/derive");
    var isFunction = require("base/isFunction");
    var boost = require("boost/boost");
    var copyProperties = require("base/copyProperties");
    var Event = require("boost/Event");

    var emptyArray = [],
        noop = function () {},
        slice = emptyArray.slice,
        concat = emptyArray.concat;

    function isObject(obj) {
        return type(obj) == "object";
    }

    function likeArray(obj) {
        return typeof obj.length == 'number';
    }

    function flatten(array) {
        return array.length > 0 ? concat.apply([], array) : array;
    }

    function walkTree (node, handler) {
        handler(node);
        if (!node.firstChild) { return; }
        for (var cur = node.firstChild; cur; cur = cur.nextSibling) {
            walkTree(cur, handler);
        }
    }

    function updateStyle (element) {
        //TODO: change to update all children style
        walkTree(element, function (curElement) {
            var elementStyleRender = curElement.__styleRender__;
            if (!elementStyleRender) {
                //应对非xml-parser解析出的元素
                console.warn("to auto update element's style when it's class changes, element.__styleRender(added in xml-parser.js) is required.");
                return;
            }

            elementStyleRender.clearStyle(curElement);
            elementStyleRender.applyOnOne(curElement);
        });
    }

    function $(selector, context) {
        var dom;
        if (!selector) {
            dom = [];
        } else if (typeof selector == 'string') {
            // FIXME 不支持传HTML
            context = context || boost;
            dom = context.querySelectorAll(selector);
        } else if (selector instanceof $) {
            return selector;
        } else if (likeArray(selector)) {
            //dom = slice.call(selector);
            dom = selector;
        } else if (isObject(selector)) {
            dom = [selector];
        }
        dom.__proto__ = $.fn;
        return dom;
    }

    $.fn = $.prototype = {
        // Because a collection acts like an array
        // copy over these useful array functions.
        forEach: emptyArray.forEach,
        reduce: emptyArray.reduce,
        push: emptyArray.push,
        sort: emptyArray.sort,
        indexOf: emptyArray.indexOf,
        concat: emptyArray.concat,
        // `map` and `slice` in the jQuery API work differently
        // from their array counterparts
        map: function (fn) {
            return $($.map(this, function (el, i) {
                return fn.call(el, i, el);
            }));
        },
        slice: function () {
            return $(slice.apply(this, arguments));
        },
        get: function (idx) {
            return idx === undefined ? slice.call(this) : this[idx >= 0 ? idx : idx + this.length];
        },
        size: function () {
            return this.length;
        },
        remove: function () {
            return this.each(function () {
                if (this.parentNode != null)
                    this.parentNode.removeChild(this);
            });
        },
        each: function (callback) {
            emptyArray.every.call(this, function (el, idx) {
                return callback.call(el, idx, el) !== false;
            });
            return this;
        },
        eq: function (idx) {
            return idx === -1 ? this.slice(idx) : this.slice(idx, +idx + 1);
        },
        find: function (selector) {
            var result;
            if (!selector) {
                result = $();
            } else if (this.length === 1) {
                result = $(this[0].querySelectorAll(selector));
            } else {
                result = this.map(function () {
                    return this.querySelectorAll(selector);
                });
            }
            return result;
        },
        empty: function () {
            return this.each(function () {
                var node = null;
                while ((node = this.firstChild) !== null) {
                    this.removeChild(node);
                }
            });
        },
        data: function (key, value) {
            var attrName = "data-" + key;
            return arguments.length > 1 ? this.attr(attrName, value) : this.attr(attrName);
        },
        attr: function (key, value) {
            if (arguments.length > 1) {
                this.each(function (idx, elem) {
                    elem.setAttribute(key, value);
                });
                return this;
            } else if (this.length > 0) {
                return this[0].getAttribute(key);
            } else {
                return null;
            }
        },

        /**
         * @param classStr {string} 可用空白分隔多个要添加的class
         */
        addClass: function (classStr) {
            var classesToAdd = classStr.split(/\s+/);
            return this.each(function (index, element) {
                var classList = element.classList;
                classesToAdd = classesToAdd.filter(function (item) { return classList.indexOf(item) === -1; });
                if (classesToAdd.length === 0) {
                    return;
                }

                element.className = classList.concat(classesToAdd).join(' ');
                updateStyle(element);
            });
        },

        /**
         * @param classStr {string} 可用空白分隔多个要添加的class
         */
        removeClass: function (classStr) {
            var classesToRemove = classStr.split(/\s+/);
            return this.each(function (index, element) {
                var classList = element.classList;
                var newClassList = classList.filter(function (item) { return classesToRemove.indexOf(item) === -1; });
                if (newClassList.length === classList.length) {
                    return;
                }

                element.className = newClassList.join(' ');
                updateStyle(element);
            });
        },

        hasClass: function (classStr) {
            var has = false;
            this.each(function (index, element) {
                if (element.classList.indexOf(classStr) !== -1) {
                    has = true;
                }
            });
            return has;
        },

        on: function (type, callback) {
            return this.each(function (idx, element) {
                element.addEventListener(type, callback, false);
            });
        },

        off: function (type, callback) {
            return this.each(function (idx, element) {
                element.removeEventListener(type, callback);
            });
        },

        trigger: function (type, data) {
            return this.each(function (idx, element) {
                var event = new Event(element, type);
                event.data = data;
                element.dispatchEvent(event);
            });
        },

        val: function (value) {
            if (arguments.length > 0) {
                return this.each(function (idx, element) {
                    element.value = value;
                });
            } else if (this.length > 0) {
                return this[0].value;
            } else {
                return null;
            }
        },

        css: function (key, value) {
            assert(arguments.length > 1, "目前只支持 css(key, value)");
            return this.each(function (idx, element) {
                element.style[key] = value;
            });
        }
    };

    $.map = function (elements, callback) {
        var value, values = [],
            i, key;
        if (likeArray(elements))
            for (i = 0; i < elements.length; i++) {
                value = callback(elements[i], i);
                if (value != null) values.push(value);
            } else
                for (key in elements) {
                    value = callback(elements[key], key);
                    if (value != null) values.push(value);
                }
        return flatten(values);
    };

    $.Widget = function (options, element) {
        this.element = element;
        this.$element = $(element);
        this.options = copyProperties({}, this.options, options);
        this._create();
        this._trigger('create');
        this._init();
    };
    $.Widget.prototype = {
        options: {},
        _create: noop,
        _init: noop,
        _trigger: function (type, data) {
            type = this.widgetName + ':' + type;
            data = data || {};
            this.$element.trigger(type, data);
        }
    };
    $.widget = function (name, proto) {
        var dataKey = "__widget_" + name + "__";
        proto = copyProperties({}, proto, {
            widgetName: name
        });
        var constructor = derive($.Widget, proto);

        $.fn[name] = function (options) {
            var isMethodCall = typeof options === 'string';
            var args = slice.call(arguments, 1);
            var returnValue = this;

            if (isMethodCall) {
                this.each(function () {
                    var $this = $(this);
                    var instance = $this.data(dataKey);
                    var methodValue;

                    if (!instance) {
                        throw new Error('cannot call methods on ' + name + ' prior to initialization; ' +
                            'attempted to call method "' + options + '"');
                    }
                    if (!isFunction(instance[options]) || options.charAt(0) === '_') {
                        throw new Error('no such method "' + options + '" for ' + name + ' widget instance');
                    }

                    methodValue = instance[options].apply(instance, args);
                    if (methodValue !== instance && methodValue !== undefined) {
                        returnValue = methodValue;
                        return false;
                    }
                });
            } else {
                this.each(function () {
                    var $this = $(this);
                    $this.data(dataKey, new constructor(options, this));
                    /*
                    var instance = $this.data(dataKey);
                    if (instance) {
                        // 已经初始化过
                        instance.option(options || {});
                        if (instance._init) {
                            instance._init();
                        }
                    } else {
                        $this.data(dataKey, new constructor(options, this));
                    }
                    */
                });
            }
            return returnValue;
        };
    };

    module.exports = $;
});
<|endoftext|>


<commit_before>import classNames from "classnames";
import { Confirm, Dropdown, Modal } from "reactjs-components";
import { hashHistory } from "react-router";
import { injectIntl } from "react-intl";
import mixin from "reactjs-mixin";
/* eslint-disable no-unused-vars */
import React from "react";
/* eslint-enable no-unused-vars */
import { StoreMixin } from "mesosphere-shared-reactjs";

import AlertPanel from "#SRC/js/components/AlertPanel";
import AlertPanelHeader from "#SRC/js/components/AlertPanelHeader";
import CollapsingString from "#SRC/js/components/CollapsingString";
import DCOSStore from "#SRC/js/stores/DCOSStore";
import ExpandingTable from "#SRC/js/components/ExpandingTable";
import Icon from "#SRC/js/components/Icon";
import Image from "#SRC/js/components/Image";
import Loader from "#SRC/js/components/Loader";
import ModalHeading from "#SRC/js/components/modals/ModalHeading";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StatusBar from "#SRC/js/components/StatusBar";
import StringUtil from "#SRC/js/utils/StringUtil";
import TimeAgo from "#SRC/js/components/TimeAgo";
import UserActions from "#SRC/js/constants/UserActions";

import defaultServiceImage from "../../img/icon-service-default-small@2x.png";
import MarathonActions from "../events/MarathonActions";

const columnClasses = {
  id: "deployments-table-column-id",
  startTime: "deployments-table-column-start-time",
  status: "deployments-table-column-status",
  action: "deployments-table-column-actions"
};
const columnHeadings = ResourceTableUtil.renderHeading({
  id: "Affected Services",
  startTime: "Started",
  status: "Status",
  action: null
});
const METHODS_TO_BIND = [
  "renderAffectedServices",
  "renderStartTime",
  "renderStatus",
  "renderActionsMenu",
  "handleRollbackClick",
  "handleRollbackCancel",
  "handleRollbackConfirm",
  "onMarathonStoreDeploymentRollbackSuccess",
  "onMarathonStoreDeploymentRollbackError"
];

// collapsing columns are tightly coupled to the left-align caret property;
// this wrapper allows ordinary columns to collapse.
function columnClassNameGetter(prop, sortBy, row) {
  return classNames(
    columnClasses[prop],
    ResourceTableUtil.getClassName(prop, sortBy, row)
  );
}

class DeploymentsModal extends mixin(StoreMixin) {
  constructor() {
    super(...arguments);

    this.state = {};
    this.store_listeners = [
      { name: "dcos", events: ["change"], suppressUpdate: false },
      {
        name: "marathon",
        events: ["deploymentRollbackSuccess", "deploymentRollbackError"],
        suppressUpdate: true
      }
    ];

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    }, this);
  }

  onMarathonStoreDeploymentRollbackSuccess(data) {
    const { deploymentToRollback } = this.state;
    if (
      deploymentToRollback != null &&
      deploymentToRollback.getId() === data.originalDeploymentID
    ) {
      this.setState({
        awaitingRevertDeploymentResponse: false,
        deploymentToRollback: null,
        deploymentRollbackError: null
      });
    }
  }

  onMarathonStoreDeploymentRollbackError(data) {
    const { deploymentToRollback } = this.state;
    if (
      deploymentToRollback != null &&
      deploymentToRollback.getId() === data.originalDeploymentID
    ) {
      this.setState({
        awaitingRevertDeploymentResponse: false,
        deploymentRollbackError: data.error
      });
    }
  }

  handleActionSelect(deployment, action) {
    if (action.id === "rollback") {
      this.handleRollbackClick(deployment);
    }
  }

  handleRollbackClick(deployment) {
    this.setState({ deploymentToRollback: deployment });
  }

  handleRollbackCancel() {
    this.setState({
      awaitingRevertDeploymentResponse: false,
      deploymentToRollback: null,
      deploymentRollbackError: null
    });
  }

  handleRollbackConfirm() {
    const { deploymentToRollback } = this.state;

    if (deploymentToRollback != null) {
      this.setState({ awaitingRevertDeploymentResponse: true });
      MarathonActions.revertDeployment(deploymentToRollback.getId());

      this.props.onClose();
    }
  }

  getChildTableData(deployment) {
    return [
      ...deployment.getAffectedServices().map(function(service) {
        service.deployment = deployment;
        service.isStale = false;

        return service;
      }),
      ...deployment.getStaleServiceIds().map(function(serviceID) {
        return {
          deployment,
          serviceID,
          isStale: true
        };
      })
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.id} />
        <col className={columnClasses.startTime} />
        <col className={columnClasses.status} />
        <col className={columnClasses.action} />
      </colgroup>
    );
  }

  getColumns() {
    return [
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "id",
        render: this.renderAffectedServices
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "startTime",
        render: this.renderStartTime
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "status",
        render: this.renderStatus
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "action",
        render: this.renderActionsMenu,
        sortable: false
      }
    ];
  }

  getRollbackModalText(deploymentToRollback) {
    const serviceNames = deploymentToRollback
      .getAffectedServices()
      .map(function(service) {
        return StringUtil.capitalize(service.getName());
      });
    const listOfServiceNames = StringUtil.humanizeArray(serviceNames);
    const serviceCount = serviceNames.length;

    const service = StringUtil.pluralize("service", serviceCount);
    const its = serviceCount === 1 ? "its" : "their";
    const version = StringUtil.pluralize("version", serviceCount);

    if (deploymentToRollback.isStarting()) {
      return `This will stop the current deployment of ${listOfServiceNames} and
              start a new deployment to ${UserActions.DELETE} the affected
              ${service}.`;
    }

    return `This will stop the current deployment of ${listOfServiceNames} and
            start a new deployment to revert the affected ${service} to ${its}
            previous ${version}.`;
  }

  getServiceDisplayPath(service) {
    // Remove the service's name from the end of the path.
    const servicePath = service
      .getId()
      .replace(new RegExp(`/${service.getName()}$`), "");

    return (
      <a
        href={`#/services/overview/${encodeURIComponent(servicePath)}`}
        onClick={this.props.onClose}
      >
        {`services${servicePath}`}

    );
  }

  getTableData(deploymentsItems) {
    return deploymentsItems.map(deployment => {
      deployment.children = this.getChildTableData(deployment);

      return deployment;
    });
  }

  renderActionsMenu(prop, deployment, rowOptions) {
    if (rowOptions.isParent && deployment != null) {
      let actionText = "Rollback";
      if (deployment.isStarting()) {
        actionText = `${actionText} & ${StringUtil.capitalize(UserActions.DELETE)}`;
      }

      const dropdownItems = [
        {
          className: "hidden",
          id: "default",
          html: "",
          selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
        },
        {
          id: "rollback",
          html: (

              {actionText}

          )
        }
      ];

      return (
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={dropdownItems}
          persistentID="default"
          onItemSelection={this.handleActionSelect.bind(this, deployment)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      );
    }

    return null;
  }

  renderAffectedServices(prop, item, rowOptions) {
    // item is an instance of Deployment
    if (rowOptions.isParent) {
      let classes = null;

      if (item.children && item.children.length > 0) {
        classes = classNames("expanding-table-primary-cell is-expandable", {
          "is-expanded": rowOptions.isExpanded
        });
      }

      return (

          <CollapsingString string={item.getId()} />

      );
    }

    if (item.isStale) {
      return (

          <div className="table-cell-flex-box" key={`stale_${item.serviceID}`}>

              <Image src={defaultServiceImage} />

              {item.serviceID}

      );
    }

    // item is an instance of Service
    const id = encodeURIComponent(item.getId());
    const image = item.getImages()["icon-small"];

    return (

           {
              hashHistory.push(`/services/detail/${id}`);
            }}
          >

              <Image fallbackSrc={defaultServiceImage} src={image} />

              {item.getName()}

          {this.getServiceDisplayPath(item)}

    );
  }

  renderEmpty() {
    return (
      <AlertPanel>
        <AlertPanelHeader>No active deployments</AlertPanelHeader>

Active deployments will be shown here.

      </AlertPanel>
    );
  }

  renderLoading() {
    return <Loader />;
  }

  renderRollbackModal() {
    const {
      awaitingRevertDeploymentResponse,
      deploymentToRollback,
      deploymentRollbackError
    } = this.state;

    const heading = (
      <ModalHeading>
        Are you sure?
      </ModalHeading>
    );

    if (deploymentToRollback != null) {
      return (
        <Confirm
          closeByBackdropClick={true}
          disabled={awaitingRevertDeploymentResponse}
          header={heading}
          onClose={this.handleRollbackCancel}
          leftButtonCallback={this.handleRollbackCancel}
          leftButtonText="Cancel"
          rightButtonClassName="button button-danger"
          rightButtonCallback={this.handleRollbackConfirm}
          rightButtonText="Continue Rollback"
          showHeader={true}
        >

{this.getRollbackModalText(deploymentToRollback)}

            {this.renderRollbackError(deploymentRollbackError)}

        </Confirm>
      );
    }
  }

  renderStartTime(prop, deployment, rowOptions) {
    if (rowOptions.isParent) {
      return (
        <TimeAgo
          className="deployment-start-time"
          time={deployment.getStartTime()}
        />
      );
    }

    return null;
  }

  renderStatus(prop, item, rowOptions) {
    if (rowOptions.isParent) {
      const currentStep = item.getCurrentStep();
      const totalSteps = item.getTotalSteps();

      return this.renderStatusBar(currentStep, totalSteps);
    }

    let currentActions = {};
    if (
      item.deployment.currentActions &&
      item.deployment.currentActions.length > 0
    ) {
      currentActions = item.deployment.currentActions.reduce(function(
        memo,
        action
      ) {
        memo[action.app] = action.action;

        return memo;
      }, {});
    }

    let statusText = item.getStatus();

    if (currentActions[item.id] != null) {
      statusText = currentActions[item.id];
    }

    return statusText;
  }

  renderStatusBar(currentStep, totalSteps) {
    const data = [
      { className: "color-4", value: currentStep - 1 },
      { className: "staged", value: 1 },
      { className: "", value: totalSteps - currentStep }
    ];

    return <StatusBar className="status-bar--large" data={data} />;
  }

  renderTable(deploymentsItems) {
    return (

        <ExpandingTable
          childRowClassName="expanding-table-child"
          className="deployments-table expanding-table table table-hover table-borderless-outer table-borderless-inner-columns flush-bottom"
          columns={this.getColumns()}
          colGroup={this.getColGroup()}
          data={this.getTableData(deploymentsItems)}
          expandRowsByDefault={true}
        />
        {this.renderRollbackModal()}

    );
  }

  renderRollbackError(deploymentRollbackError) {
    if (deploymentRollbackError != null) {
      return (

          {deploymentRollbackError}

      );
    }
  }

  render() {
    let content = null;
    const deployments = DCOSStore.deploymentsList.getItems();
    const { intl, isOpen, onClose } = this.props;
    const loading = !DCOSStore.serviceDataReceived;

    if (loading) {
      content = this.renderLoading();
    } else if (deployments.length === 0) {
      content = this.renderEmpty();
    } else if (isOpen) {
      content = this.renderTable(deployments);
    }

    const footer = (

        <button className="button" onClick={onClose}>Close</button>

    );
    const heading = (
      <ModalHeading>
        {intl.formatMessage(
          {
            id: "SERVICES.DEPLOYMENT_MODAL_HEADING"
          },
          {
            deploymentsCount: deployments.length
          }
        )}
      </ModalHeading>
    );

    return (
      <Modal
        modalClass="modal modal-large modal--deployments"
        footer={footer}
        header={heading}
        onClose={onClose}
        open={isOpen}
        showFooter={true}
        showHeader={true}
      >
        {content}
      </Modal>
    );
  }
}

module.exports = injectIntl(DeploymentsModal);
<commit_msg>feature(DeploymentsModal): Hide actions dropdown if deployment contains SDK service<commit_after>import classNames from "classnames";
import { Confirm, Dropdown, Modal } from "reactjs-components";
import { hashHistory } from "react-router";
import { injectIntl } from "react-intl";
import mixin from "reactjs-mixin";
/* eslint-disable no-unused-vars */
import React from "react";
/* eslint-enable no-unused-vars */
import { StoreMixin } from "mesosphere-shared-reactjs";

import AlertPanel from "#SRC/js/components/AlertPanel";
import AlertPanelHeader from "#SRC/js/components/AlertPanelHeader";
import CollapsingString from "#SRC/js/components/CollapsingString";
import DCOSStore from "#SRC/js/stores/DCOSStore";
import ExpandingTable from "#SRC/js/components/ExpandingTable";
import Icon from "#SRC/js/components/Icon";
import Image from "#SRC/js/components/Image";
import Loader from "#SRC/js/components/Loader";
import ModalHeading from "#SRC/js/components/modals/ModalHeading";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import ServiceUtil from "#SRC/js/utils/ServiceUtil";
import StatusBar from "#SRC/js/components/StatusBar";
import StringUtil from "#SRC/js/utils/StringUtil";
import TimeAgo from "#SRC/js/components/TimeAgo";
import UserActions from "#SRC/js/constants/UserActions";

import defaultServiceImage from "../../img/icon-service-default-small@2x.png";
import MarathonActions from "../events/MarathonActions";

const columnClasses = {
  id: "deployments-table-column-id",
  startTime: "deployments-table-column-start-time",
  status: "deployments-table-column-status",
  action: "deployments-table-column-actions"
};
const columnHeadings = ResourceTableUtil.renderHeading({
  id: "Affected Services",
  startTime: "Started",
  status: "Status",
  action: null
});
const METHODS_TO_BIND = [
  "renderAffectedServices",
  "renderStartTime",
  "renderStatus",
  "renderActionsMenu",
  "handleRollbackClick",
  "handleRollbackCancel",
  "handleRollbackConfirm",
  "onMarathonStoreDeploymentRollbackSuccess",
  "onMarathonStoreDeploymentRollbackError"
];

// collapsing columns are tightly coupled to the left-align caret property;
// this wrapper allows ordinary columns to collapse.
function columnClassNameGetter(prop, sortBy, row) {
  return classNames(
    columnClasses[prop],
    ResourceTableUtil.getClassName(prop, sortBy, row)
  );
}

class DeploymentsModal extends mixin(StoreMixin) {
  constructor() {
    super(...arguments);

    this.state = {};
    this.store_listeners = [
      { name: "dcos", events: ["change"], suppressUpdate: false },
      {
        name: "marathon",
        events: ["deploymentRollbackSuccess", "deploymentRollbackError"],
        suppressUpdate: true
      }
    ];

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    }, this);
  }

  onMarathonStoreDeploymentRollbackSuccess(data) {
    const { deploymentToRollback } = this.state;
    if (
      deploymentToRollback != null &&
      deploymentToRollback.getId() === data.originalDeploymentID
    ) {
      this.setState({
        awaitingRevertDeploymentResponse: false,
        deploymentToRollback: null,
        deploymentRollbackError: null
      });
    }
  }

  onMarathonStoreDeploymentRollbackError(data) {
    const { deploymentToRollback } = this.state;
    if (
      deploymentToRollback != null &&
      deploymentToRollback.getId() === data.originalDeploymentID
    ) {
      this.setState({
        awaitingRevertDeploymentResponse: false,
        deploymentRollbackError: data.error
      });
    }
  }

  handleActionSelect(deployment, action) {
    if (action.id === "rollback") {
      this.handleRollbackClick(deployment);
    }
  }

  handleRollbackClick(deployment) {
    this.setState({ deploymentToRollback: deployment });
  }

  handleRollbackCancel() {
    this.setState({
      awaitingRevertDeploymentResponse: false,
      deploymentToRollback: null,
      deploymentRollbackError: null
    });
  }

  handleRollbackConfirm() {
    const { deploymentToRollback } = this.state;

    if (deploymentToRollback != null) {
      this.setState({ awaitingRevertDeploymentResponse: true });
      MarathonActions.revertDeployment(deploymentToRollback.getId());

      this.props.onClose();
    }
  }

  getChildTableData(deployment) {
    return [
      ...deployment.getAffectedServices().map(function(service) {
        service.deployment = deployment;
        service.isStale = false;

        return service;
      }),
      ...deployment.getStaleServiceIds().map(function(serviceID) {
        return {
          deployment,
          serviceID,
          isStale: true
        };
      })
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.id} />
        <col className={columnClasses.startTime} />
        <col className={columnClasses.status} />
        <col className={columnClasses.action} />
      </colgroup>
    );
  }

  getColumns() {
    return [
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "id",
        render: this.renderAffectedServices
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "startTime",
        render: this.renderStartTime
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "status",
        render: this.renderStatus
      },
      {
        className: columnClassNameGetter,
        heading: columnHeadings,
        prop: "action",
        render: this.renderActionsMenu,
        sortable: false
      }
    ];
  }

  getRollbackModalText(deploymentToRollback) {
    const serviceNames = deploymentToRollback
      .getAffectedServices()
      .map(function(service) {
        return StringUtil.capitalize(service.getName());
      });
    const listOfServiceNames = StringUtil.humanizeArray(serviceNames);
    const serviceCount = serviceNames.length;

    const service = StringUtil.pluralize("service", serviceCount);
    const its = serviceCount === 1 ? "its" : "their";
    const version = StringUtil.pluralize("version", serviceCount);

    if (deploymentToRollback.isStarting()) {
      return `This will stop the current deployment of ${listOfServiceNames} and
              start a new deployment to ${UserActions.DELETE} the affected
              ${service}.`;
    }

    return `This will stop the current deployment of ${listOfServiceNames} and
            start a new deployment to revert the affected ${service} to ${its}
            previous ${version}.`;
  }

  getServiceDisplayPath(service) {
    // Remove the service's name from the end of the path.
    const servicePath = service
      .getId()
      .replace(new RegExp(`/${service.getName()}$`), "");

    return (
      <a
        href={`#/services/overview/${encodeURIComponent(servicePath)}`}
        onClick={this.props.onClose}
      >
        {`services${servicePath}`}

    );
  }

  getTableData(deploymentsItems) {
    return deploymentsItems.map(deployment => {
      deployment.children = this.getChildTableData(deployment);

      return deployment;
    });
  }

  renderActionsMenu(prop, deployment, rowOptions) {
    const { children = [] } = deployment;

    const doesDeploymentContainSDKService = children.some(function(service) {
      return ServiceUtil.isSDKService(service);
    });

    if (
      !doesDeploymentContainSDKService &&
      rowOptions.isParent &&
      deployment != null
    ) {
      let actionText = "Rollback";
      if (deployment.isStarting()) {
        actionText = `${actionText} & ${StringUtil.capitalize(UserActions.DELETE)}`;
      }

      const dropdownItems = [
        {
          className: "hidden",
          id: "default",
          html: "",
          selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
        },
        {
          id: "rollback",
          html: (

              {actionText}

          )
        }
      ];

      return (
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={dropdownItems}
          persistentID="default"
          onItemSelection={this.handleActionSelect.bind(this, deployment)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      );
    }

    return null;
  }

  renderAffectedServices(prop, item, rowOptions) {
    // item is an instance of Deployment
    if (rowOptions.isParent) {
      let classes = null;

      if (item.children && item.children.length > 0) {
        classes = classNames("expanding-table-primary-cell is-expandable", {
          "is-expanded": rowOptions.isExpanded
        });
      }

      return (

          <CollapsingString string={item.getId()} />

      );
    }

    if (item.isStale) {
      return (

          <div className="table-cell-flex-box" key={`stale_${item.serviceID}`}>

              <Image src={defaultServiceImage} />

              {item.serviceID}

      );
    }

    // item is an instance of Service
    const id = encodeURIComponent(item.getId());
    const image = item.getImages()["icon-small"];

    return (

           {
              hashHistory.push(`/services/detail/${id}`);
            }}
          >

              <Image fallbackSrc={defaultServiceImage} src={image} />

              {item.getName()}

          {this.getServiceDisplayPath(item)}

    );
  }

  renderEmpty() {
    return (
      <AlertPanel>
        <AlertPanelHeader>No active deployments</AlertPanelHeader>

Active deployments will be shown here.

      </AlertPanel>
    );
  }

  renderLoading() {
    return <Loader />;
  }

  renderRollbackModal() {
    const {
      awaitingRevertDeploymentResponse,
      deploymentToRollback,
      deploymentRollbackError
    } = this.state;

    const heading = (
      <ModalHeading>
        Are you sure?
      </ModalHeading>
    );

    if (deploymentToRollback != null) {
      return (
        <Confirm
          closeByBackdropClick={true}
          disabled={awaitingRevertDeploymentResponse}
          header={heading}
          onClose={this.handleRollbackCancel}
          leftButtonCallback={this.handleRollbackCancel}
          leftButtonText="Cancel"
          rightButtonClassName="button button-danger"
          rightButtonCallback={this.handleRollbackConfirm}
          rightButtonText="Continue Rollback"
          showHeader={true}
        >

{this.getRollbackModalText(deploymentToRollback)}

            {this.renderRollbackError(deploymentRollbackError)}

        </Confirm>
      );
    }
  }

  renderStartTime(prop, deployment, rowOptions) {
    if (rowOptions.isParent) {
      return (
        <TimeAgo
          className="deployment-start-time"
          time={deployment.getStartTime()}
        />
      );
    }

    return null;
  }

  renderStatus(prop, item, rowOptions) {
    if (rowOptions.isParent) {
      const currentStep = item.getCurrentStep();
      const totalSteps = item.getTotalSteps();

      return this.renderStatusBar(currentStep, totalSteps);
    }

    let currentActions = {};
    if (
      item.deployment.currentActions &&
      item.deployment.currentActions.length > 0
    ) {
      currentActions = item.deployment.currentActions.reduce(function(
        memo,
        action
      ) {
        memo[action.app] = action.action;

        return memo;
      }, {});
    }

    let statusText = item.getStatus();

    if (currentActions[item.id] != null) {
      statusText = currentActions[item.id];
    }

    return statusText;
  }

  renderStatusBar(currentStep, totalSteps) {
    const data = [
      { className: "color-4", value: currentStep - 1 },
      { className: "staged", value: 1 },
      { className: "", value: totalSteps - currentStep }
    ];

    return <StatusBar className="status-bar--large" data={data} />;
  }

  renderTable(deploymentsItems) {
    return (

        <ExpandingTable
          childRowClassName="expanding-table-child"
          className="deployments-table expanding-table table table-hover table-borderless-outer table-borderless-inner-columns flush-bottom"
          columns={this.getColumns()}
          colGroup={this.getColGroup()}
          data={this.getTableData(deploymentsItems)}
          expandRowsByDefault={true}
        />
        {this.renderRollbackModal()}

    );
  }

  renderRollbackError(deploymentRollbackError) {
    if (deploymentRollbackError != null) {
      return (

          {deploymentRollbackError}

      );
    }
  }

  render() {
    let content = null;
    const deployments = DCOSStore.deploymentsList.getItems();
    const { intl, isOpen, onClose } = this.props;
    const loading = !DCOSStore.serviceDataReceived;

    if (loading) {
      content = this.renderLoading();
    } else if (deployments.length === 0) {
      content = this.renderEmpty();
    } else if (isOpen) {
      content = this.renderTable(deployments);
    }

    const footer = (

        <button className="button" onClick={onClose}>Close</button>

    );
    const heading = (
      <ModalHeading>
        {intl.formatMessage(
          {
            id: "SERVICES.DEPLOYMENT_MODAL_HEADING"
          },
          {
            deploymentsCount: deployments.length
          }
        )}
      </ModalHeading>
    );

    return (
      <Modal
        modalClass="modal modal-large modal--deployments"
        footer={footer}
        header={heading}
        onClose={onClose}
        open={isOpen}
        showFooter={true}
        showHeader={true}
      >
        {content}
      </Modal>
    );
  }
}

module.exports = injectIntl(DeploymentsModal);
<|endoftext|>


<commit_before>import styled from 'styled-components'
import theme from '../../styles/theme'
import GaramondRegular from '../../../static/font/Garamond-Regular.ttf'
import GaramondItalic from '../../../static/font/Garamond-Italic.ttf'
import GaramondBold from '../../../static/font/Garamond-Bold.ttf'
import GaramondBoldItalic from '../../../static/font/Garamond-BoldItalic.ttf'
import mediaQuery from '../../styles/mediaQuery'

export const LayoutWrapper = styled.section`
  min-height: 100vh;
  max-width: ${theme.large};
  margin: 0 auto;
  padding: 1.6rem 1.6rem 1.2rem 1.6rem;

  ${mediaQuery.lessThan('header')`
    padding: 0.8rem 1.6rem 1.2rem 1.6rem;
  `}

  /* Font-faces */
  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondRegular}) format('truetype');
    font-weight: normal;
    font-style: normal;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondItalic}) format('truetype');
    font-weight: normal;
    font-style: italic;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondBold}) format('truetype');
    font-weight: bold;
    font-style: normal;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondBoldItalic}) format('truetype');
    font-weight: bold;
    font-style: italic;
  }
`

export const LayoutMain = styled.main``
<commit_msg>Adjust padding-top on Layout<commit_after>import styled from 'styled-components'
import theme from '../../styles/theme'
import GaramondRegular from '../../../static/font/Garamond-Regular.ttf'
import GaramondItalic from '../../../static/font/Garamond-Italic.ttf'
import GaramondBold from '../../../static/font/Garamond-Bold.ttf'
import GaramondBoldItalic from '../../../static/font/Garamond-BoldItalic.ttf'
import mediaQuery from '../../styles/mediaQuery'

export const LayoutWrapper = styled.section`
  min-height: 100vh;
  max-width: ${theme.large};
  margin: 0 auto;
  padding: 1.6rem 1.6rem 1.2rem 1.6rem;

  ${mediaQuery.lessThan('header')`
    padding: 0 1.6rem 1.2rem 1.6rem;
  `}

  /* Font-faces */
  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondRegular}) format('truetype');
    font-weight: normal;
    font-style: normal;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondItalic}) format('truetype');
    font-weight: normal;
    font-style: italic;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondBold}) format('truetype');
    font-weight: bold;
    font-style: normal;
  }

  @font-face {
    font-family: 'Garamond';
    src: url(${GaramondBoldItalic}) format('truetype');
    font-weight: bold;
    font-style: italic;
  }
`

export const LayoutMain = styled.main``
<|endoftext|>


<commit_before>(function () {
    'use strict';

    const directive = (Base, $compile) => {

        return {
            scope: true,
            require: {
                select: '^wSelect'
            },
            transclude: true,
            template: '

',
            link: ($scope, $element, $attrs, { select }) => {

                class Option extends Base {

                    constructor() {
                        super($scope);
                        /**
                         * @type {boolean}
                         * @private
                         */
                        this._isActive = false;
                        /**
                         * @type {Select}
                         */
                        this.select = select;
                        /**
                         * @type {string|number}
                         */
                        this.value = $attrs.value;

                        if ($attrs.optionType) {
                            switch ($attrs.optionType) {
                                case 'boolean':
                                    this.value = this.value === 'true';
                                    break;
                                case 'number':
                                    this.value = Number(this.value);
                                    break;
                                default:
                                    throw new Error('Wrong option type!');
                            }
                        }

                        /**
                         * @type {Select}
                         */
                        this.select.registerOption(this);

                        if (tsUtils.isEmpty(this.value)) {
                            throw new Error('Empty value of option!');
                        }

                        this._setHandlers();
                    }

                    /**
                     * @return {JQuery}
                     */
                    getContent() {
                        return $compile(this._getContentHTML())($scope);
                    }

                    onClick() {
                        this.select.setActive(this);
                        $scope.$apply();
                    }

                    setActive(active) {
                        this._isActive = active;
                        const index = this.select.getOptionIndex(this);
                        $element.css('order', active ? -index : 0).toggleClass('active', active);
                    }

                    /**
                     * @return {string}
                     * @private
                     */
                    _getContentHTML() {
                        return `<div class="title-content">${$element.find('.option').html()}</div>`;
                    }

                    /**
                     * @private
                     */
                    _setHandlers() {
                        $element.on('click', this.onClick.bind(this));
                    }

                }

                new Option();
            }
        };
    };

    directive.$inject = ['Base', '$compile'];

    angular.module('app.ui').directive('wOption', directive);

})();

<commit_msg>Added a commentary<commit_after>(function () {
    'use strict';

    const directive = (Base, $compile) => {

        return {
            scope: true,
            require: {
                select: '^wSelect'
            },
            transclude: true,
            template: '

',
            link: ($scope, $element, $attrs, { select }) => {

                class Option extends Base {

                    constructor() {
                        super($scope);
                        /**
                         * @type {boolean}
                         * @private
                         */
                        this._isActive = false;
                        /**
                         * @type {Select}
                         */
                        this.select = select;
                        /**
                         * @type {string|number}
                         */
                        this.value = $attrs.value;

                        if ($attrs.optionType) {
                            switch ($attrs.optionType) {
                                case 'boolean':
                                    this.value = this.value === 'true';
                                    break;
                                case 'number':
                                    this.value = Number(this.value);
                                    break;
                                default:
                                    throw new Error('Wrong option type!');
                            }
                        }

                        /**
                         * @type {Select}
                         */
                        this.select.registerOption(this);

                        if (tsUtils.isEmpty(this.value)) {
                            throw new Error('Empty value of option!');
                        }

                        this._setHandlers();
                    }

                    /**
                     * @return {JQuery}
                     */
                    getContent() {
                        return $compile(this._getContentHTML())($scope);
                    }

                    onClick() {
                        this.select.setActive(this);
                        $scope.$apply();
                    }

                    setActive(active) {
                        this._isActive = active;
                        const index = this.select.getOptionIndex(this);
                        // Get the active option to the top of the dropdown list
                        $element.css('order', active ? -index : 0).toggleClass('active', active);
                    }

                    /**
                     * @return {string}
                     * @private
                     */
                    _getContentHTML() {
                        return `<div class="title-content">${$element.find('.option').html()}</div>`;
                    }

                    /**
                     * @private
                     */
                    _setHandlers() {
                        $element.on('click', this.onClick.bind(this));
                    }

                }

                new Option();
            }
        };
    };

    directive.$inject = ['Base', '$compile'];

    angular.module('app.ui').directive('wOption', directive);

})();

<|endoftext|>