<commit_before>angular.module('documentationEditorApp', [])
  .config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode({ enabled: true, requireBase: false });
  }])
  .controller('EditorController', ['$scope', '$http', '$window', '$location', function($scope, $http, $window, $location) {
    $scope.sections = [];
    $scope.undoRedo = [];
    $scope.id = 0;
    $scope.source = '';

    $scope.nextID = 0;
    function getNextID() {
      return $scope.nextID++;
    }

    function parse(source) {
      var lines = source.replace(/\n{3,}/g, "\n\n").split("\n");
      var currentBlock = null, blockContent = '';
      var sections = [];
      for (var i = 0; i < lines.length; ++i) {
        var line = lines[i];
        if (line.indexOf('[block:') === 0) {
          currentBlock = line.substring(7, line.length - 1);
        } else if (line.indexOf('[/block]') === 0) {
          sections.push({
            id: getNextID(),
            type: currentBlock,
            content: JSON.parse(blockContent)
          });
          currentBlock = null;
          blockContent = '';
        } else if (currentBlock != null) {
          blockContent = blockContent + line + '\n';
        } else {
          if (sections.length > 0 && sections[sections.length - 1].type === 'text') {
            sections[sections.length - 1].content = sections[sections.length - 1].content + "\n" + line;
          } else if (line) {
            sections.push({
              id: getNextID(),
              type: 'text',
              content: line
            });
          }
        }
      }
      return sections;
    }

    function serialize(sections) {
      var data = [];
      $.each(sections, function(i, section) {
        if (section.type === 'text') {
          data.push(section.content + "\n");
        } else {
          data.push('[block:' + section.type + ']');
          data.push(JSON.stringify(section.content));
          data.push("[/block]\n");
        }
      });
      return data.join("\n");
    }

    $scope.init = function(id, path) {
      $scope.id = id;
      $scope.path = path;

      $http.get($scope.path + '/source/' + id).then(function(content) {
        $scope.source = content.data;
        $scope.sections = parse($scope.source);
      });
    };

    $scope.rawView =  false;
    $scope.toggleView = function($event) {
      $event.preventDefault();
      $scope.undoRedo = [];
      if ($scope.rawView) {
        $scope.sections = parse(this.source);
      } else {
        $scope.source = serialize($scope.sections);
      }
      $scope.rawView = !$scope.rawView;
    };

    $scope.undo = function($event) {
      $event.preventDefault();
      if ($scope.undoRedo.length === 0) {
        return;
      }
      var o = $scope.undoRedo.pop();
      if (o.type === 'addSection') {
        $scope.sections = $.grep($scope.sections, function(e) { return e.id !== o.data.obj.id });
      } else if (o.type === 'deleteSection') {
        $scope.sections = o.data.sections;
      } else if (o.type === 'deleteLanguage' || o.type === 'addLanguage') {
        var index = getIndex(o.data.id);
        var obj = {
          id: $scope.sections[index].id,
          type: 'code',
          content: {
            codes: o.data.codes
          }
        };
        $scope.sections = [].concat($scope.sections.slice(0, index), [obj], $scope.sections.slice(index + 1));
      } else if (o.type === 'deleteColumn' || o.type === 'addColumn' || o.type === 'addRow' || o.type === 'deleteRow') {
        var index = getIndex(o.data.id);
        $scope.sections[index] = {
          id: $scope.sections[index].id,
          type: 'parameters',
          content: o.data.content
        };
      }
    }

    function getIndex(id) {
      for (var i = 0; i < $scope.sections.length; ++i) {
        if ($scope.sections[i].id === id) {
          return i;
        }
      }
      return -1;
    }

    function add(id, obj) {
      $scope.undoRedo.push({
        type: 'addSection',
        data: {
          id: id,
          obj: obj
        }
      });
      var index = getIndex(id);
      $scope.sections = [].concat($scope.sections.slice(0, index + 1), [obj], $scope.sections.slice(index + 1));
    }

    $scope.addText = function($event, id) {
      $event.preventDefault();
      add(id, { id: getNextID(), type: 'text', content: 'FIXME' });
    };

    $scope.addCallout = function($event, type, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'callout',
        content: {
          type: type,
          body: 'FIXME'
        }
      });
    };

    $scope.addCode = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'code',
        content: {
          codes: [
            {
              language: 'fixme',
              code: '// FIXME'
            }
          ]
        }
      });
    };

    $scope.getIntegerIterator = function(v) {
      var a = [];
      for (var i = 0; i < v; ++i) {
        a.push(i);
      }
      return a;
    };

    $scope.deleteSection = function($event, id) {
      $event.preventDefault();
      $scope.undoRedo.push({
        type: 'deleteSection',
        data: {
          sections: $scope.sections
        }
      });
      $scope.sections = $.grep($scope.sections, function(e) { return e.id !== id });
    };

    $scope.deleteLanguage = function($event, id, languageIndex) {
      $event.preventDefault();
      var index = getIndex(id);
      $scope.undoRedo.push({
        type: 'deleteLanguage',
        data: {
          id: id,
          codes: $scope.sections[index].content.codes
        }
      });
      $scope.sections[index].content.codes = $.grep($scope.sections[index].content.codes, function(e, i) { return i !== languageIndex; });
    };

    $scope.addLanguage = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      $scope.undoRedo.push({
        type: 'addLanguage',
        data: {
          id: id,
          codes: $scope.sections[index].content.codes
        }
      });
      $scope.sections[index].content.codes = [].concat($scope.sections[index].content.codes, [{ language: 'fixme', code: '// FIXME' }]);
    };

    $scope.addImage = function($event, id) {
      $event.preventDefault();
      var url = prompt("Image URL (absolute or from after /assets/)", "algolia256x80.png");
      if (url) {
        add(id, {
          type: 'image',
          content: {
            images: [
              {
                image: [url]
              }
            ]
          }
        });
      }
    };

    $scope.addColumn = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'addColumn',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      var col = section.content.cols;
      section.content.data['h-' + col] = section.content.data['h-' + col] || '';
      section.content.cols = section.content.cols + 1;
      for (var i = 0; i < section.content.rows; ++i) {
        section.content.data[i + '-' + col] = section.content.data[i + '-' + col] || '';
      }
    };

    $scope.deleteColumn = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'deleteColumn',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      section.content.cols = section.content.cols - 1;
    };

    $scope.addRow = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'addRow',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      var rows = section.content.rows;
      section.content.rows = section.content.rows + 1;
      for (var i = 0; i < section.content.cols; ++i) {
        section.content.data[rows + '-' + i] = section.content.data[rows + '-' + i] || '';
      }
    };

    $scope.deleteRow = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'deleteRow',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      section.content.rows = section.content.rows - 1;
    };

    $scope.addIf = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'if',
        content: {
          condition: 'FIXME'
        }
      });
    };

    $scope.addEndIf = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'endif',
        content: {}
      });
    };

    $scope.addTable = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'parameters',
        content: {
          cols: 2,
          rows: 2,
          data: {
            'h-0': 'FIXME 1',
            'h-1': 'FIXME 2',
            '0-0': '',
            '0-1': '',
            '1-0': '',
            '1-1': ''
          }
        }
      });
    };

    function save(preview) {
      return $http.post($scope.path + '/source/' + $scope.id, { data: serialize($scope.sections), preview: preview });
    }

    $scope.preview = function($event) {
      $event.preventDefault();
      save(true).then(function(content) {
        $window.open($scope.path + '/preview/' + content.data.id);
        $location.path($scope.path + '/edit/' + content.data.id).replace();
      });
    };

    $scope.publish = function($event) {
      $event.preventDefault();

      if (confirm('Are you sure you want to publish this page?')) {
        save(false).then(function(content) {
          $window.location.href = $scope.path + '/' + content.data.slug;
        });
      }
    }

  }]).directive('contenteditable', ['$document', function($document) {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ngModel) {
        function read() {
          var text = element.text().replace(/<div>/g, '').replace(/<\/div>/g, "\n").replace(/<br>/g, "\n").replace(/\n{3,}/g, "\n\n");
          ngModel.$setViewValue(text);
        }

        ngModel.$render = function() {
          element.text(ngModel.$viewValue || "");
        };

        element.bind("blur keyup change", function() {
          scope.$apply(read);
        });

        // force every copy/paste to be plain/text
        element.bind('paste', function(e) {
          e.preventDefault();
          var text = (e.originalEvent || e).clipboardData.getData('text/plain') || '';
          $document[0].execCommand("insertHTML", false, text);
        });
      }
    };
  }])
;
<commit_msg>Do not rewrite links automatically<commit_after>angular.module('documentationEditorApp', [])
  .config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode({ enabled: true, requireBase: false, rewriteLinks: false });
  }])
  .controller('EditorController', ['$scope', '$http', '$window', '$location', function($scope, $http, $window, $location) {
    $scope.sections = [];
    $scope.undoRedo = [];
    $scope.id = 0;
    $scope.source = '';

    $scope.nextID = 0;
    function getNextID() {
      return $scope.nextID++;
    }

    function parse(source) {
      var lines = source.replace(/\n{3,}/g, "\n\n").split("\n");
      var currentBlock = null, blockContent = '';
      var sections = [];
      for (var i = 0; i < lines.length; ++i) {
        var line = lines[i];
        if (line.indexOf('[block:') === 0) {
          currentBlock = line.substring(7, line.length - 1);
        } else if (line.indexOf('[/block]') === 0) {
          sections.push({
            id: getNextID(),
            type: currentBlock,
            content: JSON.parse(blockContent)
          });
          currentBlock = null;
          blockContent = '';
        } else if (currentBlock != null) {
          blockContent = blockContent + line + '\n';
        } else {
          if (sections.length > 0 && sections[sections.length - 1].type === 'text') {
            sections[sections.length - 1].content = sections[sections.length - 1].content + "\n" + line;
          } else if (line) {
            sections.push({
              id: getNextID(),
              type: 'text',
              content: line
            });
          }
        }
      }
      return sections;
    }

    function serialize(sections) {
      var data = [];
      $.each(sections, function(i, section) {
        if (section.type === 'text') {
          data.push(section.content + "\n");
        } else {
          data.push('[block:' + section.type + ']');
          data.push(JSON.stringify(section.content));
          data.push("[/block]\n");
        }
      });
      return data.join("\n");
    }

    $scope.init = function(id, path) {
      $scope.id = id;
      $scope.path = path;

      $http.get($scope.path + '/source/' + id).then(function(content) {
        $scope.source = content.data;
        $scope.sections = parse($scope.source);
      });
    };

    $scope.rawView =  false;
    $scope.toggleView = function($event) {
      $event.preventDefault();
      $scope.undoRedo = [];
      if ($scope.rawView) {
        $scope.sections = parse(this.source);
      } else {
        $scope.source = serialize($scope.sections);
      }
      $scope.rawView = !$scope.rawView;
    };

    $scope.undo = function($event) {
      $event.preventDefault();
      if ($scope.undoRedo.length === 0) {
        return;
      }
      var o = $scope.undoRedo.pop();
      if (o.type === 'addSection') {
        $scope.sections = $.grep($scope.sections, function(e) { return e.id !== o.data.obj.id });
      } else if (o.type === 'deleteSection') {
        $scope.sections = o.data.sections;
      } else if (o.type === 'deleteLanguage' || o.type === 'addLanguage') {
        var index = getIndex(o.data.id);
        var obj = {
          id: $scope.sections[index].id,
          type: 'code',
          content: {
            codes: o.data.codes
          }
        };
        $scope.sections = [].concat($scope.sections.slice(0, index), [obj], $scope.sections.slice(index + 1));
      } else if (o.type === 'deleteColumn' || o.type === 'addColumn' || o.type === 'addRow' || o.type === 'deleteRow') {
        var index = getIndex(o.data.id);
        $scope.sections[index] = {
          id: $scope.sections[index].id,
          type: 'parameters',
          content: o.data.content
        };
      }
    }

    function getIndex(id) {
      for (var i = 0; i < $scope.sections.length; ++i) {
        if ($scope.sections[i].id === id) {
          return i;
        }
      }
      return -1;
    }

    function add(id, obj) {
      $scope.undoRedo.push({
        type: 'addSection',
        data: {
          id: id,
          obj: obj
        }
      });
      var index = getIndex(id);
      $scope.sections = [].concat($scope.sections.slice(0, index + 1), [obj], $scope.sections.slice(index + 1));
    }

    $scope.addText = function($event, id) {
      $event.preventDefault();
      add(id, { id: getNextID(), type: 'text', content: 'FIXME' });
    };

    $scope.addCallout = function($event, type, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'callout',
        content: {
          type: type,
          body: 'FIXME'
        }
      });
    };

    $scope.addCode = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'code',
        content: {
          codes: [
            {
              language: 'fixme',
              code: '// FIXME'
            }
          ]
        }
      });
    };

    $scope.getIntegerIterator = function(v) {
      var a = [];
      for (var i = 0; i < v; ++i) {
        a.push(i);
      }
      return a;
    };

    $scope.deleteSection = function($event, id) {
      $event.preventDefault();
      $scope.undoRedo.push({
        type: 'deleteSection',
        data: {
          sections: $scope.sections
        }
      });
      $scope.sections = $.grep($scope.sections, function(e) { return e.id !== id });
    };

    $scope.deleteLanguage = function($event, id, languageIndex) {
      $event.preventDefault();
      var index = getIndex(id);
      $scope.undoRedo.push({
        type: 'deleteLanguage',
        data: {
          id: id,
          codes: $scope.sections[index].content.codes
        }
      });
      $scope.sections[index].content.codes = $.grep($scope.sections[index].content.codes, function(e, i) { return i !== languageIndex; });
    };

    $scope.addLanguage = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      $scope.undoRedo.push({
        type: 'addLanguage',
        data: {
          id: id,
          codes: $scope.sections[index].content.codes
        }
      });
      $scope.sections[index].content.codes = [].concat($scope.sections[index].content.codes, [{ language: 'fixme', code: '// FIXME' }]);
    };

    $scope.addImage = function($event, id) {
      $event.preventDefault();
      var url = prompt("Image URL (absolute or from after /assets/)", "algolia256x80.png");
      if (url) {
        add(id, {
          type: 'image',
          content: {
            images: [
              {
                image: [url]
              }
            ]
          }
        });
      }
    };

    $scope.addColumn = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'addColumn',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      var col = section.content.cols;
      section.content.data['h-' + col] = section.content.data['h-' + col] || '';
      section.content.cols = section.content.cols + 1;
      for (var i = 0; i < section.content.rows; ++i) {
        section.content.data[i + '-' + col] = section.content.data[i + '-' + col] || '';
      }
    };

    $scope.deleteColumn = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'deleteColumn',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      section.content.cols = section.content.cols - 1;
    };

    $scope.addRow = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'addRow',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      var rows = section.content.rows;
      section.content.rows = section.content.rows + 1;
      for (var i = 0; i < section.content.cols; ++i) {
        section.content.data[rows + '-' + i] = section.content.data[rows + '-' + i] || '';
      }
    };

    $scope.deleteRow = function($event, id) {
      $event.preventDefault();
      var index = getIndex(id);
      var section = $scope.sections[index];

      $scope.undoRedo.push({
        type: 'deleteRow',
        data: {
          id: id,
          content: {
            data: section.content.data,
            cols: section.content.cols,
            rows: section.content.rows
          }
        }
      });

      section.content.rows = section.content.rows - 1;
    };

    $scope.addIf = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'if',
        content: {
          condition: 'FIXME'
        }
      });
    };

    $scope.addEndIf = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'endif',
        content: {}
      });
    };

    $scope.addTable = function($event, id) {
      $event.preventDefault();
      add(id, {
        id: getNextID(),
        type: 'parameters',
        content: {
          cols: 2,
          rows: 2,
          data: {
            'h-0': 'FIXME 1',
            'h-1': 'FIXME 2',
            '0-0': '',
            '0-1': '',
            '1-0': '',
            '1-1': ''
          }
        }
      });
    };

    function save(preview) {
      return $http.post($scope.path + '/source/' + $scope.id, { data: serialize($scope.sections), preview: preview });
    }

    $scope.preview = function($event) {
      $event.preventDefault();
      save(true).then(function(content) {
        $window.open($scope.path + '/preview/' + content.data.id);
        $location.path($scope.path + '/edit/' + content.data.id).replace();
      });
    };

    $scope.publish = function($event) {
      $event.preventDefault();

      if (confirm('Are you sure you want to publish this page?')) {
        save(false).then(function(content) {
          $window.location.href = $scope.path + '/' + content.data.slug;
        });
      }
    }

  }]).directive('contenteditable', ['$document', function($document) {
    return {
      require: 'ngModel',
      link: function(scope, element, attrs, ngModel) {
        function read() {
          var text = element.text().replace(/<div>/g, '').replace(/<\/div>/g, "\n").replace(/<br>/g, "\n").replace(/\n{3,}/g, "\n\n");
          ngModel.$setViewValue(text);
        }

        ngModel.$render = function() {
          element.text(ngModel.$viewValue || "");
        };

        element.bind("blur keyup change", function() {
          scope.$apply(read);
        });

        // force every copy/paste to be plain/text
        element.bind('paste', function(e) {
          e.preventDefault();
          var text = (e.originalEvent || e).clipboardData.getData('text/plain') || '';
          $document[0].execCommand("insertHTML", false, text);
        });
      }
    };
  }])
;
<|endoftext|>


<commit_before>module.exports = function () {};
<commit_msg>add event signup<commit_after>'use strict';

var HoekBoom = require('hoek-boom'),
    config = require('../../config');

module.exports = function signup(request, reply) {
    var id,
        volunteers,
        self = this,
        doc,
        userToAdd;

    HoekBoom.assertBoom(request.params.id, 'ID is required', 'badRequest');
    HoekBoom.assertBoom(request.payload.name, 'Name is required', 'badRequest');
    HoekBoom.assertBoom(request.payload.email, 'Email is required', 'badRequest');
    HoekBoom.assertBoom(request.payload.phone, 'Phone number is required', 'badRequest');

    id = request.params.id;
    userToAdd = request.payload;

    self.els.get({index: config.elsEventsIndex, type: config.elsEventsType, id: id}, function(error, resp) {
        if (error && !resp.found) {
            return reply({status: 404, message: 'Not found'});
        }
        else if (error) {
            console.error(error);
            return reply({status: 400});
        }

        doc = resp._source;
        volunteers = resp._source.volunteers || [];

        if (volunteers.length >= parseInt(doc.event_details.volunteers_needed.max)) {
            return reply({status: 400, message: 'This event has filled up'});
        }

        volunteers.push(userToAdd);
        doc.volunteers = volunteers;

        self.els.index({index: config.elsEventsIndex, type: config.elsEventsType, id: id, body: doc}, function (err, resp) {
            if (err) {
                console.log("error:", err);
                return reply({status: 400, message: err});
            }

            return reply({status: 200, resp: resp});
        });
    });
};
<|endoftext|>


<commit_before>var GridStatesManager = {
  // do ajax save
  saveStates: function(gridName, type, value){
    if(gridName) {
      type = type || "order";
      vale = value || {};
      var url = "/wulin_master/grid_states_manages/save",
      data = decodeURIComponent($.param({ grid_name: encodeURIComponent(gridName),
                                        state_type: encodeURIComponent(type),
                                        state_value: value,
                                        authenticity_token: window._token }));
      $.post(url, data, function(){});
    }
  },

  // grid events
  onStateEvents: function(grid) {
    var self = this;

    // save columns width when columns resized
    grid.onColumnsResized.subscribe(function(){
      var widthJson = {};
      $.each(this.getColumns(), function(index, column){
        widthJson[column.id] = column.width;
      });
      self.saveStates(grid.name, "width", widthJson);
    });

    // save columns sorting info when columns sorted
    grid.onSort.subscribe(function(e, args){
      var loader = grid.loader, sortJson = {};
      sortJson["sortCol"] = loader.getSortColumn();
      sortJson["sortDir"] = loader.getSortDirection();
      // update sort state and save it to db
      grid.states["sort"] = {sortCol: sortJson["sortCol"], sortDir: sortJson["sortDir"]};
      self.saveStates(grid.name, "sort", sortJson);
    });

    // save columns order when columns re-ordered
    grid.onColumnsReordered.subscribe(function(e, args){
      var orderJson = {};
      $.each(this.getColumns(), function(index, column){
        orderJson[index] = column.id;
      });

      self.saveStates(grid.name, "order", orderJson);
    });

    // save filter states when input filter value
    if(grid.filterPanel) {
      grid.filterPanel.onFilterLoaded.subscribe(function(e, args){
        var filterJson = {};
        $.each(args.filterData, function(index,data){
          filterJson[data['id']] = encodeURIComponent(data['value']);
        });
        self.saveStates(grid.name, "filter", filterJson);
      });

      grid.filterPanel.onFilterPanelClosed.subscribe(function(e, args){
        $(grid.getHeaderRow()).find('input[type="text"]').val('');
        self.saveStates(grid.name, "filter", {});
      });
    }

    // save columns visibility when pick columns
    if(grid.picker){
      grid.picker.onColumnsPick.subscribe(function(e, args){
        var hiddenArr = [], hiddenJson = {}, visibilityColumns = grid.getColumns();

        // Regenerate Filter panel
        grid.filterPanel.generateFilters();

        visibilityColumns = $.map(visibilityColumns, function(n, i){
            return n.id;
        });
        allColumns = $.map(grid.columns, function(n, i){
            return n.id;
        });

        hiddenArr = $.grep(allColumns, function(n, i){
            return visibilityColumns.indexOf(n) < 0;
        });

        $.each(hiddenArr, function(index, column){
            hiddenJson[index] = column;
        });
        self.saveStates(grid.name, "visibility", hiddenArr);
      })
    }
  },

  // Restore columns order states
  restoreOrderStates: function(columns, orderStates){
	  if(!orderStates) return columns;

	  var new_columns = [], i, j, k;
    // find id column
    for(i in columns){
      if (columns[i].id == "id"){
        new_columns.push(columns[i]);
        break;
      }
    }
		// push other columns according to states
		for(j in orderStates){
		  for(k in columns) {
		    if(columns[k].id == orderStates[j]){
		      new_columns.push(columns[k]);
		      break;
		    }
		  }
		}
		// push columns that are not in the state in abritrary order
		for(i in columns) {
	    var found = false;
	    for(j in new_columns) {
	      if (columns[i].id == new_columns[j].id) {
	        found = true;
	      }
	    }
	    if (found==false) {
	      new_columns.push(columns[i]);
	    }
	  }
		return new_columns;
	},

	// Restore columns visibility states
	restoreVisibilityStates: function(columns, visibilityStates) {
	  if(!visibilityStates) return false;

	  // push visible columns according to states
	  for(var i in columns){
	    var visible = true;
	    for(var j in visibilityStates){
	      if(columns[i].id == visibilityStates[j]){
	        visible = false;
	        break;
	      }
	    }
	    columns[i].visible = visible;
	  }
	},

	// Restore columns width states
	restoreWidthStates: function(columns, widthStates) {
	  if(!widthStates) return false;

    // restore width
    for(var i in widthStates){
      for(var j in columns){
        if(columns[j].id == i){
          columns[j].width = parseInt(widthStates[i]);
          break;
        }
      }
    }
	},

	// Restore columns sorting states
	restoreSortingStates: function(grid, loader, sortingStates) {
	  if(sortingStates){
      grid.setSortColumn(sortingStates["sortCol"], sortingStates["sortDir"] == 1);
	    if(grid.options.eagerLoading != false){
        loader.setSort(sortingStates["sortCol"], sortingStates["sortDir"]);
      }
	  }
	},

	// Attach state filters
	applyFilters: function(originalFilters, filterStates) {
	  if (filterStates) {
      originalFilters = originalFilters || [];
  	  $.each(filterStates, function(k, v){
        originalFilters.push({column: k, value: v, operator: 'equals'})
  	    //path += "&filters[][column]=" + encodeURIComponent(k) + "&filters[][value]=" + encodeURIComponent(v);
  	  })
    }
	  return originalFilters;
	}

}

<commit_msg>BUG - fixed the onColumnsPick event when the grid has no filter panel<commit_after>var GridStatesManager = {
  // do ajax save
  saveStates: function(gridName, type, value){
    if(gridName) {
      type = type || "order";
      vale = value || {};
      var url = "/wulin_master/grid_states_manages/save",
      data = decodeURIComponent($.param({ grid_name: encodeURIComponent(gridName),
                                        state_type: encodeURIComponent(type),
                                        state_value: value,
                                        authenticity_token: window._token }));
      $.post(url, data, function(){});
    }
  },

  // grid events
  onStateEvents: function(grid) {
    var self = this;

    // save columns width when columns resized
    grid.onColumnsResized.subscribe(function(){
      var widthJson = {};
      $.each(this.getColumns(), function(index, column){
        widthJson[column.id] = column.width;
      });
      self.saveStates(grid.name, "width", widthJson);
    });

    // save columns sorting info when columns sorted
    grid.onSort.subscribe(function(e, args){
      var loader = grid.loader, sortJson = {};
      sortJson["sortCol"] = loader.getSortColumn();
      sortJson["sortDir"] = loader.getSortDirection();
      // update sort state and save it to db
      grid.states["sort"] = {sortCol: sortJson["sortCol"], sortDir: sortJson["sortDir"]};
      self.saveStates(grid.name, "sort", sortJson);
    });

    // save columns order when columns re-ordered
    grid.onColumnsReordered.subscribe(function(e, args){
      var orderJson = {};
      $.each(this.getColumns(), function(index, column){
        orderJson[index] = column.id;
      });

      self.saveStates(grid.name, "order", orderJson);
    });

    // save filter states when input filter value
    if(grid.filterPanel) {
      grid.filterPanel.onFilterLoaded.subscribe(function(e, args){
        var filterJson = {};
        $.each(args.filterData, function(index,data){
          filterJson[data['id']] = encodeURIComponent(data['value']);
        });
        self.saveStates(grid.name, "filter", filterJson);
      });

      grid.filterPanel.onFilterPanelClosed.subscribe(function(e, args){
        $(grid.getHeaderRow()).find('input[type="text"]').val('');
        self.saveStates(grid.name, "filter", {});
      });
    }

    // save columns visibility when pick columns
    if(grid.picker){
      grid.picker.onColumnsPick.subscribe(function(e, args){
        var hiddenArr = [], hiddenJson = {}, visibilityColumns = grid.getColumns();

        // Regenerate Filter panel
        if(grid.filterPanel) {
          grid.filterPanel.generateFilters();
        }

        visibilityColumns = $.map(visibilityColumns, function(n, i){
            return n.id;
        });
        allColumns = $.map(grid.columns, function(n, i){
            return n.id;
        });

        hiddenArr = $.grep(allColumns, function(n, i){
            return visibilityColumns.indexOf(n) < 0;
        });

        $.each(hiddenArr, function(index, column){
            hiddenJson[index] = column;
        });
        self.saveStates(grid.name, "visibility", hiddenArr);
      })
    }
  },

  // Restore columns order states
  restoreOrderStates: function(columns, orderStates){
	  if(!orderStates) return columns;

	  var new_columns = [], i, j, k;
    // find id column
    for(i in columns){
      if (columns[i].id == "id"){
        new_columns.push(columns[i]);
        break;
      }
    }
		// push other columns according to states
		for(j in orderStates){
		  for(k in columns) {
		    if(columns[k].id == orderStates[j]){
		      new_columns.push(columns[k]);
		      break;
		    }
		  }
		}
		// push columns that are not in the state in abritrary order
		for(i in columns) {
	    var found = false;
	    for(j in new_columns) {
	      if (columns[i].id == new_columns[j].id) {
	        found = true;
	      }
	    }
	    if (found==false) {
	      new_columns.push(columns[i]);
	    }
	  }
		return new_columns;
	},

	// Restore columns visibility states
	restoreVisibilityStates: function(columns, visibilityStates) {
	  if(!visibilityStates) return false;

	  // push visible columns according to states
	  for(var i in columns){
	    var visible = true;
	    for(var j in visibilityStates){
	      if(columns[i].id == visibilityStates[j]){
	        visible = false;
	        break;
	      }
	    }
	    columns[i].visible = visible;
	  }
	},

	// Restore columns width states
	restoreWidthStates: function(columns, widthStates) {
	  if(!widthStates) return false;

    // restore width
    for(var i in widthStates){
      for(var j in columns){
        if(columns[j].id == i){
          columns[j].width = parseInt(widthStates[i]);
          break;
        }
      }
    }
	},

	// Restore columns sorting states
	restoreSortingStates: function(grid, loader, sortingStates) {
	  if(sortingStates){
      grid.setSortColumn(sortingStates["sortCol"], sortingStates["sortDir"] == 1);
	    if(grid.options.eagerLoading != false){
        loader.setSort(sortingStates["sortCol"], sortingStates["sortDir"]);
      }
	  }
	},

	// Attach state filters
	applyFilters: function(originalFilters, filterStates) {
	  if (filterStates) {
      originalFilters = originalFilters || [];
  	  $.each(filterStates, function(k, v){
        originalFilters.push({column: k, value: v, operator: 'equals'})
  	    //path += "&filters[][column]=" + encodeURIComponent(k) + "&filters[][value]=" + encodeURIComponent(v);
  	  })
    }
	  return originalFilters;
	}

}

<|endoftext|>


<commit_before>var https = require('https');
var querystring = require('querystring');

function AtmoClient(atmoLocation, proxy) {
    //TODO: proper handling of atmo URL, https:// etc...
    this.atmoLocation = atmoLocation;
    this.atmoPath = '/api/v1';
    this.basedProxy = new Buffer(proxy).toString('base64');
}

AtmoClient.prototype.getPortMappings = function (cb) {
    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/port_mappings/',
        method: 'GET',
        rejectUnauthorized: false,
        headers: {
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 200) {
            cb(new Error('error getting port mappings:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.port_mappings);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with getting port mappings:", e);
        cb(e);
    });

    request.end();
};

AtmoClient.prototype.getVirtualMachine = function (id, cb) {
    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/virtual_machines/' + id,
        method: 'GET',
        rejectUnauthorized: false,
        headers: {
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 200) {
            cb(new Error('error getting vm:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.virtual_machine);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with getting vm:", e);
        cb(e);
    });

    request.end();
};

AtmoClient.prototype.newAppliance = function (appliance, cb) {
    var data = {
        appliance: {
            appliance_set_id: appliance.setId,
            name: appliance.name,
            configuration_template_id: appliance.templateId,
            params: appliance.params ? appliance.params : []
        }
    };

    var postData = JSON.stringify(data);

    //console.log(postData);

    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/appliances',
        method: 'POST',
        rejectUnauthorized: false,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': postData.length,
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 201) {
            cb(new Error('error creating appliance:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.appliance);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with creating appliance:", e);
        cb(e);
    });

    request.write(postData);
    request.end();
};

AtmoClient.prototype.newApplianceSet = function (appliances, cb) {
    var data = {
        appliance_set: {
            name: 'workflow',
            optimization_policy: 'manual',
            appliances: []
        }
    };

    appliances.forEach(function (appliance) {
        //data.appliances.push(
        data.appliance_set.appliances.push(
            {
                configuration_template_id: appliance.applianceId,
                params: appliance.params,
                vms: appliance.vms
            }
        )
    });

    var postData = JSON.stringify(data);

    //console.log(postData);

    var request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/appliance_sets',
        method: 'POST',
        rejectUnauthorized: false,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': postData.length,
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 201) {
            cb(new Error('error creating appliance set:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.appliance_set);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with creating appliance set:", e);
        cb(e);
    });

    request.write(postData);
    request.end();
};

function createClient(atmoLocation, basedProxy) {
    return new AtmoClient(atmoLocation, basedProxy);
}

module.exports.createClient = createClient;<commit_msg>Fix creating appliance set with empty appliance list.<commit_after>var https = require('https');
var querystring = require('querystring');

function AtmoClient(atmoLocation, proxy) {
    //TODO: proper handling of atmo URL, https:// etc...
    this.atmoLocation = atmoLocation;
    this.atmoPath = '/api/v1';
    this.basedProxy = new Buffer(proxy).toString('base64');
}

AtmoClient.prototype.getPortMappings = function (cb) {
    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/port_mappings/',
        method: 'GET',
        rejectUnauthorized: false,
        headers: {
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 200) {
            cb(new Error('error getting port mappings:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.port_mappings);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with getting port mappings:", e);
        cb(e);
    });

    request.end();
};

AtmoClient.prototype.getVirtualMachine = function (id, cb) {
    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/virtual_machines/' + id,
        method: 'GET',
        rejectUnauthorized: false,
        headers: {
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 200) {
            cb(new Error('error getting vm:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.virtual_machine);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with getting vm:", e);
        cb(e);
    });

    request.end();
};

AtmoClient.prototype.newAppliance = function (appliance, cb) {
    var data = {
        appliance: {
            appliance_set_id: appliance.setId,
            name: appliance.name,
            configuration_template_id: appliance.templateId,
            params: appliance.params ? appliance.params : []
        }
    };

    var postData = JSON.stringify(data);

    //console.log(postData);

    request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/appliances',
        method: 'POST',
        rejectUnauthorized: false,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': postData.length,
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 201) {
            cb(new Error('error creating appliance:' + res.toString()));
            //console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            //console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.appliance);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with creating appliance:", e);
        cb(e);
    });

    request.write(postData);
    request.end();
};

AtmoClient.prototype.newApplianceSet = function (appliances, cb) {
    var data = {
        appliance_set: {
            name: 'workflow_test',
            optimization_policy: 'manual',
            appliances: []
        }
    };

    if (appliances) {
        appliances.forEach(function (appliance) {
            //data.appliances.push(
            data.appliance_set.appliances.push(
                {
                    configuration_template_id: appliance.applianceId,
                    params: appliance.params,
                    vms: appliance.vms
                }
            )
        });
    }

    var postData = JSON.stringify(data);

    console.log(postData);

    var request = https.request({
        hostname: this.atmoLocation,
        path: this.atmoPath + '/appliance_sets',
        method: 'POST',
        rejectUnauthorized: false,
        headers: {
            'Content-Type': 'application/json',
            'Content-Length': postData.length,
            'PROXY': this.basedProxy
        }
    }, function (res) {
        //console.log(res.statusCode);
        if (res.statusCode != 201) {
            cb(new Error('error creating appliance set:' + res.toString()));
            console.log(res);
            return;
        }
        res.setEncoding('utf8');
        var data = '';
        res.on('data', function (chunk) {
            data += chunk;
        });
        res.on('end', function () {
            console.log('end of response', data);
            var resData = JSON.parse(data);
            cb(null, resData.appliance_set);
        });
    });

    request.on('error', function (e) {
        console.log("Problem with creating appliance set:", e);
        cb(e);
    });

    request.write(postData);
    request.end();
};

function createClient(atmoLocation, basedProxy) {
    return new AtmoClient(atmoLocation, basedProxy);
}

module.exports.createClient = createClient;<|endoftext|>


<commit_before>/*
 * Copyright 2013 Amadeus s.a.s.
 * 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.
 */

/**
 * This module start some browsers if needed for a given campaign.
 * Browsers are not necessarily installed locally, for this reason this module
 * emit events that could be listened to by external plugins to drive remote machines
 *
 * Useful events are
 * attester.server.started raised when the server is listening
 * attester.server.attached raised when there's at least one campaign attached to the server
 * launcher.connect raised when there's at least one campaign ready to be executed by slaves
 *
 * The most interesting event for plugins is launcher.connect which provides as well the
 * slave URL. Other events might be used to perform initialization.
 */

var Q = require('q');

var attester = require("../attester");

// Include also some default launchers
var phantomLauncher = require("../launchers/phantom-launcher");
var browserLauncher = require("../launchers/browser-launcher");

// so that (phantomjs-instances + the number of browsers) can be > 10:
process.stdout.setMaxListeners(256);
process.stderr.setMaxListeners(256);

// Do it here so that launchers using childProcesses don't have to care about it
var childProcesses = require("../util/child-processes.js");

/* I believe the order in which events are raised is fragile. server.attached is in the callback
 * of campaign.init, while campaign.tasksList is inside the init, just after the callback
 * In the future we might change this logic, so this module makes sure that launcher.connect
 * is always raised after server.attached and it always contains the slave URL
 */
var urlInfo = null;
var raiseOnAttached = false;

// Raise the connect event

function raiseConnect() {
    attester.event.emit("launcher.connect", urlInfo.slaveURL);
}

function onServerAttached(event) {
    urlInfo = event;
    if (raiseOnAttached) {
        process.nextTick(raiseConnect);
    }
}

function onTasksList(event) {
    if (!urlInfo) {
        // The server is not attached yet
        raiseOnAttached = true;
    } else {
        process.nextTick(raiseConnect);
    }
}

exports.__init__ = function () {
    attester.event.once("attester.server.attached", onServerAttached);
    attester.event.on("attester.campaign.tasksList", onTasksList);
    phantomLauncher.__init__();
    browserLauncher.__init__();
};

exports.__reset__ = function () {
    urlInfo = null;
    raiseOnAttached = false;

    browserLauncher.__reset__();
    phantomLauncher.__reset__();
    attester.event.off("attester.server.attached", onServerAttached);
    attester.event.off("attester.campaign.tasksList", onTasksList);
    childProcesses.closeAll();

    attester.logger.logDebug("__reset__ launcher done");
    return Q();
};<commit_msg>Add timeout in launchers' dispose<commit_after>/*
 * Copyright 2013 Amadeus s.a.s.
 * 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.
 */

/**
 * This module start some browsers if needed for a given campaign.
 * Browsers are not necessarily installed locally, for this reason this module
 * emit events that could be listened to by external plugins to drive remote machines
 *
 * Useful events are
 * attester.server.started raised when the server is listening
 * attester.server.attached raised when there's at least one campaign attached to the server
 * launcher.connect raised when there's at least one campaign ready to be executed by slaves
 *
 * The most interesting event for plugins is launcher.connect which provides as well the
 * slave URL. Other events might be used to perform initialization.
 */

var Q = require('q');

var attester = require("../attester");

// Include also some default launchers
var phantomLauncher = require("../launchers/phantom-launcher");
var browserLauncher = require("../launchers/browser-launcher");

// so that (phantomjs-instances + the number of browsers) can be > 10:
process.stdout.setMaxListeners(256);
process.stderr.setMaxListeners(256);

// Do it here so that launchers using childProcesses don't have to care about it
var childProcesses = require("../util/child-processes.js");

/* I believe the order in which events are raised is fragile. server.attached is in the callback
 * of campaign.init, while campaign.tasksList is inside the init, just after the callback
 * In the future we might change this logic, so this module makes sure that launcher.connect
 * is always raised after server.attached and it always contains the slave URL
 */
var urlInfo = null;
var raiseOnAttached = false;

// Raise the connect event

function raiseConnect() {
    attester.event.emit("launcher.connect", urlInfo.slaveURL);
}

function onServerAttached(event) {
    urlInfo = event;
    if (raiseOnAttached) {
        process.nextTick(raiseConnect);
    }
}

function onTasksList(event) {
    if (!urlInfo) {
        // The server is not attached yet
        raiseOnAttached = true;
    } else {
        process.nextTick(raiseConnect);
    }
}

exports.__init__ = function () {
    attester.event.once("attester.server.attached", onServerAttached);
    attester.event.on("attester.campaign.tasksList", onTasksList);
    phantomLauncher.__init__();
    browserLauncher.__init__();
};

exports.__reset__ = function () {
    urlInfo = null;
    raiseOnAttached = false;

    browserLauncher.__reset__();
    phantomLauncher.__reset__();
    attester.event.off("attester.server.attached", onServerAttached);
    attester.event.off("attester.campaign.tasksList", onTasksList);

    var deferred = Q.defer();
    setTimeout(function () {
        // give some time for the client to disconnect so we don't have to kill it
        childProcesses.closeAll();
        attester.logger.logDebug("__reset__ launcher done");
        deferred.resolve();
    }, 500);

    return deferred.promise;
};<|endoftext|>


<commit_before>'use babel';

/**
 * TRIGGER FORMATTING ON ENTER KEY
 * On action newline below
 * CHECK LINE CONTEXT TextEditor ::scopeDescriptorForBufferPosition(bufferPosition)
 * FORMAT ON ENTER Mutating Text ::insert new line (TextEditor) // Trigger any shortcuts on
 * ADD EW FILE BUTTON
 *
 *
 * For this 15 mins session, I want to
 * - emit a fake event
 * - check that it launched
 * - check that =y plugin's function was called
 * || check my pugin subscribttions
 */

import { CompositeDisposable } from 'atom'; // Atom event handle
import { Package } from './package'; // Our package class
import debounce from 'lodash.debounce';

const pack = new Package();
let isActive = true;
let disableOnCommit = true;
let saveDelay;

export default {
  config: {
    delay: {
      title: 'Delay',
      description: 'The amount of milliseconds after a change before editor auto saves',
      type: 'integer',
      default: 500,
      minimum: 500
    },
    disableOnCommit: {
      title: 'Git Plus: Disable on commit?',
      description: 'If true automatically disables autosave when creating a commit message (Git Plus automatically commits on save)',
      type: 'boolean',
      default: true
    }
  },

  toggle() {
    let message = null;
    isActive = !isActive;

    console.log('Toggle!');

    if (isActive) {
      atom.notifications.addInfo('Autosave Onchange Enabled!', {
        text: '',
        dismissable: true
      });
    } else {
      atom.notifications.addInfo('Autosave Onchange Disabled!', {
        text: '',
        dismissable: true
      });
    }
  },

  activate(state) {
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();
    this.subscriptions.add(
      atom.commands.add('atom-workspace', {
        'autosave-onchange:toggle': this.toggle
      }),
      atom.config.onDidChange('autosave-onchange.delay', (values) => {
        console.log(`autosave delay changed from ${values.oldValue} to ${values.newValue}`);
        saveDelay = values.newValue;
      }),
      atom.config.onDidChange('autosave-onchange.disableOnCommit', (values) => {
        console.log(`Disable on commit has changed: ${values.newValue}`);
        disableOnCommit = values.newValue;
      })
    );

    saveDelay = atom.config.get('autosave-onchange.delay');
    disableOnCommit = atom.config.get('autosave-onchange.disableOnCommit');

    let observeEditors = atom.workspace.observeTextEditors((editor) => {
      let activeWorkspace = atom.views.getView(editor);

      //TODO: Handle circumstance when autosave already disabled...
      // git-plus performs a commit on save, we will disable autosave functionality
      // until the commit is completed for canceled...
      if(disableOnCommit && editor.getTitle() === 'COMMIT_EDITMSG') {
        console.log('Disabling autosave until commit is made or canceled...');
        isActive = false;
        editor.onDidDestroy(() => {
          console.log('Commit over, renabling autosave...');
          isActive = true;
        });
      }

      activeWorkspace.addEventListener('autocomplete-plus:cancel', (e) =>
        pack.onAutoCompleteClose(editor, isActive));

      editor.onDidChangeCursorPosition(
        debounce((ev) =>
          pack.onDidChangeCursorPosition(editor, activeWorkspace, isActive), saveDelay));
    });

    this.subscriptions.add(observeEditors);
  },

  deactivate() {
    // Remove all package event subscriptions
    this.subscriptions.dispose();
  },

  // Is called every time package executes a command
  serialize() {
    return;
  }
};
<commit_msg>Refactor toggle on commit<commit_after>'use babel';

/**
 * TRIGGER FORMATTING ON ENTER KEY
 * On action newline below
 * CHECK LINE CONTEXT TextEditor ::scopeDescriptorForBufferPosition(bufferPosition)
 * FORMAT ON ENTER Mutating Text ::insert new line (TextEditor) // Trigger any shortcuts on
 * ADD EW FILE BUTTON
 *
 *
 * For this 15 mins session, I want to
 * - emit a fake event
 * - check that it launched
 * - check that =y plugin's function was called
 * || check my pugin subscribttions
 */

import {
  CompositeDisposable
} from 'atom'; // Atom event handle
import {
  Package
} from './package'; // Our package class
import debounce from 'lodash.debounce';

const pack = new Package();
let isActive = true;
let disableOnCommit = true;
let saveDelay;

export default {
  config: {
    delay: {
      title: 'Delay',
      description: 'The amount of milliseconds after a change before editor auto saves',
      type: 'integer',
      default: 500,
      minimum: 500
    },
    disableOnCommit: {
      title: 'Git Plus: Disable on commit?',
      description: 'If true automatically disables autosave when creating a commit message (Git Plus automatically commits on save)',
      type: 'boolean',
      default: true
    }
  },

  toggle() {
    let message = null;
    isActive = !isActive;

    console.log('Toggle!');

    if (isActive) {
      atom.notifications.addInfo('Autosave Onchange Enabled!', {
        text: '',
        dismissable: true
      });
    } else {
      atom.notifications.addInfo('Autosave Onchange Disabled!', {
        text: '',
        dismissable: true
      });
    }
  },

  activate(state) {
    // Events subscribed to in atom's system can be easily cleaned up with a CompositeDisposable
    this.subscriptions = new CompositeDisposable();
    this.subscriptions.add(
      atom.commands.add('atom-workspace', {
        'autosave-onchange:toggle': this.toggle
      }),
      atom.config.onDidChange('autosave-onchange.delay', (values) => {
        console.log(`autosave delay changed from ${values.oldValue} to ${values.newValue}`);
        saveDelay = values.newValue;
      }),
      atom.config.onDidChange('autosave-onchange.disableOnCommit', (values) => {
        console.log(`Disable on commit has changed: ${values.newValue}`);
        disableOnCommit = values.newValue;
      })
    );

    saveDelay = atom.config.get('autosave-onchange.delay');
    disableOnCommit = atom.config.get('autosave-onchange.disableOnCommit');
    let observeEditors = atom.workspace.observeTextEditors((editor) => {
      let activeWorkspace = atom.views.getView(editor);

      if (disableOnCommit && isActive && editor.getTitle() === 'COMMIT_EDITMSG') {
        console.log('Disabling autosave until commit screen is open...');
        this.toggle();
        editor.onDidDestroy(() => {
          console.log('Commit screen closed, enabling autosave...');
          if(!isActive) this.toggle();
        });
      }

      activeWorkspace.addEventListener('autocomplete-plus:cancel', (e) =>
        pack.onAutoCompleteClose(editor, isActive));

      editor.onDidChangeCursorPosition(
        debounce((ev) =>
          pack.onDidChangeCursorPosition(editor, activeWorkspace, isActive), saveDelay));
    });

    this.subscriptions.add(observeEditors);
  },

  deactivate() {
    // Remove all package event subscriptions
    this.subscriptions.dispose();
  },

  // Is called every time package executes a command
  serialize() {
    return;
  }
};
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var EventEmitter = require('events').EventEmitter;
var debug = require('./util/debug')('batch');
var queue = require('queue-async');
var HostScheduler = require('./scheduler/host-scheduler');

var EMPTY_QUEUE = true;

var MINUTE = 60 * 1000;
var CONSUME_QUEUE_INTERVAL = 1 * MINUTE;

function Batch(name, userDatabaseMetadataService, jobSubscriber, jobQueue, jobRunner, jobService, redisPool, logger) {
    EventEmitter.call(this);
    this.name = name || 'batch';
    this.userDatabaseMetadataService = userDatabaseMetadataService;
    this.jobSubscriber = jobSubscriber;
    this.jobQueue = jobQueue;
    this.jobRunner = jobRunner;
    this.jobService = jobService;
    this.logger = logger;
    this.hostScheduler = new HostScheduler(this.name, { run: this.processJob.bind(this) }, redisPool);

    // map: user => jobId. Will be used for draining jobs.
    this.workInProgressJobs = {};
}
util.inherits(Batch, EventEmitter);

module.exports = Batch;

Batch.prototype.start = function () {
    var self = this;
    var onJobHandler = createJobHandler(self.name, self.userDatabaseMetadataService, self.hostScheduler);

    self.jobQueue.scanQueues(function (err, queues) {
        if (err) {
            return self.emit('error', err);
        }

        queues.forEach(onJobHandler);
        self._startQueueConsumerInterval(onJobHandler);

        self.jobSubscriber.subscribe(onJobHandler, function (err) {
            if (err) {
                return self.emit('error', err);
            }

            self.emit('ready');
        });
    });
};

function createJobHandler (name, userDatabaseMetadataService, hostScheduler) {
    return function onJobHandler(user) {
        userDatabaseMetadataService.getUserMetadata(user, function (err, userDatabaseMetadata) {
            if (err) {
                return debug('Could not get host user=%s from %s. Reason: %s', user, name, err.message);
            }

            var host = userDatabaseMetadata.host;

            debug('[%s] onJobHandler(%s, %s)', name, user, host);
            hostScheduler.add(host, user, function(err) {
                if (err) {
                    return debug(
                        'Could not schedule host=%s user=%s from %s. Reason: %s', host, user, name, err.message
                    );
                }
            });
        });
    };
}

Batch.prototype._startQueueConsumerInterval = function (onJobHandler) {
    var self = this;

    self.consumeQueueInterval = setInterval(function () {
        self.jobQueue.getQueues(function (err, queues) {
            if (err) {
                return debug('Could not get queues from %s. Reason: %s', self.name, err.message);
            }

            queues.forEach(onJobHandler);
        });
    }, CONSUME_QUEUE_INTERVAL);
};

Batch.prototype._stopQueueConsumerInterval = function () {
    if (this.consumeQueueInterval) {
        clearInterval(this.consumeQueueInterval);
    }
};

Batch.prototype.processJob = function (user, callback) {
    var self = this;

    self.jobQueue.dequeue(user, function (err, jobId) {
        if (err) {
            return callback(new Error('Could not get job from "' + user + '". Reason: ' + err.message), !EMPTY_QUEUE);
        }

        if (!jobId) {
            debug('Queue empty user=%s', user);
            return callback(null, EMPTY_QUEUE);
        }

        self._processWorkInProgressJob(user, jobId, function (err, job) {
            if (err) {
                debug(err);
                if (err.name === 'JobNotRunnable') {
                    return callback(null, !EMPTY_QUEUE);
                }
                return callback(err, !EMPTY_QUEUE);
            }

            debug(
                '[%s] Job=%s status=%s user=%s (failed_reason=%s)',
                self.name, jobId, job.data.status, user, job.failed_reason
            );

            self.logger.log(job);

            return callback(null, !EMPTY_QUEUE);
        });
    });
};

Batch.prototype._processWorkInProgressJob = function (user, jobId, callback) {
    var self = this;

    self.setWorkInProgressJob(user, jobId, function (errSet) {
        if (errSet) {
            debug(new Error('Could not add job to work-in-progress list. Reason: ' + errSet.message));
        }

        self.jobRunner.run(jobId, function (err, job) {
            self.clearWorkInProgressJob(user, jobId, function (errClear) {
                if (errClear) {
                    debug(new Error('Could not clear job from work-in-progress list. Reason: ' + errClear.message));
                }

                return callback(err, job);
            });
        });
    });
};

Batch.prototype.drain = function (callback) {
    var self = this;
    var workingUsers = this.getWorkInProgressUsers();
    var batchQueues = queue(workingUsers.length);

    workingUsers.forEach(function (user) {
        batchQueues.defer(self._drainJob.bind(self), user);
    });

    batchQueues.awaitAll(function (err) {
        if (err) {
            debug('Something went wrong draining', err);
        } else {
            debug('Drain complete');
        }

        callback();
    });
};

Batch.prototype._drainJob = function (user, callback) {
    var self = this;
    var job_id = this.getWorkInProgressJob(user);

    if (!job_id) {
        return process.nextTick(function () {
            return callback();
        });
    }

    this.jobService.drain(job_id, function (err) {
        if (err && err.name === 'CancelNotAllowedError') {
            return callback();
        }

        if (err) {
            return callback(err);
        }

        self.jobQueue.enqueueFirst(user, job_id, callback);
    });
};

Batch.prototype.stop = function (callback) {
    this.removeAllListeners();
    this._stopQueueConsumerInterval();
    this.jobSubscriber.unsubscribe(callback);
};

/* Work in progress jobs */

Batch.prototype.setWorkInProgressJob = function(user, jobId, callback) {
    this.workInProgressJobs[user] = jobId;
    this.jobService.addWorkInProgressJob(user, jobId, callback);
};

Batch.prototype.getWorkInProgressJob = function(user) {
    return this.workInProgressJobs[user];
};

Batch.prototype.clearWorkInProgressJob = function(user, jobId, callback) {
    delete this.workInProgressJobs[user];
    this.jobService.clearWorkInProgressJob(user, jobId, callback);
};

Batch.prototype.getWorkInProgressUsers = function() {
    return Object.keys(this.workInProgressJobs);
};
<commit_msg>Improve naming<commit_after>'use strict';

var util = require('util');
var EventEmitter = require('events').EventEmitter;
var debug = require('./util/debug')('batch');
var queue = require('queue-async');
var HostScheduler = require('./scheduler/host-scheduler');

var EMPTY_QUEUE = true;

var MINUTE = 60 * 1000;
var SCHEDULE_INTERVAL = 1 * MINUTE;

function Batch(name, userDatabaseMetadataService, jobSubscriber, jobQueue, jobRunner, jobService, redisPool, logger) {
    EventEmitter.call(this);
    this.name = name || 'batch';
    this.userDatabaseMetadataService = userDatabaseMetadataService;
    this.jobSubscriber = jobSubscriber;
    this.jobQueue = jobQueue;
    this.jobRunner = jobRunner;
    this.jobService = jobService;
    this.logger = logger;
    this.hostScheduler = new HostScheduler(this.name, { run: this.processJob.bind(this) }, redisPool);

    // map: user => jobId. Will be used for draining jobs.
    this.workInProgressJobs = {};
}
util.inherits(Batch, EventEmitter);

module.exports = Batch;

Batch.prototype.start = function () {
    var self = this;
    var onJobHandler = createJobHandler(self.name, self.userDatabaseMetadataService, self.hostScheduler);

    self.jobQueue.scanQueues(function (err, queues) {
        if (err) {
            return self.emit('error', err);
        }

        queues.forEach(onJobHandler);
        self._startScheduleInterval(onJobHandler);

        self.jobSubscriber.subscribe(onJobHandler, function (err) {
            if (err) {
                return self.emit('error', err);
            }

            self.emit('ready');
        });
    });
};

function createJobHandler (name, userDatabaseMetadataService, hostScheduler) {
    return function onJobHandler(user) {
        userDatabaseMetadataService.getUserMetadata(user, function (err, userDatabaseMetadata) {
            if (err) {
                return debug('Could not get host user=%s from %s. Reason: %s', user, name, err.message);
            }

            var host = userDatabaseMetadata.host;

            debug('[%s] onJobHandler(%s, %s)', name, user, host);
            hostScheduler.add(host, user, function(err) {
                if (err) {
                    return debug(
                        'Could not schedule host=%s user=%s from %s. Reason: %s', host, user, name, err.message
                    );
                }
            });
        });
    };
}

Batch.prototype._startScheduleInterval = function (onJobHandler) {
    var self = this;

    self.scheduleInterval = setInterval(function () {
        self.jobQueue.getQueues(function (err, queues) {
            if (err) {
                return debug('Could not get queues from %s. Reason: %s', self.name, err.message);
            }

            queues.forEach(onJobHandler);
        });
    }, SCHEDULE_INTERVAL);
};

Batch.prototype._stopScheduleInterval = function () {
    if (this.scheduleInterval) {
        clearInterval(this.scheduleInterval);
    }
};

Batch.prototype.processJob = function (user, callback) {
    var self = this;

    self.jobQueue.dequeue(user, function (err, jobId) {
        if (err) {
            return callback(new Error('Could not get job from "' + user + '". Reason: ' + err.message), !EMPTY_QUEUE);
        }

        if (!jobId) {
            debug('Queue empty user=%s', user);
            return callback(null, EMPTY_QUEUE);
        }

        self._processWorkInProgressJob(user, jobId, function (err, job) {
            if (err) {
                debug(err);
                if (err.name === 'JobNotRunnable') {
                    return callback(null, !EMPTY_QUEUE);
                }
                return callback(err, !EMPTY_QUEUE);
            }

            debug(
                '[%s] Job=%s status=%s user=%s (failed_reason=%s)',
                self.name, jobId, job.data.status, user, job.failed_reason
            );

            self.logger.log(job);

            return callback(null, !EMPTY_QUEUE);
        });
    });
};

Batch.prototype._processWorkInProgressJob = function (user, jobId, callback) {
    var self = this;

    self.setWorkInProgressJob(user, jobId, function (errSet) {
        if (errSet) {
            debug(new Error('Could not add job to work-in-progress list. Reason: ' + errSet.message));
        }

        self.jobRunner.run(jobId, function (err, job) {
            self.clearWorkInProgressJob(user, jobId, function (errClear) {
                if (errClear) {
                    debug(new Error('Could not clear job from work-in-progress list. Reason: ' + errClear.message));
                }

                return callback(err, job);
            });
        });
    });
};

Batch.prototype.drain = function (callback) {
    var self = this;
    var workingUsers = this.getWorkInProgressUsers();
    var batchQueues = queue(workingUsers.length);

    workingUsers.forEach(function (user) {
        batchQueues.defer(self._drainJob.bind(self), user);
    });

    batchQueues.awaitAll(function (err) {
        if (err) {
            debug('Something went wrong draining', err);
        } else {
            debug('Drain complete');
        }

        callback();
    });
};

Batch.prototype._drainJob = function (user, callback) {
    var self = this;
    var job_id = this.getWorkInProgressJob(user);

    if (!job_id) {
        return process.nextTick(function () {
            return callback();
        });
    }

    this.jobService.drain(job_id, function (err) {
        if (err && err.name === 'CancelNotAllowedError') {
            return callback();
        }

        if (err) {
            return callback(err);
        }

        self.jobQueue.enqueueFirst(user, job_id, callback);
    });
};

Batch.prototype.stop = function (callback) {
    this.removeAllListeners();
    this._stopScheduleInterval();
    this.jobSubscriber.unsubscribe(callback);
};

/* Work in progress jobs */

Batch.prototype.setWorkInProgressJob = function(user, jobId, callback) {
    this.workInProgressJobs[user] = jobId;
    this.jobService.addWorkInProgressJob(user, jobId, callback);
};

Batch.prototype.getWorkInProgressJob = function(user) {
    return this.workInProgressJobs[user];
};

Batch.prototype.clearWorkInProgressJob = function(user, jobId, callback) {
    delete this.workInProgressJobs[user];
    this.jobService.clearWorkInProgressJob(user, jobId, callback);
};

Batch.prototype.getWorkInProgressUsers = function() {
    return Object.keys(this.workInProgressJobs);
};
<|endoftext|>


<commit_before>/*!
 * merkleblock.js - merkleblock object for bcoin
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var bcoin = require('./env');
var utils = require('./utils');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var DUMMY = new Buffer([0]);
var AbstractBlock = bcoin.abstractblock;

/**
 * Represents a merkle (filtered) block.
 * @exports MerkleBlock
 * @constructor
 * @extends AbstractBlock
 * @param {NakedBlock} options
 */

function MerkleBlock(options) {
  if (!(this instanceof MerkleBlock))
    return new MerkleBlock(options);

  AbstractBlock.call(this, options);

  this.hashes = [];
  this.flags = DUMMY;

  // List of matched TXs
  this.map = {};
  this.matches = [];
  this._validPartial = null;

  // TXs that will be pushed on
  this.txs = [];

  if (options)
    this.fromOptions(options);
}

utils.inherits(MerkleBlock, AbstractBlock);

/**
 * Inject properties from options object.
 * @private
 * @param {NakedBlock} options
 */

MerkleBlock.prototype.fromOptions = function fromOptions(options) {
  assert(options, 'MerkleBlock data is required.');
  assert(Array.isArray(options.hashes));
  assert(Buffer.isBuffer(options.flags));

  if (options.hashes)
    this.hashes = options.hashes;

  if (options.flags)
    this.flags = options.flags;

  return this;
};

/**
 * Instantiate merkle block from options object.
 * @param {NakedBlock} options
 * @returns {MerkleBlock}
 */

MerkleBlock.fromOptions = function fromOptions(data) {
  return new MerkleBlock().fromOptions(data);
};

/**
 * Get merkleblock size.
 * @returns {Number} Size.
 */

MerkleBlock.prototype.getSize = function getSize() {
  var writer = new bcoin.writer();
  this.toRaw(writer);
  return writer.written;
};

/**
 * Add a transaction to the block's tx vector.
 * @param {TX|NakedTX} tx
 * @returns {TX}
 */

MerkleBlock.prototype.addTX = function addTX(tx) {
  var hash = tx.hash('hex');
  var index = this.map[hash];

  this.txs.push(tx);
  tx.setBlock(this, index);

  return index;
};

/**
 * Test the block's _matched_ transaction vector against a hash.
 * @param {Hash|TX} hash
 * @returns {Boolean}
 */

MerkleBlock.prototype.hasTX = function hasTX(hash) {
  if (hash instanceof bcoin.tx)
    hash = hash.hash('hex');

  this.verifyPartial();

  return this.map[hash] != null;
};

/**
 * Verify the partial merkletree. Push leaves onto
 * {@link MerkleBlock#tx} and into {@link MerkleBlock#map}.
 * @private
 * @returns {Boolean}
 */

MerkleBlock.prototype.verifyPartial = function verifyPartial() {
  var tree;

  if (this._validPartial != null)
    return this._validPartial;

  tree = this.extractTree();

  if (!tree || tree.root !== this.merkleRoot) {
    this._validPartial = false;
    return false;
  }

  this.matches = tree.matches;
  this.map = tree.map;
  this._validPartial = true;

  return true;
};

/**
 * Extract the matches from partial merkle
 * tree and calculate merkle root.
 * @private
 * @returns {Object}
 */

MerkleBlock.prototype.extractTree = function extractTree() {
  var bitsUsed = 0;
  var hashUsed = 0;
  var matches = [];
  var indexes = [];
  var map = {};
  var failed = false;
  var hashes = [];
  var flags = this.flags;
  var totalTX = this.totalTX;
  var height = 0;
  var root, p;

  function width(height) {
    return (totalTX + (1 << height) - 1) >>> height;
  }

  function traverse(height, pos) {
    var parent, hash, left, right, txid;

    if (bitsUsed >= flags.length * 8) {
      failed = true;
      return constants.ZERO_HASH;
    }

    parent = (flags[bitsUsed / 8 | 0] >>> (bitsUsed % 8)) & 1;
    bitsUsed++;

    if (height === 0 || !parent) {
      if (hashUsed >= hashes.length) {
        failed = true;
        return constants.ZERO_HASH;
      }
      hash = hashes[hashUsed++];
      if (height === 0 && parent) {
        txid = hash.toString('hex');
        matches.push(txid);
        indexes.push(pos);
        map[txid] = pos;
      }
      return hash;
    }

    left = traverse(height - 1, pos * 2);
    if (pos * 2 + 1 < width(height - 1)) {
      right = traverse(height - 1, pos * 2 + 1);
      if (utils.equal(right, left))
        failed = true;
    } else {
      right = left;
    }

    return utils.hash256(Buffer.concat([left, right]));
  }

  for (p = 0; p < this.hashes.length; p++)
    hashes.push(new Buffer(this.hashes[p], 'hex'));

  if (totalTX === 0)
    return;

  if (totalTX > constants.block.MAX_SIZE / 60)
    return;

  if (hashes.length > totalTX)
    return;

  if (flags.length * 8 < hashes.length)
    return;

  height = 0;
  while (width(height) > 1)
    height++;

  root = traverse(height, 0);

  if (failed)
    return;

  if (((bitsUsed + 7) / 8 | 0) !== flags.length)
    return;

  if (hashUsed !== hashes.length)
    return;

  return {
    root: root.toString('hex'),
    matches: matches,
    indexes: indexes,
    map: map
  };
};

/**
 * Do non-contextual verification on the block.
 * Verify the headers and the partial merkle tree.
 * @alias MerkleBlock#verify
 * @param {Object?} ret - Return object, may be
 * set with properties `reason` and `score`.
 * @returns {Boolean}
 */

MerkleBlock.prototype._verify = function _verify(ret) {
  if (!ret)
    ret = {};

  if (!this.verifyHeaders(ret))
    return false;

  if (!this.verifyPartial()) {
    ret.reason = 'bad-txnmrklroot';
    ret.score = 100;
    return false;
  }

  return true;
};

/**
 * Extract the coinbase height (always -1).
 * @returns {Number}
 */

MerkleBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
  return -1;
};

/**
 * Inspect the block and return a more
 * user-friendly representation of the data.
 * @returns {Object}
 */

MerkleBlock.prototype.inspect = function inspect() {
  return {
    type: 'merkleblock',
    hash: this.rhash,
    height: this.height,
    date: utils.date(this.ts),
    version: this.version,
    prevBlock: utils.revHex(this.prevBlock),
    merkleRoot: utils.revHex(this.merkleRoot),
    ts: this.ts,
    bits: this.bits,
    nonce: this.nonce,
    totalTX: this.totalTX,
    hashes: this.hashes,
    flags: this.flags,
    map: this.map,
    txs: this.txs.length
  };
};

/**
 * Serialize the merkleblock.
 * @param {String?} enc - Encoding, can be `'hex'` or null.
 * @returns {Buffer|String}
 */

MerkleBlock.prototype.toRaw = function toRaw(writer) {
  var p = bcoin.writer(writer);
  var i;

  p.writeU32(this.version);
  p.writeHash(this.prevBlock);
  p.writeHash(this.merkleRoot);
  p.writeU32(this.ts);
  p.writeU32(this.bits);
  p.writeU32(this.nonce);
  p.writeU32(this.totalTX);

  p.writeVarint(this.hashes.length);

  for (i = 0; i < this.hashes.length; i++)
    p.writeHash(this.hashes[i]);

  p.writeVarBytes(this.flags);

  if (!writer)
    p = p.render();

  return p;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

MerkleBlock.prototype.fromRaw = function fromRaw(data) {
  var p = bcoin.reader(data);
  var i, hashCount;

  this.version = p.readU32();
  this.prevBlock = p.readHash('hex');
  this.merkleRoot = p.readHash('hex');
  this.ts = p.readU32();
  this.bits = p.readU32();
  this.nonce = p.readU32();
  this.totalTX = p.readU32();

  hashCount = p.readVarint();

  this.hashes = [];

  for (i = 0; i < hashCount; i++)
    this.hashes.push(p.readHash('hex'));

  this.flags = p.readVarBytes();

  return this;
};

/**
 * Instantiate a merkleblock from a serialized data.
 * @param {Buffer} data
 * @param {String?} enc - Encoding, can be `'hex'` or null.
 * @returns {MerkleBlock}
 */

MerkleBlock.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new MerkleBlock().fromRaw(data);
};

/**
 * Convert the block to an object suitable
 * for JSON serialization. Note that the hashes
 * will be reversed to abide by bitcoind's legacy
 * of little-endian uint256s.
 * @returns {Object}
 */

MerkleBlock.prototype.toJSON = function toJSON() {
  return {
    type: 'merkleblock',
    hash: this.rhash,
    height: this.height,
    version: this.version,
    prevBlock: utils.revHex(this.prevBlock),
    merkleRoot: utils.revHex(this.merkleRoot),
    ts: this.ts,
    bits: this.bits,
    nonce: this.nonce,
    totalTX: this.totalTX,
    hashes: this.hashes,
    flags: this.flags.toString('hex')
  };
};

/**
 * Inject properties from json object.
 * @private
 * @param {Object} json
 */

MerkleBlock.prototype.fromJSON = function fromJSON(json) {
  assert(json, 'MerkleBlock data is required.');
  assert.equal(json.type, 'merkleblock');
  assert(Array.isArray(json.hashes));
  assert(typeof json.flags === 'string');

  this.parseJSON(json);

  this.hashes = json.hashes;
  this.flags = new Buffer(json.flags, 'hex');

  return this;
};

/**
 * Instantiate a merkle block from a jsonified block object.
 * @param {Object} json - The jsonified block object.
 * @returns {MerkleBlock}
 */

MerkleBlock.fromJSON = function fromJSON(json) {
  return new MerkleBlock().fromJSON(json);
};

/**
 * Create a merkleblock from a {@link Block} object, passing
 * it through a filter first. This will build the partial
 * merkle tree.
 * @param {Block} block
 * @param {Bloom} filter
 * @returns {MerkleBlock}
 */

MerkleBlock.fromBlock = function fromBlock(block, filter) {
  var matches = [];
  var txs = [];
  var leaves = [];
  var bits = [];
  var hashes = [];
  var i, tx, totalTX, height, flags, p, merkle;

  for (i = 0; i < block.txs.length; i++) {
    tx = block.txs[i];
    if (tx.isWatched(filter)) {
      matches.push(1);
      txs.push(tx);
    } else {
      matches.push(0);
    }
    leaves.push(tx.hash());
  }

  totalTX = leaves.length;

  function width(height) {
    return (totalTX + (1 << height) - 1) >>> height;
  }

  function hash(height, pos, leaves) {
    var left, right;

    if (height === 0)
      return leaves[pos];

    left = hash(height - 1, pos * 2, leaves);

    if (pos * 2 + 1 < width(height - 1))
      right = hash(height - 1, pos * 2 + 1, leaves);
    else
      right = left;

    return utils.hash256(Buffer.concat([left, right]));
  }

  function traverse(height, pos, leaves, matches) {
    var parent = 0;
    var p;

    for (p = (pos << height); p < ((pos + 1) << height) && p < totalTX; p++)
      parent |= matches[p];

    bits.push(parent);

    if (height === 0 || !parent) {
      hashes.push(hash(height, pos, leaves).toString('hex'));
      return;
    }

    traverse(height - 1, pos * 2, leaves, matches);

    if (pos * 2 + 1 < width(height - 1))
      traverse(height - 1, pos * 2 + 1, leaves, matches);
  }

  height = 0;
  while (width(height) > 1)
    height++;

  traverse(height, 0, leaves, matches);

  flags = new Buffer((bits.length + 7) / 8 | 0);
  flags.fill(0);

  for (p = 0; p < bits.length; p++)
    flags[p / 8 | 0] |= bits[p] << (p % 8);

  merkle = new MerkleBlock();
  merkle._hash = block._hash;
  merkle.version = block.version;
  merkle.prevBlock = block.prevBlock;
  merkle.merkleRoot = block.merkleRoot;
  merkle.ts = block.ts;
  merkle.bits = block.bits;
  merkle.nonce = block.nonce;
  merkle.totalTX = totalTX;
  merkle.height = block.height;
  merkle.hashes = hashes;
  merkle.flags = flags;
  merkle.txs = txs;

  return merkle;
};

/**
 * Test whether an object is a MerkleBlock.
 * @param {Object} obj
 * @returns {Boolean}
 */

MerkleBlock.isMerkleBlock = function isMerkleBlock(obj) {
  return obj
    && obj.flags !== undefined
    && typeof obj.verifyPartial === 'function';
};

/*
 * Expose
 */

module.exports = MerkleBlock;
<commit_msg>minor.<commit_after>/*!
 * merkleblock.js - merkleblock object for bcoin
 * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
 * Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

var bcoin = require('./env');
var utils = require('./utils');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var DUMMY = new Buffer([0]);
var AbstractBlock = bcoin.abstractblock;

/**
 * Represents a merkle (filtered) block.
 * @exports MerkleBlock
 * @constructor
 * @extends AbstractBlock
 * @param {NakedBlock} options
 */

function MerkleBlock(options) {
  if (!(this instanceof MerkleBlock))
    return new MerkleBlock(options);

  AbstractBlock.call(this, options);

  this.hashes = [];
  this.flags = DUMMY;

  // List of matched TXs
  this.map = {};
  this.matches = [];
  this._validPartial = null;

  // TXs that will be pushed on
  this.txs = [];

  if (options)
    this.fromOptions(options);
}

utils.inherits(MerkleBlock, AbstractBlock);

/**
 * Inject properties from options object.
 * @private
 * @param {NakedBlock} options
 */

MerkleBlock.prototype.fromOptions = function fromOptions(options) {
  assert(options, 'MerkleBlock data is required.');
  assert(Array.isArray(options.hashes));
  assert(Buffer.isBuffer(options.flags));

  if (options.hashes)
    this.hashes = options.hashes;

  if (options.flags)
    this.flags = options.flags;

  return this;
};

/**
 * Instantiate merkle block from options object.
 * @param {NakedBlock} options
 * @returns {MerkleBlock}
 */

MerkleBlock.fromOptions = function fromOptions(data) {
  return new MerkleBlock().fromOptions(data);
};

/**
 * Get merkleblock size.
 * @returns {Number} Size.
 */

MerkleBlock.prototype.getSize = function getSize() {
  var writer = new bcoin.writer();
  this.toRaw(writer);
  return writer.written;
};

/**
 * Add a transaction to the block's tx vector.
 * @param {TX|NakedTX} tx
 * @returns {TX}
 */

MerkleBlock.prototype.addTX = function addTX(tx) {
  var hash = tx.hash('hex');
  var index = this.map[hash];

  this.txs.push(tx);
  tx.setBlock(this, index);

  return index;
};

/**
 * Test the block's _matched_ transaction vector against a hash.
 * @param {Hash|TX} hash
 * @returns {Boolean}
 */

MerkleBlock.prototype.hasTX = function hasTX(hash) {
  if (hash instanceof bcoin.tx)
    hash = hash.hash('hex');

  this.verifyPartial();

  return this.map[hash] != null;
};

/**
 * Verify the partial merkletree. Push leaves onto
 * {@link MerkleBlock#tx} and into {@link MerkleBlock#map}.
 * @private
 * @returns {Boolean}
 */

MerkleBlock.prototype.verifyPartial = function verifyPartial() {
  var tree;

  if (this._validPartial != null)
    return this._validPartial;

  tree = this.extractTree();

  if (!tree || tree.root !== this.merkleRoot) {
    this._validPartial = false;
    return false;
  }

  this.matches = tree.matches;
  this.map = tree.map;
  this._validPartial = true;

  return true;
};

/**
 * Extract the matches from partial merkle
 * tree and calculate merkle root.
 * @private
 * @returns {Object}
 */

MerkleBlock.prototype.extractTree = function extractTree() {
  var bitsUsed = 0;
  var hashUsed = 0;
  var matches = [];
  var indexes = [];
  var map = {};
  var failed = false;
  var hashes = [];
  var flags = this.flags;
  var totalTX = this.totalTX;
  var height = 0;
  var root, p;

  function width(height) {
    return (totalTX + (1 << height) - 1) >>> height;
  }

  function traverse(height, pos) {
    var parent, hash, left, right, txid;

    if (bitsUsed >= flags.length * 8) {
      failed = true;
      return constants.ZERO_HASH;
    }

    parent = (flags[bitsUsed / 8 | 0] >>> (bitsUsed % 8)) & 1;
    bitsUsed++;

    if (height === 0 || !parent) {
      if (hashUsed >= hashes.length) {
        failed = true;
        return constants.ZERO_HASH;
      }
      hash = hashes[hashUsed++];
      if (height === 0 && parent) {
        txid = hash.toString('hex');
        matches.push(txid);
        indexes.push(pos);
        map[txid] = pos;
      }
      return hash;
    }

    left = traverse(height - 1, pos * 2);
    if (pos * 2 + 1 < width(height - 1)) {
      right = traverse(height - 1, pos * 2 + 1);
      if (utils.equal(right, left))
        failed = true;
    } else {
      right = left;
    }

    return utils.hash256(Buffer.concat([left, right]));
  }

  for (p = 0; p < this.hashes.length; p++)
    hashes.push(new Buffer(this.hashes[p], 'hex'));

  if (totalTX === 0)
    return;

  if (totalTX > constants.block.MAX_SIZE / 60)
    return;

  if (hashes.length > totalTX)
    return;

  if (flags.length * 8 < hashes.length)
    return;

  height = 0;
  while (width(height) > 1)
    height++;

  root = traverse(height, 0);

  if (failed)
    return;

  if (((bitsUsed + 7) / 8 | 0) !== flags.length)
    return;

  if (hashUsed !== hashes.length)
    return;

  return {
    root: root.toString('hex'),
    matches: matches,
    indexes: indexes,
    map: map
  };
};

/**
 * Do non-contextual verification on the block.
 * Verify the headers and the partial merkle tree.
 * @alias MerkleBlock#verify
 * @param {Object?} ret - Return object, may be
 * set with properties `reason` and `score`.
 * @returns {Boolean}
 */

MerkleBlock.prototype._verify = function _verify(ret) {
  if (!ret)
    ret = {};

  if (!this.verifyHeaders(ret))
    return false;

  if (!this.verifyPartial()) {
    ret.reason = 'bad-txnmrklroot';
    ret.score = 100;
    return false;
  }

  return true;
};

/**
 * Extract the coinbase height (always -1).
 * @returns {Number}
 */

MerkleBlock.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
  return -1;
};

/**
 * Inspect the block and return a more
 * user-friendly representation of the data.
 * @returns {Object}
 */

MerkleBlock.prototype.inspect = function inspect() {
  return {
    type: 'merkleblock',
    hash: this.rhash,
    height: this.height,
    date: utils.date(this.ts),
    version: this.version,
    prevBlock: utils.revHex(this.prevBlock),
    merkleRoot: utils.revHex(this.merkleRoot),
    ts: this.ts,
    bits: this.bits,
    nonce: this.nonce,
    totalTX: this.totalTX,
    hashes: this.hashes,
    flags: this.flags,
    map: this.map,
    txs: this.txs.length
  };
};

/**
 * Serialize the merkleblock.
 * @param {String?} enc - Encoding, can be `'hex'` or null.
 * @returns {Buffer|String}
 */

MerkleBlock.prototype.toRaw = function toRaw(writer) {
  var p = bcoin.writer(writer);
  var i;

  p.writeU32(this.version);
  p.writeHash(this.prevBlock);
  p.writeHash(this.merkleRoot);
  p.writeU32(this.ts);
  p.writeU32(this.bits);
  p.writeU32(this.nonce);
  p.writeU32(this.totalTX);

  p.writeVarint(this.hashes.length);

  for (i = 0; i < this.hashes.length; i++)
    p.writeHash(this.hashes[i]);

  p.writeVarBytes(this.flags);

  if (!writer)
    p = p.render();

  return p;
};

/**
 * Inject properties from serialized data.
 * @private
 * @param {Buffer} data
 */

MerkleBlock.prototype.fromRaw = function fromRaw(data) {
  var p = bcoin.reader(data);
  var i, hashCount;

  this.version = p.readU32();
  this.prevBlock = p.readHash('hex');
  this.merkleRoot = p.readHash('hex');
  this.ts = p.readU32();
  this.bits = p.readU32();
  this.nonce = p.readU32();
  this.totalTX = p.readU32();

  hashCount = p.readVarint();

  for (i = 0; i < hashCount; i++)
    this.hashes.push(p.readHash('hex'));

  this.flags = p.readVarBytes();

  return this;
};

/**
 * Instantiate a merkleblock from a serialized data.
 * @param {Buffer} data
 * @param {String?} enc - Encoding, can be `'hex'` or null.
 * @returns {MerkleBlock}
 */

MerkleBlock.fromRaw = function fromRaw(data, enc) {
  if (typeof data === 'string')
    data = new Buffer(data, enc);
  return new MerkleBlock().fromRaw(data);
};

/**
 * Convert the block to an object suitable
 * for JSON serialization. Note that the hashes
 * will be reversed to abide by bitcoind's legacy
 * of little-endian uint256s.
 * @returns {Object}
 */

MerkleBlock.prototype.toJSON = function toJSON() {
  return {
    type: 'merkleblock',
    hash: this.rhash,
    height: this.height,
    version: this.version,
    prevBlock: utils.revHex(this.prevBlock),
    merkleRoot: utils.revHex(this.merkleRoot),
    ts: this.ts,
    bits: this.bits,
    nonce: this.nonce,
    totalTX: this.totalTX,
    hashes: this.hashes,
    flags: this.flags.toString('hex')
  };
};

/**
 * Inject properties from json object.
 * @private
 * @param {Object} json
 */

MerkleBlock.prototype.fromJSON = function fromJSON(json) {
  assert(json, 'MerkleBlock data is required.');
  assert.equal(json.type, 'merkleblock');
  assert(Array.isArray(json.hashes));
  assert(typeof json.flags === 'string');

  this.parseJSON(json);

  this.hashes = json.hashes;
  this.flags = new Buffer(json.flags, 'hex');

  return this;
};

/**
 * Instantiate a merkle block from a jsonified block object.
 * @param {Object} json - The jsonified block object.
 * @returns {MerkleBlock}
 */

MerkleBlock.fromJSON = function fromJSON(json) {
  return new MerkleBlock().fromJSON(json);
};

/**
 * Create a merkleblock from a {@link Block} object, passing
 * it through a filter first. This will build the partial
 * merkle tree.
 * @param {Block} block
 * @param {Bloom} filter
 * @returns {MerkleBlock}
 */

MerkleBlock.fromBlock = function fromBlock(block, filter) {
  var matches = [];
  var txs = [];
  var leaves = [];
  var bits = [];
  var hashes = [];
  var i, tx, totalTX, height, flags, p, merkle;

  for (i = 0; i < block.txs.length; i++) {
    tx = block.txs[i];
    if (tx.isWatched(filter)) {
      matches.push(1);
      txs.push(tx);
    } else {
      matches.push(0);
    }
    leaves.push(tx.hash());
  }

  totalTX = leaves.length;

  function width(height) {
    return (totalTX + (1 << height) - 1) >>> height;
  }

  function hash(height, pos, leaves) {
    var left, right;

    if (height === 0)
      return leaves[pos];

    left = hash(height - 1, pos * 2, leaves);

    if (pos * 2 + 1 < width(height - 1))
      right = hash(height - 1, pos * 2 + 1, leaves);
    else
      right = left;

    return utils.hash256(Buffer.concat([left, right]));
  }

  function traverse(height, pos, leaves, matches) {
    var parent = 0;
    var p;

    for (p = (pos << height); p < ((pos + 1) << height) && p < totalTX; p++)
      parent |= matches[p];

    bits.push(parent);

    if (height === 0 || !parent) {
      hashes.push(hash(height, pos, leaves).toString('hex'));
      return;
    }

    traverse(height - 1, pos * 2, leaves, matches);

    if (pos * 2 + 1 < width(height - 1))
      traverse(height - 1, pos * 2 + 1, leaves, matches);
  }

  height = 0;
  while (width(height) > 1)
    height++;

  traverse(height, 0, leaves, matches);

  flags = new Buffer((bits.length + 7) / 8 | 0);
  flags.fill(0);

  for (p = 0; p < bits.length; p++)
    flags[p / 8 | 0] |= bits[p] << (p % 8);

  merkle = new MerkleBlock();
  merkle._hash = block._hash;
  merkle.version = block.version;
  merkle.prevBlock = block.prevBlock;
  merkle.merkleRoot = block.merkleRoot;
  merkle.ts = block.ts;
  merkle.bits = block.bits;
  merkle.nonce = block.nonce;
  merkle.totalTX = totalTX;
  merkle.height = block.height;
  merkle.hashes = hashes;
  merkle.flags = flags;
  merkle.txs = txs;

  return merkle;
};

/**
 * Test whether an object is a MerkleBlock.
 * @param {Object} obj
 * @returns {Boolean}
 */

MerkleBlock.isMerkleBlock = function isMerkleBlock(obj) {
  return obj
    && obj.flags !== undefined
    && typeof obj.verifyPartial === 'function';
};

/*
 * Expose
 */

module.exports = MerkleBlock;
<|endoftext|>