<commit_before>import {
  Control,
  DomEvent,
  DomUtil,
  Evented,
  Util,
  latLngBounds
} from 'leaflet';
import { geosearchCore } from '../Classes/GeosearchCore';
import { arcgisOnlineProvider } from '../Providers/ArcgisOnlineGeocoder';
import { Util as EsriUtil } from 'esri-leaflet';

export var Geosearch = Control.extend({
  includes: Evented.prototype,

  options: {
    position: 'topleft',
    collapseAfterResult: true,
    expanded: false,
    allowMultipleResults: true,
    placeholder: 'Search for places or addresses',
    title: 'Location Search'
  },

  initialize: function (options) {
    Util.setOptions(this, options);

    if (!options || !options.providers || !options.providers.length) {
      if (!options) {
        options = {};
      }
      options.providers = [ arcgisOnlineProvider() ];
    }

    // instantiate the underlying class and pass along options
    this._geosearchCore = geosearchCore(this, options);
    this._geosearchCore._providers = options.providers;

    // bubble each providers events to the control
    this._geosearchCore.addEventParent(this);
    for (var i = 0; i < this._geosearchCore._providers.length; i++) {
      this._geosearchCore._providers[i].addEventParent(this);
    }

    this._geosearchCore._pendingSuggestions = [];

    Control.prototype.initialize.call(options);
  },

  _renderSuggestions: function (suggestions) {
    var currentGroup;

    if (suggestions.length > 0) {
      this._suggestions.style.display = 'block';
    }
    // set the maxHeight of the suggestions box to
    // map height
    // - suggestions offset (distance from top of suggestions to top of control)
    // - control offset (distance from top of control to top of map)
    // - 10 (extra padding)
    this._suggestions.style.maxHeight = (this._map.getSize().y - this._suggestions.offsetTop - this._wrapper.offsetTop - 10) + 'px';

    var nodes = [];
    var list;
    var header;
    var suggestionTextArray = [];

    for (var i = 0; i < suggestions.length; i++) {
      var suggestion = suggestions[i];
      if (!header && this._geosearchCore._providers.length > 1 && currentGroup !== suggestion.provider.options.label) {
        header = DomUtil.create('span', 'geocoder-control-header', this._suggestions);
        header.textContent = suggestion.provider.options.label;
        header.innerText = suggestion.provider.options.label;
        currentGroup = suggestion.provider.options.label;
        nodes.push(header);
      }

      if (!list) {
        list = DomUtil.create('ul', 'geocoder-control-list', this._suggestions);
      }

      if (suggestionTextArray.indexOf(suggestion.text) === -1) {
        var suggestionItem = DomUtil.create('li', 'geocoder-control-suggestion', list);

        suggestionItem.innerHTML = suggestion.text;
        suggestionItem.provider = suggestion.provider;
        suggestionItem['data-magic-key'] = suggestion.magicKey;
        suggestionItem.unformattedText = suggestion.unformattedText;
      } else {
        for (var j = 0; j < list.childNodes.length; j++) {
          // if the same text already appears in the list of suggestions, append an additional ObjectID to its magicKey instead
          if (list.childNodes[j].innerHTML === suggestion.text) {
            list.childNodes[j]['data-magic-key'] += ',' + suggestion.magicKey;
          }
        }
      }
      suggestionTextArray.push(suggestion.text);
    }

    DomUtil.removeClass(this._input, 'geocoder-control-loading');

    nodes.push(list);

    return nodes;
  },

  _boundsFromResults: function (results) {
    if (!results.length) {
      return;
    }

    var nullIsland = latLngBounds([0, 0], [0, 0]);
    var resultBounds = [];
    var resultLatlngs = [];

    // collect the bounds and center of each result
    for (var i = results.length - 1; i >= 0; i--) {
      var result = results[i];

      resultLatlngs.push(result.latlng);

      // make sure bounds are valid and not 0,0. sometimes bounds are incorrect or not present
      if (result.bounds && result.bounds.isValid() && !result.bounds.equals(nullIsland)) {
        resultBounds.push(result.bounds);
      }
    }

    // form a bounds object containing all center points
    var bounds = latLngBounds(resultLatlngs);

    // and extend it to contain all bounds objects
    for (var j = 0; j < resultBounds.length; j++) {
      bounds.extend(resultBounds[j]);
    }

    return bounds;
  },

  clear: function () {
    this._suggestions.innerHTML = '';
    this._suggestions.style.display = 'none';
    this._input.value = '';

    if (this.options.collapseAfterResult) {
      this._input.placeholder = '';
      DomUtil.removeClass(this._wrapper, 'geocoder-control-expanded');
    }

    if (!this._map.scrollWheelZoom.enabled() && this._map.options.scrollWheelZoom) {
      this._map.scrollWheelZoom.enable();
    }
  },

  clearSuggestions: function () {
    if (this._nodes) {
      for (var k = 0; k < this._nodes.length; k++) {
        if (this._nodes[k].parentElement) {
          this._suggestions.removeChild(this._nodes[k]);
        }
      }
    }
  },

  _setupClick: function () {
    DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    this._input.focus();
  },

  disable: function () {
    this._input.disabled = true;
    DomUtil.addClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.removeListener(this._wrapper, 'click', this._setupClick, this);
  },

  enable: function () {
    this._input.disabled = false;
    DomUtil.removeClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);
  },

  getAttribution: function () {
    var attribs = [];

    for (var i = 0; i < this._providers.length; i++) {
      if (this._providers[i].options.attribution) {
        attribs.push(this._providers[i].options.attribution);
      }
    }

    return attribs.join(', ');
  },

  geocodeSuggestion: function (e) {
    var suggestionItem = e.target || e.srcElement;

    // make sure and point at the actual 'geocoder-control-suggestion'
    if (suggestionItem.classList.length < 1) {
      suggestionItem = suggestionItem.parentNode;
    }

    this._geosearchCore._geocode(suggestionItem.unformattedText, suggestionItem['data-magic-key'], suggestionItem.provider);
    this.clear();
  },

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    EsriUtil.setEsriAttribution(map);

    this._map = map;
    this._wrapper = DomUtil.create('div', 'geocoder-control');
    this._input = DomUtil.create('input', 'geocoder-control-input leaflet-bar', this._wrapper);
    this._input.title = this.options.title;

    if (this.options.expanded) {
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
      this._input.placeholder = this.options.placeholder;
    }

    this._suggestions = DomUtil.create('div', 'geocoder-control-suggestions leaflet-bar', this._wrapper);

    var credits = this._geosearchCore._getAttribution();

    if (map.attributionControl) {
      map.attributionControl.addAttribution(credits);
    }

    DomEvent.addListener(this._input, 'focus', function (e) {
      this._input.placeholder = this.options.placeholder;
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    }, this);

    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);

    // make sure both click and touch spawn an address/poi search
    DomEvent.addListener(this._suggestions, 'mousedown', this.geocodeSuggestion, this);
    DomEvent.addListener(this._suggestions, 'touchend', this.geocodeSuggestion, this);

    DomEvent.addListener(this._input, 'blur', function (e) {
      this.clear();
    }, this);

    DomEvent.addListener(this._input, 'keydown', function (e) {
      var text = (e.target || e.srcElement).value;

      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');

      var list = this._suggestions.querySelectorAll('.' + 'geocoder-control-suggestion');
      var selected = this._suggestions.querySelectorAll('.' + 'geocoder-control-selected')[0];
      var selectedPosition;

      for (var i = 0; i < list.length; i++) {
        if (list[i] === selected) {
          selectedPosition = i;
          break;
        }
      }

      switch (e.keyCode) {
        case 13:
          /*
            if an item has been selected, geocode it
            if focus is on the input textbox, geocode only if multiple results are allowed and more than two characters are present, or if a single suggestion is displayed.
            if less than two characters have been typed, abort the geocode
          */
          if (selected) {
            this._geosearchCore._geocode(selected.unformattedText, selected['data-magic-key'], selected.provider);
            this.clear();
          } else if (this.options.allowMultipleResults && text.length >= 2) {
            this._geosearchCore._geocode(this._input.value, undefined);
            this.clear();
          } else {
            if (list.length === 1) {
              DomUtil.addClass(list[0], 'geocoder-control-selected');
              this._geosearchCore._geocode(list[0].innerHTML, list[0]['data-magic-key'], list[0].provider);
            } else {
              this.clear();
              this._input.blur();
            }
          }
          DomEvent.preventDefault(e);
          break;
        case 38:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var previousItem = list[selectedPosition - 1];

          if (selected && previousItem) {
            DomUtil.addClass(previousItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[list.length - 1], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        case 40:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var nextItem = list[selectedPosition + 1];

          if (selected && nextItem) {
            DomUtil.addClass(nextItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[0], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        default:
          // when the input changes we should cancel all pending suggestion requests if possible to avoid result collisions
          for (var x = 0; x < this._geosearchCore._pendingSuggestions.length; x++) {
            var request = this._geosearchCore._pendingSuggestions[x];
            if (request && request.abort && !request.id) {
              request.abort();
            }
          }
          break;
      }
    }, this);

    DomEvent.addListener(this._input, 'keyup', Util.throttle(function (e) {
      var key = e.which || e.keyCode;
      var text = (e.target || e.srcElement).value;

      // require at least 2 characters for suggestions
      if (text.length < 2) {
        this._suggestions.innerHTML = '';
        this._suggestions.style.display = 'none';
        DomUtil.removeClass(this._input, 'geocoder-control-loading');
        return;
      }

      // if this is the escape key it will clear the input so clear suggestions
      if (key === 27) {
        this._suggestions.innerHTML = '';
        this._suggestions.style.display = 'none';
        return;
      }

      // if this is NOT the up/down arrows or enter make a suggestion
      if (key !== 13 && key !== 38 && key !== 40) {
        if (this._input.value !== this._lastValue) {
          this._lastValue = this._input.value;
          DomUtil.addClass(this._input, 'geocoder-control-loading');
          this._geosearchCore._suggest(text);
        }
      }
    }, 50, this), this);

    DomEvent.disableClickPropagation(this._wrapper);

    // when mouse moves over suggestions disable scroll wheel zoom if its enabled
    DomEvent.addListener(this._suggestions, 'mouseover', function (e) {
      if (map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.disable();
      }
    });

    // when mouse moves leaves suggestions enable scroll wheel zoom if its disabled
    DomEvent.addListener(this._suggestions, 'mouseout', function (e) {
      if (!map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.enable();
      }
    });

    this._geosearchCore.on('load', function (e) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');
      this.clear();
      this._input.blur();
    }, this);

    return this._wrapper;
  }
});

export function geosearch (options) {
  return new Geosearch(options);
}

export default geosearch;
<commit_msg>this is why pull requests are a good idea<commit_after>import {
  Control,
  DomEvent,
  DomUtil,
  Evented,
  Util,
  latLngBounds
} from 'leaflet';
import { geosearchCore } from '../Classes/GeosearchCore';
import { arcgisOnlineProvider } from '../Providers/ArcgisOnlineGeocoder';
import { Util as EsriUtil } from 'esri-leaflet';

export var Geosearch = Control.extend({
  includes: Evented.prototype,

  options: {
    position: 'topleft',
    collapseAfterResult: true,
    expanded: false,
    allowMultipleResults: true,
    placeholder: 'Search for places or addresses',
    title: 'Location Search'
  },

  initialize: function (options) {
    Util.setOptions(this, options);

    if (!options || !options.providers || !options.providers.length) {
      if (!options) {
        options = {};
      }
      options.providers = [ arcgisOnlineProvider() ];
    }

    // instantiate the underlying class and pass along options
    this._geosearchCore = geosearchCore(this, options);
    this._geosearchCore._providers = options.providers;

    // bubble each providers events to the control
    this._geosearchCore.addEventParent(this);
    for (var i = 0; i < this._geosearchCore._providers.length; i++) {
      this._geosearchCore._providers[i].addEventParent(this);
    }

    this._geosearchCore._pendingSuggestions = [];

    Control.prototype.initialize.call(options);
  },

  _renderSuggestions: function (suggestions) {
    var currentGroup;

    if (suggestions.length > 0) {
      this._suggestions.style.display = 'block';
    }
    // set the maxHeight of the suggestions box to
    // map height
    // - suggestions offset (distance from top of suggestions to top of control)
    // - control offset (distance from top of control to top of map)
    // - 10 (extra padding)
    this._suggestions.style.maxHeight = (this._map.getSize().y - this._suggestions.offsetTop - this._wrapper.offsetTop - 10) + 'px';

    var nodes = [];
    var list;
    var header;
    var suggestionTextArray = [];

    for (var i = 0; i < suggestions.length; i++) {
      var suggestion = suggestions[i];
      if (!header && this._geosearchCore._providers.length > 1 && currentGroup !== suggestion.provider.options.label) {
        header = DomUtil.create('span', 'geocoder-control-header', this._suggestions);
        header.textContent = suggestion.provider.options.label;
        header.innerText = suggestion.provider.options.label;
        currentGroup = suggestion.provider.options.label;
        nodes.push(header);
      }

      if (!list) {
        list = DomUtil.create('ul', 'geocoder-control-list', this._suggestions);
      }

      if (suggestionTextArray.indexOf(suggestion.text) === -1) {
        var suggestionItem = DomUtil.create('li', 'geocoder-control-suggestion', list);

        suggestionItem.innerHTML = suggestion.text;
        suggestionItem.provider = suggestion.provider;
        suggestionItem['data-magic-key'] = suggestion.magicKey;
        suggestionItem.unformattedText = suggestion.unformattedText;
      } else {
        for (var j = 0; j < list.childNodes.length; j++) {
          // if the same text already appears in the list of suggestions, append an additional ObjectID to its magicKey instead
          if (list.childNodes[j].innerHTML === suggestion.text) {
            list.childNodes[j]['data-magic-key'] += ',' + suggestion.magicKey;
          }
        }
      }
      suggestionTextArray.push(suggestion.text);
    }

    DomUtil.removeClass(this._input, 'geocoder-control-loading');

    nodes.push(list);

    return nodes;
  },

  _boundsFromResults: function (results) {
    if (!results.length) {
      return;
    }

    var nullIsland = latLngBounds([0, 0], [0, 0]);
    var resultBounds = [];
    var resultLatlngs = [];

    // collect the bounds and center of each result
    for (var i = results.length - 1; i >= 0; i--) {
      var result = results[i];

      resultLatlngs.push(result.latlng);

      // make sure bounds are valid and not 0,0. sometimes bounds are incorrect or not present
      if (result.bounds && result.bounds.isValid() && !result.bounds.equals(nullIsland)) {
        resultBounds.push(result.bounds);
      }
    }

    // form a bounds object containing all center points
    var bounds = latLngBounds(resultLatlngs);

    // and extend it to contain all bounds objects
    for (var j = 0; j < resultBounds.length; j++) {
      bounds.extend(resultBounds[j]);
    }

    return bounds;
  },

  clear: function () {
    this._suggestions.innerHTML = '';
    this._suggestions.style.display = 'none';
    this._input.value = '';

    if (this.options.collapseAfterResult) {
      this._input.placeholder = '';
      DomUtil.removeClass(this._wrapper, 'geocoder-control-expanded');
    }

    if (!this._map.scrollWheelZoom.enabled() && this._map.options.scrollWheelZoom) {
      this._map.scrollWheelZoom.enable();
    }
  },

  clearSuggestions: function () {
    if (this._nodes) {
      for (var k = 0; k < this._nodes.length; k++) {
        if (this._nodes[k].parentElement) {
          this._suggestions.removeChild(this._nodes[k]);
        }
      }
    }
  },

  _setupClick: function () {
    DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    this._input.focus();
  },

  disable: function () {
    this._input.disabled = true;
    DomUtil.addClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.removeListener(this._wrapper, 'click', this._setupClick, this);
  },

  enable: function () {
    this._input.disabled = false;
    DomUtil.removeClass(this._input, 'geocoder-control-input-disabled');
    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);
  },

  getAttribution: function () {
    var attribs = [];

    for (var i = 0; i < this._providers.length; i++) {
      if (this._providers[i].options.attribution) {
        attribs.push(this._providers[i].options.attribution);
      }
    }

    return attribs.join(', ');
  },

  geocodeSuggestion: function (e) {
    var suggestionItem = e.target || e.srcElement;

    // make sure and point at the actual 'geocoder-control-suggestion'
    if (suggestionItem.classList.length < 1) {
      suggestionItem = suggestionItem.parentNode;
    }

    this._geosearchCore._geocode(suggestionItem.unformattedText, suggestionItem['data-magic-key'], suggestionItem.provider);
    this.clear();
  },

  onAdd: function (map) {
    // include 'Powered by Esri' in map attribution
    EsriUtil.setEsriAttribution(map);

    this._map = map;
    this._wrapper = DomUtil.create('div', 'geocoder-control');
    this._input = DomUtil.create('input', 'geocoder-control-input leaflet-bar', this._wrapper);
    this._input.title = this.options.title;

    if (this.options.expanded) {
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
      this._input.placeholder = this.options.placeholder;
    }

    this._suggestions = DomUtil.create('div', 'geocoder-control-suggestions leaflet-bar', this._wrapper);

    var credits = this._geosearchCore._getAttribution();

    if (map.attributionControl) {
      map.attributionControl.addAttribution(credits);
    }

    DomEvent.addListener(this._input, 'focus', function (e) {
      this._input.placeholder = this.options.placeholder;
      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');
    }, this);

    DomEvent.addListener(this._wrapper, 'click', this._setupClick, this);

    // make sure both click and touch spawn an address/poi search
    DomEvent.addListener(this._suggestions, 'mousedown', this.geocodeSuggestion, this);
    DomEvent.addListener(this._suggestions, 'touchend', this.geocodeSuggestion, this);

    DomEvent.addListener(this._input, 'blur', function (e) {
      this.clear();
    }, this);

    DomEvent.addListener(this._input, 'keydown', function (e) {
      var text = (e.target || e.srcElement).value;

      DomUtil.addClass(this._wrapper, 'geocoder-control-expanded');

      var list = this._suggestions.querySelectorAll('.' + 'geocoder-control-suggestion');
      var selected = this._suggestions.querySelectorAll('.' + 'geocoder-control-selected')[0];
      var selectedPosition;

      for (var i = 0; i < list.length; i++) {
        if (list[i] === selected) {
          selectedPosition = i;
          break;
        }
      }

      switch (e.keyCode) {
        case 13:
          /*
            if an item has been selected, geocode it
            if focus is on the input textbox, geocode only if multiple results are allowed and more than two characters are present, or if a single suggestion is displayed.
            if less than two characters have been typed, abort the geocode
          */
          if (selected) {
            this._geosearchCore._geocode(selected.unformattedText, selected['data-magic-key'], selected.provider);
            this.clear();
          } else if (this.options.allowMultipleResults && text.length >= 2) {
            this._geosearchCore._geocode(this._input.value, undefined);
            this.clear();
          } else {
            if (list.length === 1) {
              DomUtil.addClass(list[0], 'geocoder-control-selected');
              this._geosearchCore._geocode(list[0].innerHTML, list[0]['data-magic-key'], list[0].provider);
            } else {
              this.clear();
              this._input.blur();
            }
          }
          DomEvent.preventDefault(e);
          break;
        case 38:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var previousItem = list[selectedPosition - 1];

          if (selected && previousItem) {
            DomUtil.addClass(previousItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[list.length - 1], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        case 40:
          if (selected) {
            DomUtil.removeClass(selected, 'geocoder-control-selected');
          }

          var nextItem = list[selectedPosition + 1];

          if (selected && nextItem) {
            DomUtil.addClass(nextItem, 'geocoder-control-selected');
          } else {
            DomUtil.addClass(list[0], 'geocoder-control-selected');
          }
          DomEvent.preventDefault(e);
          break;
        default:
          // when the input changes we should cancel all pending suggestion requests if possible to avoid result collisions
          for (var x = 0; x < this._geosearchCore._pendingSuggestions.length; x++) {
            var request = this._geosearchCore._pendingSuggestions[x];
            if (request && request.abort && !request.id) {
              request.abort();
            }
          }
          break;
      }
    }, this);

    DomEvent.addListener(this._input, 'keyup', Util.throttle(function (e) {
      var key = e.which || e.keyCode;
      var text = (e.target || e.srcElement).value;

      // require at least 2 characters for suggestions
      if (text.length < 2) {
        this._suggestions.innerHTML = '';
        this._suggestions.style.display = 'none';
        DomUtil.removeClass(this._input, 'geocoder-control-loading');
        return;
      }

      // if this is the escape key it will clear the input so clear suggestions
      if (key === 27) {
        this._suggestions.innerHTML = '';
        this._suggestions.style.display = 'none';
        return;
      }

      // if this is NOT the up/down arrows or enter make a suggestion
      if (key !== 13 && key !== 38 && key !== 40) {
        if (this._input.value !== this._lastValue) {
          this._lastValue = this._input.value;
          DomUtil.addClass(this._input, 'geocoder-control-loading');
          this._geosearchCore._suggest(text);
        }
      }
    }, 50, this), this);

    DomEvent.disableClickPropagation(this._wrapper);

    // when mouse moves over suggestions disable scroll wheel zoom if its enabled
    DomEvent.addListener(this._suggestions, 'mouseover', function (e) {
      if (map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.disable();
      }
    });

    // when mouse moves leaves suggestions enable scroll wheel zoom if its disabled
    DomEvent.addListener(this._suggestions, 'mouseout', function (e) {
      if (!map.scrollWheelZoom.enabled() && map.options.scrollWheelZoom) {
        map.scrollWheelZoom.enable();
      }
    });

    this._geosearchCore.on('load', function (e) {
      DomUtil.removeClass(this._input, 'geocoder-control-loading');
      this.clear();
      this._input.blur();
    }, this);

    return this._wrapper;
  }
});

export function geosearch (options) {
  return new Geosearch(options);
}

export default geosearch;
<|endoftext|>


<commit_before>(function($) {
/**
 * Image input widget. Uses FileAPI (http://mailru.github.io/FileAPI/)
 */
    function ImageInput(node, options) {
        var self = this;
        self.root_node = node;
        self.preview_node = node.find('.upload_preview').get(0);
        self.fileapi_node = node.find('.js-fileapi-wrapper input').get(0);

        var $preview = $(self.preview_node);
        self.width = $preview.data('width') || options.width;
        self.height = $preview.data('height') || options.height;

        FileAPI.event.on(self.fileapi_node, 'change', function(evt) {
            self.changeImage(evt);
        });
    }

    ImageInput.prototype = {
        changeImage: function(evt) {
            var self = this;
            var file = FileAPI.getFiles(evt)[0];
            var image = FileAPI.Image(file);
            var preview = image.preview(self.width, self.height, 'max');
            preview.get(function( err/**String*/, img/**HTMLElement*/ ) {
                while (self.preview_node.firstChild) {
                    self.preview_node.removeChild(self.preview_node.firstChild);
                }
                self.preview_node.appendChild(img);
            });
        }
    };

    $.fn.imageInput = function(options) {
        var defaults = { width: 120, height: 120 };
        var opts = $.extend(defaults, options);
        return this.each(
            function() {
                var node = $(this);
                this.image_input = new ImageInput(node, opts);
        });
    };

}(jQuery));
<commit_msg>make $.imageInput() return image input object<commit_after>(function($) {
/**
 * Image input widget. Uses FileAPI (http://mailru.github.io/FileAPI/)
 */
    function ImageInput(node, options) {
        var self = this;
        self.root_node = node;
        self.preview_node = node.find('.upload_preview').get(0);
        self.fileapi_node = node.find('.js-fileapi-wrapper input').get(0);

        var $preview = $(self.preview_node);
        self.width = $preview.data('width') || options.width;
        self.height = $preview.data('height') || options.height;

        FileAPI.event.on(self.fileapi_node, 'change', function(evt) {
            self.changeImage(evt);
        });
    }

    ImageInput.prototype = {
        changeImage: function(evt) {
            var self = this;
            var file = FileAPI.getFiles(evt)[0];
            var image = FileAPI.Image(file);
            var preview = image.preview(self.width, self.height, 'max');
            preview.get(function( err/**String*/, img/**HTMLElement*/ ) {
                while (self.preview_node.firstChild) {
                    self.preview_node.removeChild(self.preview_node.firstChild);
                }
                self.preview_node.appendChild(img);
            });
        }
    };

    $.fn.imageInput = function(options) {
        var defaults = { width: 120, height: 120 };
        var opts = $.extend(defaults, options);
        return this.each(
            function() {
                var node = $(this);
                var input = node.data('image-input');
                if (input === undefined) {
                    input = new ImageInput(node, opts);
                    node.data('image-input', input);
                }
                return input;
        });
    };

}(jQuery));
<|endoftext|>


<commit_before>"use strict";

const morgan = require("morgan");
const uuid = require("uuid");
const os = require("os");

const CUSTOMER_UUID = "customer-uuid";
const AUTH_INFO_USER_ID = "auth-info-user-id";

class ExpressMiddlewares {
  static accessLogMiddleware(
    serviceName,
    dockerMode,
    customTokens = {},
    accessLogOptions = {}
  ) {
    serviceName = serviceName || "unknown";
    const hostName = os.hostname();
    const serviceColor = process.env.SERVICE_COLOR || "unknown";
    const errorHandler = (request, response) => "error";
    const optKeys = [];

    // Check for additional access logs
    if (customTokens && typeof customTokens === "object") {
      for (const key in customTokens) {
        try {
          morgan.token(
            key,
            typeof customTokens[key] === "function"
              ? customTokens[key]
              : errorHandler
          );
        } catch (err) {
          morgan.token(key, errorHandler);
        }
        optKeys.push(`\"${key}\": \":${key}\"`);
      }
    }

    morgan.token("host_name", function getHostName(request, response) {
      return hostName;
    });

    morgan.token("service_name", function getHostName(request, response) {
      return serviceName;
    });

    morgan.token("uri", function getUri(request, response) {
      try {
        return request._parsedUrl.pathname;
      } catch (e) {
        return "error";
      }
    });

    morgan.token("query_string", function getQueryString(request) {
      try {
        return request._parsedUrl.query;
      } catch (e) {
        return "error";
      }
    });

    morgan.token("protocol", function getProtocol(request, response) {
      try {
        return request.secure ? "HTTPS" : "HTTP";
      } catch (e) {
        return "error";
      }
    });

    morgan.token("server_name", function getServerName(request, response) {
      try {
        return request.headers.host ? request.headers.host : "unknown";
      } catch (e) {
        return "error";
      }
    });

    morgan.token("service_color", function getServiceColor(request, response) {
      return serviceColor;
    });

    morgan.token("remote_client_id", function getRemoteClientId(
      request,
      response
    ) {
      let rcId = "";

      try {
        rcId = request.headers.customeruuid;

        if (request.headers[CUSTOMER_UUID]) {
          rcId = request.headers[CUSTOMER_UUID];
        }

        if (request.headers[AUTH_INFO_USER_ID]) {
          rcId = request.headers[AUTH_INFO_USER_ID];
        }

        if (response.locals.remoteClientId) {
          rcId = response.locals.remoteClientId;
        }

        if (!rcId) {
          rcId = "unknown";
        }
      } catch (e) {
        rcId = "error";
      }

      return rcId;
    });

    morgan.token("bytes_received", function getBytesReceived(
      request,
      response
    ) {
      try {
        if (typeof request.body === "string") {
          return Buffer.byteLength(request.body, "utf-8").toString();
        }

        return "0";
      } catch (e) {
        return "error";
      }
    });

    morgan.token("user_agent", function getUserAgent(request, response) {
      try {
        const userAgent = request.headers["user-agent"];
        if (typeof userAgent === "string") {
          return userAgent;
        }
        if (Array.isArray(userAgent) && userAgent.length > 0) {
          return userAgent[0];
        }
        return "";
      } catch (e) {
        return "error";
      }
    });

    return morgan(
      '{ "@timestamp": ":date[iso]", "host": ":host_name", "loglevel": "INFO",' +
        ' "correlation-id": ":req[correlation-id]", "application_type": "service", "log_type": "access",' +
        ' "service": ":service_name", "remote_address": ":remote-addr", "status": :status,' +
        ' "request_method": ":method", "uri": ":uri", "query_string": ":query_string",' +
        ' "response_time": :response-time, "protocol": ":protocol", "server_name": ":server_name",' +
        ' "current_color": ":service_color", "remote_client_id": ":remote_client_id",' +
        ' "user_agent": ":user_agent", ' +
        `${optKeys.length ? optKeys.join(", ") + "," : ""}` +
        ' "bytes_received": ":bytes_received" }',
      accessLogOptions
    );
  }

  accessLogMiddlewareFile(filePath, dockerMode) {
    morgan.token("uri", function getUri(request, response) {
      return request._parsedUrl.pathname;
    });

    morgan.token("query_string", function getQueryString(request) {
      return request._parsedUrl.query;
    });

    const accessLogStream = fs.createWriteStream(filePath, { flags: "a" });
    const hostName = os.hostname();

    return morgan(
      '{ "@timestamp": ":date[iso]", "host": "' +
        hostName +
        '", "loglevel": "INFO", "correlationId": ":req[correlation-id]", "application_type": "service", "log_type": "access", "remote_address": ":remote-addr", "status": :status, "request_method": ":method", "uri": ":uri", "query_string": ":query_string", "response_time": :response-time }',
      { stream: accessLogStream }
    );
  }
}

module.exports = ExpressMiddlewares;
<commit_msg>Revert wrong linting<commit_after>"use strict";

const morgan = require("morgan");
const uuid = require("uuid");
const os = require("os");

const CUSTOMER_UUID = "customer-uuid";
const AUTH_INFO_USER_ID = "auth-info-user-id";

class ExpressMiddlewares {

    static accessLogMiddleware(serviceName, dockerMode, customTokens = {}, accessLogOptions = {}) {

        serviceName = serviceName || "unknown";
        const hostName = os.hostname();
        const serviceColor = process.env.SERVICE_COLOR || "unknown";
        const errorHandler = (request, response) => "error";
        const optKeys = [];

        // Check for additional access logs
        if (customTokens && typeof customTokens === "object") {
            for (const key in customTokens) {
                try {
                    morgan.token(key, typeof customTokens[key] === "function" ? customTokens[key] : errorHandler);
                }
                catch(err) {
                    morgan.token(key, errorHandler);
                }
                optKeys.push(`\"${key}\": \":${key}\"`);
            }
        }

        morgan.token("host_name", function getHostName(request, response) {
            return hostName;
        });

        morgan.token("service_name", function getHostName(request, response) {
            return serviceName;
        });

        morgan.token("uri", function getUri(request, response) {
            try {
                return request._parsedUrl.pathname;
            } catch (e) {
                return "error";
            }
        });

        morgan.token("query_string", function getQueryString(request) {
            try {
                return request._parsedUrl.query;
            } catch (e) {
                return "error";
            }
        });

        morgan.token("protocol", function getProtocol(request, response) {
            try {
                return request.secure ? "HTTPS" : "HTTP";
            } catch (e) {
                return "error";
            }
        });

        morgan.token("server_name", function getServerName(request, response) {
            try {
                return request.headers.host ? request.headers.host : "unknown";
            } catch (e) {
                return "error";
            }
        });

        morgan.token("service_color", function getServiceColor(request, response) {
            return serviceColor;
        });

        morgan.token("remote_client_id", function getRemoteClientId(request, response) {

            let rcId = "";

            try {
                rcId = request.headers.customeruuid;

                if (request.headers[CUSTOMER_UUID]) {
                    rcId = request.headers[CUSTOMER_UUID];
                }

                if (request.headers[AUTH_INFO_USER_ID]) {
                    rcId = request.headers[AUTH_INFO_USER_ID];
                }

                if (response.locals.remoteClientId) {
                    rcId = response.locals.remoteClientId;
                  }

                if (!rcId) {
                    rcId = "unknown";
                }

            } catch (e) {
                rcId = "error";
            }

            return rcId;
        });

        morgan.token("bytes_received", function getBytesReceived(request, response) {
            try {

                if (typeof request.body === "string") {
                    return Buffer.byteLength(request.body, "utf-8").toString();
                }

                return "0";

            } catch (e) {
                return "error";
            }
        });

        morgan.token("user_agent", function getUserAgent(request, response) {
            try {
                const userAgent = request.headers["user-agent"];
                if (typeof userAgent === "string") {
                    return userAgent;
                }
                if (Array.isArray(userAgent) && userAgent.length > 0) {
                    return userAgent[0];
                }
                return "";
            } catch (e) {
                return "error";
            }
        });

        return morgan(
            "{ \"@timestamp\": \":date[iso]\", \"host\": \":host_name\", \"loglevel\": \"INFO\"," +
            " \"correlation-id\": \":req[correlation-id]\", \"application_type\": \"service\", \"log_type\": \"access\"," +
            " \"service\": \":service_name\", \"remote_address\": \":remote-addr\", \"status\": :status," +
            " \"request_method\": \":method\", \"uri\": \":uri\", \"query_string\": \":query_string\"," +
            " \"response_time\": :response-time, \"protocol\": \":protocol\", \"server_name\": \":server_name\"," +
            " \"current_color\": \":service_color\", \"remote_client_id\": \":remote_client_id\"," +
            " \"user_agent\": \":user_agent\", " +
            `${optKeys.length ? optKeys.join(", ") + "," : ""}` + " \"bytes_received\": \":bytes_received\" }",
            accessLogOptions);
    }

    accessLogMiddlewareFile(filePath, dockerMode) {

        morgan.token("uri", function getUri(request, response) {
            return request._parsedUrl.pathname;
        });

        morgan.token("query_string", function getQueryString(request) {
            return request._parsedUrl.query;
        });

        const accessLogStream = fs.createWriteStream(filePath, {flags: 'a'});
        const hostName = os.hostname();

        return morgan(
            "{ \"@timestamp\": \":date[iso]\", \"host\": \"" + hostName + "\", \"loglevel\": \"INFO\", \"correlationId\": \":req[correlation-id]\", \"application_type\": \"service\", \"log_type\": \"access\", \"remote_address\": \":remote-addr\", \"status\": :status, \"request_method\": \":method\", \"uri\": \":uri\", \"query_string\": \":query_string\", \"response_time\": :response-time }",
            {stream: accessLogStream});
    }
};

module.exports = ExpressMiddlewares;
<|endoftext|>


<commit_before>'use strict';

/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/**
 * This module has "for debug" code.
 */

var ForDebug = {};

ForDebug.updateDisplay_ = function() {
  setTimeout(function() {
    if (tachyfont.timer1.numberOfTimingRecords)
      if (tachyfont.timer1.numberOfTimingRecords()) {
        tachyfont.timer1.display_timing();
      }
    if (tachyfont.timer2.numberOfTimingRecords)
      if (tachyfont.timer2.numberOfTimingRecords()) {
        tachyfont.timer2.display_timing();
      }
  }, 1);
};

/**
 * If timing data was collected display them on DOM ready.
 */
if (document.readyState == 'loading') {
  document.addEventListener("DOMContentLoaded", function(event) {
    ForDebug.updateDisplay_();
  });
} else {
  ForDebug.updateDisplay_();
}

ForDebug.getCookie = function(name, fallback) {
  name += '=';
  var cookies = document.cookie.split(';');
  for(var i = 0; i < cookies.length; i++) {
    var cookie = cookies[i];
    while (cookie.charAt(0)==' ') {
      cookie = cookie.substring(1);
    }
    if (cookie.indexOf(name) != -1) {
      return cookie.substring(name.length, cookie.length);
    }
  }
  return fallback;

};

ForDebug.Bandwidths = [
  { name: 'Full', value: '0' },
  { name: '10 Kbps', value: '10' },
  { name: '100 Kbps', value: '100' },
  { name: '250 Kbps', value: '250' },
  { name: 'Argentina - 1.0 Mbps', value: '1000' },
  { name: 'India - 1.3 Mbps', value: '1300' },
  { name: '3G moving - 384 Kbps', value: '384' },
  { name: '3G stationary - 1.6 Mbps', value: '1600' },
  { name: 'China - 4.8 Mbps', value: '4800' },
  { name: 'Japan - 5.7 Mbps', value: '5700' },
  { name: 'South Korea - 14.7 Mbps', value: '14700' },
  { name: 'Taiwan - 3.4 Mbps', value: '3400' },
];

/**
 * Add a Bandwidth control.
 *
 * @param {numbber} rightOffset The offset from the right.
 * @param {numbber} topOffset The offset from the top.
 */
ForDebug.addBandwidthControl = function(rightOffset, topOffset) {
  if (typeof rightOffset != 'number' || typeof topOffset != 'number') {
    debugger;
    return;
  }
  var label_innerHTML = "Akami mobile bandwidth " +
  "(2014 report pg 31): ";

  ForDebug.addDropDownCookieControl(ForDebug.Bandwidths, rightOffset, topOffset,
    'lightYellow', label_innerHTML, 'bandwidth');
};

ForDebug.TimingTextSizes = [
  { name: 'Extra Large', value: '200%' },
  { name: 'Large', value: '150%' },
  { name: 'Regular', value: '100%' },
];

/**
 * Add a control to set the timing text size.
 * For the text to be readable on WebPageTest.org it needs to be quite large.
 * For other uses it can be a normal size
 * @param {number} rightOffset The offset from the right.
 * @param {number} topOffset The offset from the top.
 */
ForDebug.addTimingTextSizeControl = function(rightOffset, topOffset) {
  if (typeof rightOffset != 'number' || typeof topOffset != 'number') {
    debugger;
    return;
  }

  ForDebug.addDropDownCookieControl(ForDebug.TimingTextSizes, rightOffset, topOffset,
    'lightYellow', "Timing Text Size", 'timing-text-size');
};

/**
 * Add a control to set the timing text size.
 * For the text to be readable on WebPageTest.org it needs to be quite large.
 * For other uses it can be a normal size
 * @param {Array.<Object.<string,string>>} options The drop down option values.
 * @param {number} rightOffset The offset from the right.
 * @param {number} topOffset The offset from the top.
 */
ForDebug.addDropDownCookieControl = function(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name) {
  if (document.readyState == 'loading') {
    document.addEventListener("DOMContentLoaded", function(event) {
      ForDebug.addDropDownCookieControl_(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name);
    });
  } else {
    ForDebug.addDropDownCookieControl_(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name);
  }
};

ForDebug.addDropDownCookieControl_ = function(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name) {
  var span = document.createElement('span');
  span.style.position = 'absolute';
  span.style.top = topOffset + 'px';
  span.style.right = rightOffset + 'px';
  span.style.backgroundColor = background_color;
  span.style.border = '1px solid gray';
  var label = document.createElement('span');
  label.innerHTML = label_innerHTML + ": ";
  span.appendChild(label);

  var cookie_value = ForDebug.getCookie(cookie_name, '0');
  var select = document.createElement("select");
  select.selectedIndex = 0;
  for (var i = 0; i < options.length; i++) {
    var option_info = options[i];
    var option = document.createElement("option");
    option.text = option_info.name;
    option.value = option_info.value;
    select.add(option);
    if (cookie_value == option_info.value) {
      select.selectedIndex = i;
    }
  }

  function setOptionChange(select) {
    var selectedOption = select.options[select.selectedIndex];
    document.cookie = cookie_name + '=' + selectedOption.value;
  }
  setOptionChange(select);

  select.onchange = function(event) {
    var select = event.currentTarget;
    setOptionChange(select);
  };

  span.appendChild(select);
  document.body.appendChild(span);
};

/**
 * Add a "drop DB" button.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 */
ForDebug.addDropIdbButton = function(incrFontMgr, fontname) {
  if (document.readyState == 'loading') {
    document.addEventListener("DOMContentLoaded", function(event) {
      ForDebug.addDropIdbButton_(incrFontMgr, fontname);
    });
  } else {
    ForDebug.addDropIdbButton_(incrFontMgr, fontname);
  }
};

/**
 * Add a "drop DB" button.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 */
ForDebug.addDropIdbButton_ = function(incrFontMgr, fontname) {
  var span = document.createElement('span');
  span.style.position = 'absolute';
  span.style.top = '10px';
  span.style.right = '10px';
  span.style.backgroundColor = 'white';
  span.style.border = '1px solid gray';
  var msg_span = document.createElement('span');
  msg_span.id = 'dropIdb_msg';
  span.appendChild(msg_span);
  var button = document.createElement('button');
  button.onclick = dropIdb;
  var label = document.createTextNode('drop IndexedDB data');
  button.appendChild(label);
  span.appendChild(button);

  document.body.appendChild(span);

  function dropIdb() {
    var msg_span = document.getElementById('dropIdb_msg');
    ForDebug.dropIdb_(incrFontMgr, fontname, function(msg) {
      msg_span.innerHTML = msg;
    });
  }
};

/**
 * Drop the IndexedDB database.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 * @param {function} call Call this function with the status.
 * @return {Promise} The Promise for when the DB is dropped.
 * @private
 */
ForDebug.dropIdb_ = function(incrFontMgr, fontname, callback) {
  var db_name = tachyfont.IncrementalFont.DB_NAME + '/' + fontname;
  return incrFontMgr.getIDB_
  .then(function(db) {
    db.close();
    callback('dropping ' + db_name);
    var request = indexedDB.deleteDatabase(db_name);
    request.onsuccess = function(e) {
      callback('dropped ' + db_name);
    };
    request.onblocked = function() {
      callback('blocked dropping ' + db_name);
    };
    request.onerror = function(e) {
      debugger;
      callback('failed to drop ' + db_name + ': ' + e.code + '/' + e.message);
    };
  });
};

<commit_msg>a sample change<commit_after>'use strict';

/*
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

/* some change */
/**
 * This module has "for debug" code.
 */

var ForDebug = {};

ForDebug.updateDisplay_ = function() {
  setTimeout(function() {
    if (tachyfont.timer1.numberOfTimingRecords)
      if (tachyfont.timer1.numberOfTimingRecords()) {
        tachyfont.timer1.display_timing();
      }
    if (tachyfont.timer2.numberOfTimingRecords)
      if (tachyfont.timer2.numberOfTimingRecords()) {
        tachyfont.timer2.display_timing();
      }
  }, 1);
};

/**
 * If timing data was collected display them on DOM ready.
 */
if (document.readyState == 'loading') {
  document.addEventListener("DOMContentLoaded", function(event) {
    ForDebug.updateDisplay_();
  });
} else {
  ForDebug.updateDisplay_();
}

ForDebug.getCookie = function(name, fallback) {
  name += '=';
  var cookies = document.cookie.split(';');
  for(var i = 0; i < cookies.length; i++) {
    var cookie = cookies[i];
    while (cookie.charAt(0)==' ') {
      cookie = cookie.substring(1);
    }
    if (cookie.indexOf(name) != -1) {
      return cookie.substring(name.length, cookie.length);
    }
  }
  return fallback;

};

ForDebug.Bandwidths = [
  { name: 'Full', value: '0' },
  { name: '10 Kbps', value: '10' },
  { name: '100 Kbps', value: '100' },
  { name: '250 Kbps', value: '250' },
  { name: 'Argentina - 1.0 Mbps', value: '1000' },
  { name: 'India - 1.3 Mbps', value: '1300' },
  { name: '3G moving - 384 Kbps', value: '384' },
  { name: '3G stationary - 1.6 Mbps', value: '1600' },
  { name: 'China - 4.8 Mbps', value: '4800' },
  { name: 'Japan - 5.7 Mbps', value: '5700' },
  { name: 'South Korea - 14.7 Mbps', value: '14700' },
  { name: 'Taiwan - 3.4 Mbps', value: '3400' },
];

/**
 * Add a Bandwidth control.
 *
 * @param {numbber} rightOffset The offset from the right.
 * @param {numbber} topOffset The offset from the top.
 */
ForDebug.addBandwidthControl = function(rightOffset, topOffset) {
  if (typeof rightOffset != 'number' || typeof topOffset != 'number') {
    debugger;
    return;
  }
  var label_innerHTML = "Akami mobile bandwidth " +
  "(2014 report pg 31): ";

  ForDebug.addDropDownCookieControl(ForDebug.Bandwidths, rightOffset, topOffset,
    'lightYellow', label_innerHTML, 'bandwidth');
};

ForDebug.TimingTextSizes = [
  { name: 'Extra Large', value: '200%' },
  { name: 'Large', value: '150%' },
  { name: 'Regular', value: '100%' },
];

/**
 * Add a control to set the timing text size.
 * For the text to be readable on WebPageTest.org it needs to be quite large.
 * For other uses it can be a normal size
 * @param {number} rightOffset The offset from the right.
 * @param {number} topOffset The offset from the top.
 */
ForDebug.addTimingTextSizeControl = function(rightOffset, topOffset) {
  if (typeof rightOffset != 'number' || typeof topOffset != 'number') {
    debugger;
    return;
  }

  ForDebug.addDropDownCookieControl(ForDebug.TimingTextSizes, rightOffset, topOffset,
    'lightYellow', "Timing Text Size", 'timing-text-size');
};

/**
 * Add a control to set the timing text size.
 * For the text to be readable on WebPageTest.org it needs to be quite large.
 * For other uses it can be a normal size
 * @param {Array.<Object.<string,string>>} options The drop down option values.
 * @param {number} rightOffset The offset from the right.
 * @param {number} topOffset The offset from the top.
 */
ForDebug.addDropDownCookieControl = function(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name) {
  if (document.readyState == 'loading') {
    document.addEventListener("DOMContentLoaded", function(event) {
      ForDebug.addDropDownCookieControl_(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name);
    });
  } else {
    ForDebug.addDropDownCookieControl_(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name);
  }
};

ForDebug.addDropDownCookieControl_ = function(options, rightOffset, topOffset, background_color, label_innerHTML, cookie_name) {
  var span = document.createElement('span');
  span.style.position = 'absolute';
  span.style.top = topOffset + 'px';
  span.style.right = rightOffset + 'px';
  span.style.backgroundColor = background_color;
  span.style.border = '1px solid gray';
  var label = document.createElement('span');
  label.innerHTML = label_innerHTML + ": ";
  span.appendChild(label);

  var cookie_value = ForDebug.getCookie(cookie_name, '0');
  var select = document.createElement("select");
  select.selectedIndex = 0;
  for (var i = 0; i < options.length; i++) {
    var option_info = options[i];
    var option = document.createElement("option");
    option.text = option_info.name;
    option.value = option_info.value;
    select.add(option);
    if (cookie_value == option_info.value) {
      select.selectedIndex = i;
    }
  }

  function setOptionChange(select) {
    var selectedOption = select.options[select.selectedIndex];
    document.cookie = cookie_name + '=' + selectedOption.value;
  }
  setOptionChange(select);

  select.onchange = function(event) {
    var select = event.currentTarget;
    setOptionChange(select);
  };

  span.appendChild(select);
  document.body.appendChild(span);
};

/**
 * Add a "drop DB" button.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 */
ForDebug.addDropIdbButton = function(incrFontMgr, fontname) {
  if (document.readyState == 'loading') {
    document.addEventListener("DOMContentLoaded", function(event) {
      ForDebug.addDropIdbButton_(incrFontMgr, fontname);
    });
  } else {
    ForDebug.addDropIdbButton_(incrFontMgr, fontname);
  }
};

/**
 * Add a "drop DB" button.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 */
ForDebug.addDropIdbButton_ = function(incrFontMgr, fontname) {
  var span = document.createElement('span');
  span.style.position = 'absolute';
  span.style.top = '10px';
  span.style.right = '10px';
  span.style.backgroundColor = 'white';
  span.style.border = '1px solid gray';
  var msg_span = document.createElement('span');
  msg_span.id = 'dropIdb_msg';
  span.appendChild(msg_span);
  var button = document.createElement('button');
  button.onclick = dropIdb;
  var label = document.createTextNode('drop IndexedDB data');
  button.appendChild(label);
  span.appendChild(button);

  document.body.appendChild(span);

  function dropIdb() {
    var msg_span = document.getElementById('dropIdb_msg');
    ForDebug.dropIdb_(incrFontMgr, fontname, function(msg) {
      msg_span.innerHTML = msg;
    });
  }
};

/**
 * Drop the IndexedDB database.
 * @param {Object} incrFontMgr The incremental font manager.
 * @param {String} fontname The fontname.
 * @param {function} call Call this function with the status.
 * @return {Promise} The Promise for when the DB is dropped.
 * @private
 */
ForDebug.dropIdb_ = function(incrFontMgr, fontname, callback) {
  var db_name = tachyfont.IncrementalFont.DB_NAME + '/' + fontname;
  return incrFontMgr.getIDB_
  .then(function(db) {
    db.close();
    callback('dropping ' + db_name);
    var request = indexedDB.deleteDatabase(db_name);
    request.onsuccess = function(e) {
      callback('dropped ' + db_name);
    };
    request.onblocked = function() {
      callback('blocked dropping ' + db_name);
    };
    request.onerror = function(e) {
      debugger;
      callback('failed to drop ' + db_name + ': ' + e.code + '/' + e.message);
    };
  });
};

<|endoftext|>


<commit_before>var request = require('request');

exports.BlockchainDotInfo = function() {

    this.address = function(addr, handler) {
        var url = "https://blockchain.info/address/" + addr + "?format=json";
        request(url, function(err, response, body) {
            var result = {
                address: addr,
                balance: body.final_balance,
                total_received_net: body.total_received,
                total_sent_net: body.total_sent,
                tx_count: body.n_tx,
                txs: [], // TODO
                utxos: [], // TODO
            };
            handler(null, result);
        });
    }

}
<commit_msg>blockchain.info source parses json before using it<commit_after>var request = require('request');

exports.BlockchainDotInfo = function() {

    this.address = function(addr, handler) {
        var url = "https://blockchain.info/address/" + addr + "?format=json";
        request(url, function(err, response, body) {
            var data = JSON.parse(body);
            var result = {
                address: addr,
                balance: data.final_balance,
                total_received_net: data.total_received,
                total_sent_net: data.total_sent,
                tx_count: data.n_tx,
                txs: [], // TODO
                utxos: [], // TODO
            };
            handler(null, result);
        });
    }

}
<|endoftext|>


<commit_before>/* global $ helper requests sidebar */
'use strict';
// ==UserScript==
// @name         MusicBrainz edit: Display acoustIDs and merge recordings with common acoustID
// @namespace    mbz-loujine
// @author       loujine
// @version      2020.9.14
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-edit-merge_from_acoustid.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-edit-merge_from_acoustid.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org: Display acoustIDs and merge recordings with common acoustID
// @compatible   firefox+tampermonkey
// @license      MIT
// @require      https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-loujine-common.js
// @include      http*://*musicbrainz.org/work/*
// @exclude      http*://*musicbrainz.org/work/create
// @exclude      http*://*musicbrainz.org/work/*/*
// @include      http*://*musicbrainz.org/artist/*/relationships
// @include      http*://*musicbrainz.org/artist/*/recordings
// @include      http*://*musicbrainz.org/artist/*/recordings?page=*
// @grant        none
// @run-at       document-end
// ==/UserScript==

function showAcoustids() {
    const $recordings = $('table a[href*="/recording/"]');
    const recording_mbids = $recordings.map(function() {
        return this.href.split('/')[4]; // eslint-disable-line no-invalid-this
    }).get();
    const url = '//api.acoustid.org/v2/track/list_by_mbid';
    const application_api_key = 'P9e1TIJs7g';
    let params = 'client=' + application_api_key;
    params += '&mbid=' + recording_mbids.join('&mbid=');
    params += '&batch=1&disabled=0';

    $('thead > tr').append('AcoustID	');
    if ($('.subh > th').length > 1) {
        $('.subh > th')[1].colSpan += 1;
    }
    $('table.tbl > tbody > tr:not(".subh")').append('');

    requests.POST(url, params, function success(xhr) {
        const resp_mbids = JSON.parse(xhr.responseText).mbids;
        $recordings.each(function (idx, recording) {
            const acids = resp_mbids[idx].tracks.map(function (track) {
                return track.id;
            });
            $(recording).parents('tr').find('td:last').append(
                acids.map(function (acid) {
                    return $('', {
                        'href': '//acoustid.org/track/' + acid,
                        'target': '_blank'
                    }).append(
                        $('', {
                            'text': acid.slice(0, 6),
                            'data-acid': acid,
                            'data-recid': helper.mbidFromURL(recording.href),
                            'class': 'acoustID'
                        })
                    ).prepend($('
'))
                })
            );
        });
        const nodes = document.getElementsByClassName('acoustID');
        const ids = {};
        for (const node of nodes) {
            const acid = node.getAttribute('data-acid');
            if (!Object.keys(ids).includes(acid)) {
                ids[acid] = [];
            }
            ids[acid].push(node.getAttribute('data-recid'));
        }
        let duplicate_ids = Object.keys(ids).filter(
            // true if distinct recordings use the same acoustID
            acid => new Set(ids[acid]).size > 1
        );
        duplicate_ids.forEach(function (acid) {
            $('#acidForMerge').append(
                '<option value="' + acid + '">' + acid.slice(0, 6) + '</option>'
            );
        });
        duplicate_ids = duplicate_ids.map(function (acid) {
            return acid.slice(0, 6);
        });
        $(nodes).each(function (idx, node) {
            if (duplicate_ids.includes(node.textContent)) {
                $(node).css('background-color', '#' + node.textContent);
            }
        });
    });
}

function mergeFromAcoustID() {
    const acid = $('#acidForMerge')[0].value;
    const url = '//api.acoustid.org/v2/lookup';
    const application_api_key = 'P9e1TIJs7g';
    let params = 'client=' + application_api_key;
    params += '&meta=recordingids';
    params += '&trackid=' + acid;
    requests.POST(url, params, function success(xhr) {
        const recordings = JSON.parse(xhr.responseText).results[0].recordings;
        const ids = [];
        recordings.forEach(function (recording) {
            const url = '/ws/js/entity/' + recording.id;
            requests.GET(url, function (resp) {
                ids.push(JSON.parse(resp).id);
            });
        });
        setTimeout(function () {
            const url =
                '/recording/merge_queue?add-to-merge=' +
                ids.join('&add-to-merge=');
            console.log('Merge URL is ' + url);
            window.open(url);
        }, 1000);
    });
}

(function displaySidebar() {
    sidebar.container().insertAdjacentHTML('beforeend', `

Show acoustIDs

        <input type="button" id="showAcoustids" value="Show acoustIDs">

Merge from acoustID

        <select id="acidForMerge"><option value="">acoustID</option></select>
        <input type="button" id="merge" value="Merge">
    `);
})();

$(document).ready(function() {
    document.getElementById('showAcoustids').addEventListener(
        'click', showAcoustids);
    document.getElementById('merge').addEventListener(
        'click', mergeFromAcoustID);
    return false;
});
<commit_msg>[acoustid merge] Sort acoustIDs in dropdown<commit_after>/* global $ helper requests sidebar */
'use strict';
// ==UserScript==
// @name         MusicBrainz edit: Display acoustIDs and merge recordings with common acoustID
// @namespace    mbz-loujine
// @author       loujine
// @version      2020.9.22
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-edit-merge_from_acoustid.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-edit-merge_from_acoustid.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org: Display acoustIDs and merge recordings with common acoustID
// @compatible   firefox+tampermonkey
// @license      MIT
// @require      https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-loujine-common.js
// @include      http*://*musicbrainz.org/work/*
// @exclude      http*://*musicbrainz.org/work/create
// @exclude      http*://*musicbrainz.org/work/*/*
// @include      http*://*musicbrainz.org/artist/*/relationships
// @include      http*://*musicbrainz.org/artist/*/recordings
// @include      http*://*musicbrainz.org/artist/*/recordings?page=*
// @grant        none
// @run-at       document-end
// ==/UserScript==

function showAcoustids() {
    const $recordings = $('table a[href*="/recording/"]');
    const recording_mbids = $recordings.map(function() {
        return this.href.split('/')[4]; // eslint-disable-line no-invalid-this
    }).get();
    const url = '//api.acoustid.org/v2/track/list_by_mbid';
    const application_api_key = 'P9e1TIJs7g';
    let params = 'client=' + application_api_key;
    params += '&mbid=' + recording_mbids.join('&mbid=');
    params += '&batch=1&disabled=0';

    $('thead > tr').append('AcoustID	');
    if ($('.subh > th').length > 1) {
        $('.subh > th')[1].colSpan += 1;
    }
    $('table.tbl > tbody > tr:not(".subh")').append('');

    requests.POST(url, params, function success(xhr) {
        const resp_mbids = JSON.parse(xhr.responseText).mbids;
        $recordings.each(function (idx, recording) {
            const acids = resp_mbids[idx].tracks.map(function (track) {
                return track.id;
            });
            $(recording).parents('tr').find('td:last').append(
                acids.map(function (acid) {
                    return $('', {
                        'href': '//acoustid.org/track/' + acid,
                        'target': '_blank'
                    }).append(
                        $('', {
                            'text': acid.slice(0, 6),
                            'data-acid': acid,
                            'data-recid': helper.mbidFromURL(recording.href),
                            'class': 'acoustID'
                        })
                    ).prepend($('
'))
                })
            );
        });
        const nodes = document.getElementsByClassName('acoustID');
        const ids = {};
        for (const node of nodes) {
            const acid = node.getAttribute('data-acid');
            if (!Object.keys(ids).includes(acid)) {
                ids[acid] = [];
            }
            ids[acid].push(node.getAttribute('data-recid'));
        }
        let duplicate_ids = Object.keys(ids).filter(
            // true if distinct recordings use the same acoustID
            acid => new Set(ids[acid]).size > 1
        );
        duplicate_ids.sort().map(acid =>
            $('#acidForMerge').append(
                '<option value="' + acid + '">' + acid.slice(0, 6) + '</option>'
            )
        );
        duplicate_ids = duplicate_ids.map(acid => acid.slice(0, 6));
        $(nodes).each(function (idx, node) {
            if (duplicate_ids.includes(node.textContent)) {
                $(node).css('background-color', '#' + node.textContent);
            }
        });
    });
}

function mergeFromAcoustID() {
    const acid = $('#acidForMerge')[0].value;
    const url = '//api.acoustid.org/v2/lookup';
    const application_api_key = 'P9e1TIJs7g';
    let params = 'client=' + application_api_key;
    params += '&meta=recordingids';
    params += '&trackid=' + acid;
    requests.POST(url, params, function success(xhr) {
        const recordings = JSON.parse(xhr.responseText).results[0].recordings;
        const ids = [];
        recordings.forEach(function (recording) {
            const url = '/ws/js/entity/' + recording.id;
            requests.GET(url, function (resp) {
                ids.push(JSON.parse(resp).id);
            });
        });
        setTimeout(function () {
            const url =
                '/recording/merge_queue?add-to-merge=' +
                ids.join('&add-to-merge=');
            console.log('Merge URL is ' + url);
            window.open(url);
        }, 1000);
    });
}

(function displaySidebar() {
    sidebar.container().insertAdjacentHTML('beforeend', `

Show acoustIDs

        <input type="button" id="showAcoustids" value="Show acoustIDs">

Merge from acoustID

        <select id="acidForMerge"><option value="">acoustID</option></select>
        <input type="button" id="merge" value="Merge">
    `);
})();

$(document).ready(function() {
    document.getElementById('showAcoustids').addEventListener(
        'click', showAcoustids);
    document.getElementById('merge').addEventListener(
        'click', mergeFromAcoustID);
    return false;
});
<|endoftext|>


<commit_before>/**
 * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 * @namespace
 */
openerp.web.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.web.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.web.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            this._super(results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        }
    });

    openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
        passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super(event);
            }
            this.edit_record($(event.currentTarget).data('id'));
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_id != null) {
                this.reload_record(self.records.get(this.edition_id)).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.view.unpad_columns();
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_id;
                delete self.edition;
            });
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form
         * view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var record_id = $(row).data('id');
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': record_id,
                        'class': $(row).attr('class') + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button.oe-edit-row-cancel', 'click', function () {
                        self.cancel_edition();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function (e) {
                        switch (e.which) {
                            case KEY_RETURN:
                                self.save_row(true);
                                break;
                            case KEY_ESCAPE:
                                self.cancel_edition();
                                break;
                            default:
                                return;
                        }
                    });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable === 'top') {
                    self.$current.prepend($new_row);
                } else if (self.options.editable) {
                    self.$current.append($new_row);
                }
                self.edition = true;
                self.edition_id = record_id;
                self.edition_form = _.extend(new openerp.web.FormView(
                        self, $new_row.attr('id'), self.dataset, false), {
                    template: 'ListView.row.form',
                    registry: openerp.web.list.form.widgets
                });
                $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:first').removeClass('oe-field-cell').end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });
                    // Add columns for the cancel and save buttons, if
                    // there are none in the list
                    if (!self.options.selectable) {
                        self.view.pad_columns(
                            1, {except: $new_row, position: 'before'});
                    }
                    if (!self.options.deletable) {
                        self.view.pad_columns(
                            1, {except: $new_row});
                    }

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (source_record_id) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [source_record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        // insert after the source record
                        var index = self.records.indexOf(
                            self.records.get(source_record_id)) + 1;
                        record = new openerp.web.list.Record({id: id});
                        self.records.add(record, {at: index});
                        self.dataset.ids.splice(index, 0, id);
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                if (result.created && !self.edition_id) {
                    self.records.add({id: result.result},
                        {at: self.options.editable === 'top' ? 0 : null});
                    self.edition_id = result.result;
                }
                var edited_record = self.records.get(self.edition_id),
                    next_record = self.records.at(
                            self.records.indexOf(edited_record) + 1);

                self.handle_onwrite(self.edition_id);
                self.cancel_pending_edition().then(function () {
                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    var next_record_id;
                    if (next_record) {
                        next_record_id = next_record.get('id');
                        self.dataset.index = _(self.dataset.ids)
                                .indexOf(next_record_id);
                    } else {
                        self.dataset.index = 0;
                        next_record_id = self.records.at(0).get('id');
                    }
                    self.edit_record(next_record_id);
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function (record_id) {
            this.render_row_as_form(
                this.$current.find('[data-id=' + record_id + ']'));
            $(this).trigger(
                'edit',
                [record_id, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    if (!openerp.web.list) {
        openerp.web.list = {};
    }
    if (!openerp.web.list.form) {
        openerp.web.list.form = {};
    }
    openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.web.form.widgets;
    openerp.web.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.web.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.web.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.web.list.form.' + key;

        openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = !!this.modifiers.tree_invisible;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<commit_msg>[FIX] jsdoc: .include lends to the instances (Class#), not to the class<commit_after>/**
 * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out
 * @namespace
 */
openerp.web.list_editable = function (openerp) {
    var KEY_RETURN = 13,
        KEY_ESCAPE = 27;

    // editability status of list rows
    openerp.web.ListView.prototype.defaults.editable = null;

    // TODO: not sure second @lends on existing item is correct, to check
    openerp.web.ListView.include(/** @lends openerp.web.ListView# */{
        init: function () {
            var self = this;
            this._super.apply(this, arguments);
            $(this.groups).bind({
                'edit': function (e, id, dataset) {
                    self.do_edit(dataset.index, id, dataset);
                },
                'saved': function () {
                    if (self.groups.get_selection().length) {
                        return;
                    }
                    self.compute_aggregates();
                }
            })
        },
        /**
         * Handles the activation of a record in editable mode (making a record
         * editable), called *after* the record has become editable.
         *
         * The default behavior is to setup the listview's dataset to match
         * whatever dataset was provided by the editing List
         *
         * @param {Number} index index of the record in the dataset
         * @param {Object} id identifier of the record being edited
         * @param {openerp.web.DataSet} dataset dataset in which the record is available
         */
        do_edit: function (index, id, dataset) {
            _.extend(this.dataset, dataset);
        },
        /**
         * Sets editability status for the list, based on defaults, view
         * architecture and the provided flag, if any.
         *
         * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
         */
        set_editable: function (force) {
            // If ``force``, set editability to bottom
            // otherwise rely on view default
            // view' @editable is handled separately as we have not yet
            // fetched and processed the view at this point.
            this.options.editable = (
                    (force && "bottom")
                    || this.defaults.editable);
        },
        /**
         * Replace do_actual_search to handle editability process
         */
        do_actual_search: function (results) {
            this.set_editable(results.context['set_editable']);
            this._super(results);
        },
        /**
         * Replace do_add_record to handle editability (and adding new record
         * as an editable row at the top or bottom of the list)
         */
        do_add_record: function () {
            if (this.options.editable) {
                this.groups.new_record();
            } else {
                this._super();
            }
        },
        on_loaded: function (data, grouped) {
            // tree/@editable takes priority on everything else if present.
            this.options.editable = data.arch.attrs.editable || this.options.editable;
            return this._super(data, grouped);
        }
    });

    openerp.web.ListView.Groups.include(/** @lends openerp.web.ListView.Groups# */{
        passtrough_events: openerp.web.ListView.Groups.prototype.passtrough_events + " edit saved",
        new_record: function () {
            // TODO: handle multiple children
            this.children[null].new_record();
        }
    });

    openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{
        row_clicked: function (event) {
            if (!this.options.editable) {
                return this._super(event);
            }
            this.edit_record($(event.currentTarget).data('id'));
        },
        /**
         * Checks if a record is being edited, and if so cancels it
         */
        cancel_pending_edition: function () {
            var self = this, cancelled = $.Deferred();
            if (!this.edition) {
                cancelled.resolve();
                return cancelled.promise();
            }

            if (this.edition_id != null) {
                this.reload_record(self.records.get(this.edition_id)).then(function () {
                    cancelled.resolve();
                });
            } else {
                cancelled.resolve();
            }
            cancelled.then(function () {
                self.view.unpad_columns();
                self.edition_form.stop();
                self.edition_form.$element.remove();
                delete self.edition_form;
                delete self.edition_id;
                delete self.edition;
            });
            return cancelled.promise();
        },
        /**
         * Adapts this list's view description to be suitable to the inner form
         * view of a row being edited.
         *
         * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
         */
        get_form_fields_view: function () {
            // deep copy of view
            var view = $.extend(true, {}, this.group.view.fields_view);
            _(view.arch.children).each(function (widget) {
                widget.attrs.nolabel = true;
                if (widget.tag === 'button') {
                    delete widget.attrs.string;
                }
            });
            view.arch.attrs.col = 2 * view.arch.children.length;
            return view;
        },
        render_row_as_form: function (row) {
            var self = this;
            this.cancel_pending_edition().then(function () {
                var record_id = $(row).data('id');
                var $new_row = $('<tr>', {
                        id: _.uniqueId('oe-editable-row-'),
                        'data-id': record_id,
                        'class': $(row).attr('class') + ' oe_forms',
                        click: function (e) {e.stopPropagation();}
                    })
                    .delegate('button.oe-edit-row-save', 'click', function () {
                        self.save_row();
                    })
                    .delegate('button.oe-edit-row-cancel', 'click', function () {
                        self.cancel_edition();
                    })
                    .delegate('button', 'keyup', function (e) {
                        e.stopImmediatePropagation();
                    })
                    .keyup(function (e) {
                        switch (e.which) {
                            case KEY_RETURN:
                                self.save_row(true);
                                break;
                            case KEY_ESCAPE:
                                self.cancel_edition();
                                break;
                            default:
                                return;
                        }
                    });
                if (row) {
                    $new_row.replaceAll(row);
                } else if (self.options.editable === 'top') {
                    self.$current.prepend($new_row);
                } else if (self.options.editable) {
                    self.$current.append($new_row);
                }
                self.edition = true;
                self.edition_id = record_id;
                self.edition_form = _.extend(new openerp.web.FormView(
                        self, $new_row.attr('id'), self.dataset, false), {
                    template: 'ListView.row.form',
                    registry: openerp.web.list.form.widgets
                });
                $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
                    // put in $.when just in case  FormView.on_loaded becomes asynchronous
                    $new_row.find('td')
                          .addClass('oe-field-cell')
                          .removeAttr('width')
                      .end()
                      .find('td:first').removeClass('oe-field-cell').end()
                      .find('td:last').removeClass('oe-field-cell').end();
                    // pad in case of groupby
                    _(self.columns).each(function (column) {
                        if (column.meta) {
                            $new_row.prepend('<td>');
                        }
                    });
                    // Add columns for the cancel and save buttons, if
                    // there are none in the list
                    if (!self.options.selectable) {
                        self.view.pad_columns(
                            1, {except: $new_row, position: 'before'});
                    }
                    if (!self.options.deletable) {
                        self.view.pad_columns(
                            1, {except: $new_row});
                    }

                    self.edition_form.do_show();
                });
            });
        },
        handle_onwrite: function (source_record_id) {
            var self = this;
            var on_write_callback = self.view.fields_view.arch.attrs.on_write;
            if (!on_write_callback) { return; }
            this.dataset.call(on_write_callback, [source_record_id], function (ids) {
                _(ids).each(function (id) {
                    var record = self.records.get(id);
                    if (!record) {
                        // insert after the source record
                        var index = self.records.indexOf(
                            self.records.get(source_record_id)) + 1;
                        record = new openerp.web.list.Record({id: id});
                        self.records.add(record, {at: index});
                        self.dataset.ids.splice(index, 0, id);
                    }
                    self.reload_record(record);
                });
            });
        },
        /**
         * Saves the current row, and triggers the edition of its following
         * sibling if asked.
         *
         * @param {Boolean} [edit_next=false] should the next row become editable
         */
        save_row: function (edit_next) {
            var self = this;
            this.edition_form.do_save(function (result) {
                if (result.created && !self.edition_id) {
                    self.records.add({id: result.result},
                        {at: self.options.editable === 'top' ? 0 : null});
                    self.edition_id = result.result;
                }
                var edited_record = self.records.get(self.edition_id),
                    next_record = self.records.at(
                            self.records.indexOf(edited_record) + 1);

                self.handle_onwrite(self.edition_id);
                self.cancel_pending_edition().then(function () {
                    $(self).trigger('saved', [self.dataset]);
                    if (!edit_next) {
                        return;
                    }
                    if (result.created) {
                        self.new_record();
                        return;
                    }
                    var next_record_id;
                    if (next_record) {
                        next_record_id = next_record.get('id');
                        self.dataset.index = _(self.dataset.ids)
                                .indexOf(next_record_id);
                    } else {
                        self.dataset.index = 0;
                        next_record_id = self.records.at(0).get('id');
                    }
                    self.edit_record(next_record_id);
                });
            }, this.options.editable === 'top');
        },
        /**
         * Cancels the edition of the row for the current dataset index
         */
        cancel_edition: function () {
            this.cancel_pending_edition();
        },
        /**
         * Edits record currently selected via dataset
         */
        edit_record: function (record_id) {
            this.render_row_as_form(
                this.$current.find('[data-id=' + record_id + ']'));
            $(this).trigger(
                'edit',
                [record_id, this.dataset]);
        },
        new_record: function () {
            this.dataset.index = null;
            this.render_row_as_form();
        }
    });
    if (!openerp.web.list) {
        openerp.web.list = {};
    }
    if (!openerp.web.list.form) {
        openerp.web.list.form = {};
    }
    openerp.web.list.form.WidgetFrame = openerp.web.form.WidgetFrame.extend({
        template: 'ListView.row.frame'
    });
    var form_widgets = openerp.web.form.widgets;
    openerp.web.list.form.widgets = form_widgets.clone({
        'frame': 'openerp.web.list.form.WidgetFrame'
    });
    // All form widgets inherit a problematic behavior from
    // openerp.web.form.WidgetFrame: the cell itself is removed when invisible
    // whether it's @invisible or @attrs[invisible]. In list view, only the
    // former should completely remove the cell. We need to override update_dom
    // on all widgets since we can't just hit on widget itself (I think)
    var list_form_widgets = openerp.web.list.form.widgets;
    _(list_form_widgets.map).each(function (widget_path, key) {
        if (key === 'frame') { return; }
        var new_path = 'openerp.web.list.form.' + key;

        openerp.web.list.form[key] = (form_widgets.get_object(key)).extend({
            update_dom: function () {
                this.$element.children().css('visibility', '');
                if (this.modifiers.tree_invisible) {
                    var old_invisible = this.invisible;
                    this.invisible = !!this.modifiers.tree_invisible;
                    this._super();
                    this.invisible = old_invisible;
                } else if (this.invisible) {
                    this.$element.children().css('visibility', 'hidden');
                }
            }
        });
        list_form_widgets.add(key, new_path);
    });
};
<|endoftext|>


<commit_before>const gulp = require('gulp');
const sass = require('gulp-sass');
function exec(what, cb) {
    require('child_process').exec(what, function(err, stdout, stderr) {
        if (stdout) console.log(stdout);
        //if (stderr) console.log(stderr);
        cb(err);
    });
}
gulp.task('buildsystem:tsc', function(cb) {
    exec('./node_modules/.bin/tsc -p ../buildsystem/tsconfig.json', cb);
});
gulp.task('buildsystem:copy-test-data', function() {
    gulp.src('../buildsystem/*.tests/data/**')
        .pipe(gulp.dest('out/buildsystem'));
});
gulp.task('buildsystem:tests', ['buildsystem:tsc', 'buildsystem:copy-test-data'], function(cb) {
    exec('./node_modules/.bin/mocha  --inline-diffs  --colors out/buildsystem/tests.js', cb);
});
gulp.task('buildsystem:debug', ['buildsystem:tsc', 'buildsystem:copy-test-data'], function(cb) {
    exec('./node_modules/.bin/mocha  --inline-diffs --nolazy --debug-brk --colors out/buildsystem/tests.js', cb);
});
gulp.task('buildsystem', ['buildsystem:tests']);
<commit_msg>add perf profile build of the buildsystem<commit_after>const gulp = require('gulp');
const sass = require('gulp-sass');
function exec(what, cb) {
    require('child_process').exec(what, function(err, stdout, stderr) {
        if (stdout) console.log(stdout);
        //if (stderr) console.log(stderr);
        cb(err);
    });
}
gulp.task('buildsystem:tsc', function(cb) {
    exec('./node_modules/.bin/tsc -p ../buildsystem/tsconfig.json', cb);
});
gulp.task('buildsystem:copy-test-data', function() {
    gulp.src('../buildsystem/*.tests/data/**')
        .pipe(gulp.dest('out/buildsystem'));
});
gulp.task('buildsystem:tests', ['buildsystem:tsc', 'buildsystem:copy-test-data'], function(cb) {
    exec('./node_modules/.bin/mocha  --inline-diffs  --colors out/buildsystem/tests.js', cb);
});
gulp.task('buildsystem:debug', ['buildsystem:tsc', 'buildsystem:copy-test-data'], function(cb) {
    exec('./node_modules/.bin/mocha  --inline-diffs --nolazy --debug-brk --colors out/buildsystem/tests.js', cb);
});
gulp.task('buildsystem:perf', ['buildsystem:tsc', 'buildsystem:copy-test-data'], function(cb) {
    exec('./node_modules/.bin/mocha --slow 10 --prof --inline-diffs --colors out/buildsystem/tests.js', cb);
});
gulp.task('buildsystem', ['buildsystem:tests']);
<|endoftext|>