<commit_before>  // This is the base Model class in KnockoutApp
  var Model = KnockoutApp.Model = function(attributes, options){

    // 'id' is an observable property, initially set to 'false'
    this.id = ko.observable(false);

    // The model stores its data inside this object
    this.attributes = {};

    // If an 'attributes' object is passed as a parameter:
    if(attributes){

      // In case it contains an 'id' (or the idAttribute property) property set **this.id()** value to it and delete it from the attributes object
      if(attributes[this.idAttribute]){
        this.id(attributes[this.idAttribute]);
        delete attributes[this.idAttribute];
      }

      // Merge the attributes passed as parameters with the default attributes stored inside **this.defaultAttributes()**
      //
      // Using **Utils.extendObjKnockout** ensured that observable properties inside *this.defaultAttributes()*
      // are correctly set into *this.attributes*
      var defaults = Utils.cloneObjKnockout(this.defaultAttributes);
      this.attributes = Utils.extendObjKnockout(defaults, attributes);
    }

    // This function allows to be passed, as the second parameter, a reference to a **Collection**.
    // In that case a reference to the collection is set as *this.collection*
    if(options && options.collection) this.collection = options.collection;

    // Detect if the model has been created on the server by checking if the 'id' property is defined
    this.isNew = ko.computed(function(){
      return (this.id() === false) ? true : false;
    }, this);

    // Instead of overriding the function constructor use the initialize function to execute custom code on model creation
    // Knockout's observable properties can't be defined in the class prototype
    // so this is the perfect place to use them.
    if(this.initialize) this.initialize.apply(this, arguments);

  };

  // Extend Model's prototype
  ko.utils.extend(Model.prototype, {

    // An object with the default attributes for the model
    // It must be a function because no 'clone' method has been implemented so far, will be fixed in future versions
    // Here you can also use observable properties.
    defaultAttributes: {},

    idAttribute: 'id',

    // Uses this.collection.sync || KnockoutApp.Sync can be overriden
    sync: function(){
      return ((this.collection && this.collection.sync) || KnockoutApp.Sync).apply(this, arguments);
    },

    // Returns the model url on the server using the model's baseUrl or collection's url properties
    url: function(){
      var base = Utils.unwrapValue(this, 'baseUrl') || (this.collection && Utils.unwrapValue(this.collection, 'url'));
      if(this.isNew()) return base;
      return base + (base[base.length-1] === '/' ? '' : '/') + this.id();
    },

    // Validation method, should return TRUE if the model is valid
    validate: function(){
      return true;
    },

    // Fetch the model on the server and replace its attributes with the one fetched
    // Options for the Ajax call can be passed as a parameter
    fetch: function(_options){
      var self = this, //model
          options = _options || {},
          success = options.success; //custom success passed in _options

      options.success = function(data){
        delete data[self.idAttribute];
        var defaults = Utils.cloneObjKnockout(self.defaultAttributes);
        self.attributes = Utils.extendObjKnockout(defaults, data);
        if(success) success(self, data);
      };

      return this.sync.call(this, 'fetch', this, options);
    },

    // Save the model on the server.
    // If this.isNew() returns true it will be created, otherwise it will be updated
    // Options for the Ajax call can be passed as a parameter
    save: function(_options){
      if(this.validate() !== true) return false;

      var self = this, //model
          options = _options || {},
          success = options.success, //custome success passed in _options
          method = this.isNew() ? 'create' : 'update';

      options.success = function(data){
        if(method === 'create') self.id(data[self.idAttribute]);
        if(success) success(self, data);
      };

      return this.sync.call(this, method, this, options);
    },

    // Destroy the model on the server and remove it from its collection (if exists)
    // Options for the Ajax call can be passed as a parameter
    destroy: function(_options){
      var self = this, //model
          options = _options || {},
          success = options.success; //custome success passed in _options

      options.success = function(){
        if(self.collection){
          self.collection.models.remove(self);
          delete self.collection;
        }
        if(success) success(self, data);
      };

      if(this.isNew()){
        options.success();
        return false;
      }

      var xhr = this.sync.call(this, 'destroy', this, options);

      if(!options.wait){
        options.success();
        delete options.success;
      }

      return xhr;
    },

    // Used for serialization, returns an object that contains model's attributes and its id
    toJSON: function(){
      var obj = ko.toJS(this.attributes);
      if(this.id()) obj[this.idAttribute] = this.id();
      return obj;
    }
  });
<commit_msg>defaultAttributes to defaults, comments rewritten<commit_after>  // Define the Model class
  var Model = KnockoutApp.Model = function(attributes, options){

    // *id* is an observable property, initially set to *false*
    this.id = ko.observable(false);

    // An object for storing model's attributes
    this.attributes = {};

    // If an *attributes* object is passed as a parameter:
    if(attributes){

      // In case it contains the *idAttribute* property set *this.id()* value to it and delete it from the attributes object
      if(attributes[this.idAttribute]){
        this.id(attributes[this.idAttribute]);
        delete attributes[this.idAttribute];
      }

      // Merge the attributes passed as parameters with the default attributes set in *this.defaults*
      //
      // *Utils.cloneObjKnockout* is used to clone an object containing observable properties
      //
      // *Utils.extendObjKnockout* ensures that observable properties inside *this.defaults*
      // are correctly set into *this.attributes*
      var defaults = Utils.cloneObjKnockout(this.defaults);
      this.attributes = Utils.extendObjKnockout(defaults, attributes);
    }

    // If *options.collection* has been passed as a prameter set *this.collection* to *options.collection*
    if(options && options.collection) this.collection = options.collection;

    // Detect if the model has been created on the server by checking if the 'id' property is set
    this.isNew = ko.computed(function(){
      return (this.id() === false) ? true : false;
    }, this);

    // Instead of overriding the function constructor use the initialize function to execute custom code on model creation
    // Knockout's observable properties can't be defined as prototype properties
    // so this is the perfect place to define them
    if(this.initialize) this.initialize.apply(this, arguments);

  };

  // Extend Model's prototype
  ko.utils.extend(Model.prototype, {

    // An object with the default attributes for the model
    // Model's observable attributes  must be defined here
    defaults: {},

    // The property used as model's id, by default *id*
    // It can be set to something different if the server use a different value for the id (for example when using MongoDB you may set it to *_id*)
    idAttribute: 'id',

    // Model's sync method: uses Collection's sync method if the model is stored in a collection, if it isn't use KnockoutApp.Sync
    // Can be overriden with a custom method
    sync: function(){
      return ((this.collection && this.collection.sync) || KnockoutApp.Sync).apply(this, arguments);
    },

    // Returns the model url using model's baseUrl or collection's url properties. If they're both undefined, throw an error
    // If model.id() is set add model.id() to the url
    url: function(){
      var base = Utils.unwrapValue(this, 'baseUrl') || (this.collection && Utils.unwrapValue(this.collection, 'url'));
      if(typeof base === undefined) throw "Missing baseUrl or collection.url properties";
      if(this.isNew()) return base;
      return base + (base[base.length-1] === '/' ? '' : '/') + this.id();
    },

    // Validation method, should return TRUE if the model is valid, something else like an error message if it isn't
    validate: function(){
      return true;
    },

    // Fetch the model on the server and replace its attributes with the one fetched
    // Options for the sync method can be passed as an object
    fetch: function(_options){
      var self = this, //model
          options = _options || {},
          success = options.success; //custom success function passed in _options

      options.success = function(data){
        delete data[self.idAttribute];
        var defaults = Utils.cloneObjKnockout(self.defaults);
        self.attributes = Utils.extendObjKnockout(defaults, data);
        if(success) success(self, data);
      };

      return this.sync.call(this, 'fetch', this, options);
    },

    // Save the model on the server
    // If model.isNew() returns true the model will be created, otherwise it will be updated
    // Options for the sync method can be passed as an object
    save: function(_options){
      if(this.validate() !== true) return false;

      var self = this, //model
          options = _options || {},
          success = options.success, //custom success function passed in _options
          method = this.isNew() ? 'create' : 'update';

      options.success = function(data){
        if(method === 'create') self.id(data[self.idAttribute]);
        if(success) success(self, data);
      };

      return this.sync.call(this, method, this, options);
    },

    // Destroy the model on the server and remove it from its collection (if exists)
    // Options for the sync method can be passed as an object
    // If you set *wait: true* in the options' object it will remove the model from the collection without waiting for the server response
    destroy: function(_options){
      var self = this, //model
          options = _options || {},
          success = options.success; //custom success function passed in _options

      options.success = function(){
        if(self.collection){
          self.collection.models.remove(self);
          delete self.collection;
        }
        if(success) success(self, data);
      };

      if(this.isNew()){
        options.success();
        return false;
      }

      var xhr = this.sync.call(this, 'destroy', this, options);

      if(!options.wait){
        options.success();
        delete options.success;
      }

      return xhr;
    },

    // Used for serialization, returns an object that contains model's attributes and its idAttribute if set
    toJSON: function(){
      var obj = ko.toJS(this.attributes);
      if(this.id()) obj[this.idAttribute] = this.id();
      return obj;
    }
  });
<|endoftext|>


<commit_before>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint
} from '../modes';

import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg/index';
import { tooltip } from '../util/tooltip';
import { uiPresetFavorite } from './preset_favorite';
import { uiPresetIcon } from './preset_icon';
import { utilKeybinding, utilNoAuto, utilRebind } from '../util';

export function uiSearchAdd(context) {
    var dispatch = d3_dispatch('choose');
    var presets;
    var search = d3_select(null), popover = d3_select(null), list = d3_select(null);

    var shownGeometry = ['area', 'line', 'point', 'vertex'];

    function searchAdd(selection) {

        presets = context.presets().matchAnyGeometry(shownGeometry);

        var searchWrap = selection
            .append('div')
            .attr('class', 'search-wrap');

        search = searchWrap
            .append('input')
            .attr('class', 'search-input')
            .attr('placeholder', t('modes.add_feature.title'))
            .attr('type', 'search')
            .call(utilNoAuto)
            .on('keypress', function() {
                // enter/return
                if (d3_event.keyCode === 13) {
                    popover.selectAll('.list .list-item.focused button.choose')
                        .each(function(d) { d.choose.call(this); });
                    d3_event.preventDefault();
                    d3_event.stopPropagation();
                }
            })
            .on('keydown', function(){
                // up/down arrow key navigation

                var nextFocus,
                    priorFocus,
                    parentSubsection;
                if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] ||
                    d3_event.keyCode === utilKeybinding.keyCodes.tab && !d3_event.shiftKey) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    priorFocus = popover.selectAll('.list .list-item.focused');
                    if (priorFocus.empty()) {
                        nextFocus = popover.selectAll('.list > .list-item:first-child');
                    } else {
                        nextFocus = popover.selectAll('.list .list-item.focused + .list-item');
                        if (nextFocus.empty()) {
                            nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling)
                                .selectAll('.list-item:first-child');
                        }
                        if (nextFocus.empty()) {
                            parentSubsection = priorFocus.nodes()[0].closest('.list .subsection');
                            if (parentSubsection && parentSubsection.nextElementSibling) {
                                nextFocus = d3_select(parentSubsection.nextElementSibling);
                            }
                        }
                    }
                    if (!nextFocus.empty()) {
                        focusListItem(nextFocus);
                        priorFocus.classed('focused', false);
                    }

                } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] ||
                    d3_event.keyCode === utilKeybinding.keyCodes.tab && d3_event.shiftKey) {
                    d3_event.preventDefault();
                    d3_event.stopPropagation();

                    priorFocus = popover.selectAll('.list .list-item.focused');
                    if (!priorFocus.empty()) {

                        nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling);
                        if (!nextFocus.empty() && !nextFocus.classed('list-item')) {
                            nextFocus = nextFocus.selectAll('.list-item:last-child');
                        }
                        if (nextFocus.empty()) {
                            parentSubsection = priorFocus.nodes()[0].closest('.list .subsection');
                            if (parentSubsection && parentSubsection.previousElementSibling) {
                                nextFocus = d3_select(parentSubsection.previousElementSibling);
                            }
                        }
                        if (!nextFocus.empty()) {
                            focusListItem(nextFocus);
                            priorFocus.classed('focused', false);
                        }
                    }
                }
            })
            .on('mousedown', function() {
                search.attr('clicking', true);
            })
            .on('mouseup', function() {
                search.attr('clicking', null);
            })
            .on('focus', function() {
                if (search.attr('clicking')) {
                    search.attr('focusing', true);
                    search.attr('clicking', null);
                } else {
                    search.node().setSelectionRange(0, search.property('value').length);
                }
                popover.classed('hide', false);
            })
            .on('blur', function() {
                popover.classed('hide', true);
            })
            .on('click', function() {
                if (search.attr('focusing')) {
                    search.node().setSelectionRange(0, search.property('value').length);
                    search.attr('focusing', null);
                }
            })
            .on('input', function () {

                popover.selectAll('.subsection').remove();

                var value = search.property('value');
                var results;
                if (value.length) {
                    results = presets.search(value, shownGeometry);
                } else {
                    results = context.presets().recent(shownGeometry, 36);
                }

                list.call(drawList, results);

                popover.selectAll('.list .list-item.focused')
                    .classed('focused', false);
                focusListItem(popover.selectAll('.list > .list-item:first-child'));
            });

        searchWrap
            .call(svgIcon('#iD-icon-search', 'search-icon pre-text'));

        popover = selection
            .append('div')
            .attr('class', 'popover fillL hide')
            .on('mousedown', function() {
                // don't blur the search input (and thus close results)
                d3_event.preventDefault();
                d3_event.stopPropagation();
            });

        list = popover
            .append('div')
            .attr('class', 'list');//
            //.call(drawList, context.presets().defaults(geometry, 36));

        context.features().on('change.search-add', updateForFeatureHiddenState);

        context.keybinding().on('tab', function() {
            search.node().focus();
            d3_event.preventDefault();
            d3_event.stopPropagation();
        });
    }

    function focusListItem(selection) {
        if (!selection.empty()) {
            selection.classed('focused', true);
            // scroll to keep the focused item visible
            scrollPopoverToShow(selection);
        }
    }

    function scrollPopoverToShow(selection) {
        if (selection.empty()) return;

        var node = selection.nodes()[0];
        var popoverNode = popover.node();

        if (node.offsetTop < popoverNode.scrollTop) {
            popoverNode.scrollTop = node.offsetTop;

        } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight &&
            node.offsetHeight < popoverNode.offsetHeight) {
            popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight;
        }
    }

    function itemForPreset(preset) {
        if (preset.members) {
            return CategoryItem(preset);
        }
        var supportedGeometry = preset.geometry.filter(function(geometry) {
            return shownGeometry.indexOf(geometry) !== -1;
        }).sort();
        var vertexIndex = supportedGeometry.indexOf('vertex');
        if (vertexIndex !== -1 && supportedGeometry.indexOf('point') !== -1) {
            // both point and vertex allowed, just show point
            supportedGeometry.splice(vertexIndex, 1);
        }
        if (supportedGeometry.length === 1) {
            return AddablePresetItem(preset, supportedGeometry[0]);
        }
        return MultiGeometryPresetItem(preset, supportedGeometry);
    }

    function drawList(list, presets) {

        var collection = presets.collection.map(function(preset) {
            return itemForPreset(preset);
        });

        var items = list.selectAll('.list-item')
            .data(collection, function(d) { return d.preset.id; });

        items.order();

        items.exit()
            .remove();

        items.enter();
        drawItems(items.enter());

        updateForFeatureHiddenState();
    }

    function drawItems(selection) {

        var row = selection
            .append('div')
            .attr('class', 'list-item')
            .attr('id', function(d) {
                var id = 'search-add-list-item-preset-' + d.preset.id.replace(/[^a-zA-Z\d:]/g, '-');
                if (d.geometry) {
                    id += '-' + d.geometry;
                }
                return id;
            })
            .on('mouseover', function() {
                list.selectAll('.list-item.focused')
                    .classed('focused', false);
                d3_select(this)
                    .classed('focused', true);
            })
            .on('mouseout', function() {
                d3_select(this)
                    .classed('focused', false);
            });

        row.append('button')
            .attr('class', 'choose')
            .on('click', function(d) {
                d.choose.call(this);
            });

        row.each(function(d) {
            d3_select(this).call(
                uiPresetIcon()
                    .geometry(d.geometry)
                    .preset(d.preset)
                    .sizeClass('small')
            );
        });
        var label = row.append('div')
            .attr('class', 'label');

        label.each(function(d) {
            if (!d.geometry) {
                d3_select(this)
                    .call(svgIcon((textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
            }
        });

        label.append('span')
            .text(function(d) {
                if (d.isSubitem) {
                    if (d.preset.setTags({}, d.geometry).building) {
                        return t('presets.presets.building.name');
                    }
                    return t('modes.add_' + d.geometry + '.title');
                }
                return d.preset.name();
            });

        row.each(function(d) {
            if (d.geometry) {
                var presetFavorite = uiPresetFavorite(d.preset, d.geometry, context, 'accessory');
                d3_select(this).call(presetFavorite.button);
            }
        });
    }

    function updateForFeatureHiddenState() {

        var listItem = d3_selectAll('.search-add .popover .list-item');

        // remove existing tooltips
        listItem.selectAll('button.choose').call(tooltip().destroyAny);

        listItem.each(function(item, index) {
            if (!item.geometry) return;

            var hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, item.geometry);
            var isHiddenPreset = !!hiddenPresetFeaturesId;

            var button = d3_select(this).selectAll('button.choose');

            d3_select(this).classed('disabled', isHiddenPreset);
            button.classed('disabled', isHiddenPreset);

            if (isHiddenPreset) {
                var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
                var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
                var tooltipObj = { features: t('feature.' + hiddenPresetFeaturesId + '.description') };
                button.call(tooltip('dark')
                    .html(true)
                    .title(t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
                    .placement(index < 2 ? 'bottom' : 'top')
                );
            }
        });
    }

    function chooseExpandable(item, itemSelection) {

        var shouldExpand = !itemSelection.classed('expanded');

        itemSelection.classed('expanded', shouldExpand);

        var iconName = shouldExpand ?
            '#iD-icon-down' : (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
        itemSelection.selectAll('.label svg.icon use')
            .attr('href', iconName);

        if (shouldExpand) {
            var subitems = item.subitems();
            var selector = '#' + itemSelection.node().id + ' + *';
            item.subsection = d3_select(itemSelection.node().parentElement).insert('div', selector)
                .attr('class', 'subsection');
            var subitemsEnter = item.subsection.selectAll('.list-item')
                .data(subitems)
                .enter();
            drawItems(subitemsEnter);
            updateForFeatureHiddenState();
            scrollPopoverToShow(item.subsection);
        } else {
            item.subsection.remove();
        }
    }

    function CategoryItem(preset) {
        var item = {};
        item.subsection = d3_select(null);
        item.preset = preset;
        item.choose = function() {
            var selection = d3_select(this);
            if (selection.classed('disabled')) return;
            chooseExpandable(item, d3_select(selection.node().closest('.list-item')));
        };
        item.subitems = function() {
            return preset.members.matchAnyGeometry(shownGeometry).collection.map(function(preset) {
                return itemForPreset(preset);
            });
        };
        return item;
    }

    function MultiGeometryPresetItem(preset, geometries) {

        var item = {};
        item.subsection = d3_select(null);
        item.preset = preset;
        item.geometries = geometries;
        item.choose = function() {
            var selection = d3_select(this);
            if (selection.classed('disabled')) return;
            chooseExpandable(item, d3_select(selection.node().closest('.list-item')));
        };
        item.subitems = function() {
            return geometries.map(function(geometry) {
                return AddablePresetItem(preset, geometry, true);
            });
        };
        return item;
    }

    function AddablePresetItem(preset, geometry, isSubitem) {
        var item = {};
        item.isSubitem = isSubitem;
        item.preset = preset;
        item.geometry = geometry;
        item.choose = function() {
            if (d3_select(this).classed('disabled')) return;

            var markerClass = 'add-preset add-' + geometry +
                ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry;
            var modeInfo = {
                button: markerClass,
                preset: preset,
                geometry: geometry
            };
            var mode;
            switch (geometry) {
                case 'point':
                case 'vertex':
                    mode = modeAddPoint(context, modeInfo);
                    break;
                case 'line':
                    mode = modeAddLine(context, modeInfo);
                    break;
                case 'area':
                    mode = modeAddArea(context, modeInfo);
            }
            search.node().blur();
            context.presets().choose(preset);
            context.enter(mode);
        };
        return item;
    }

    return utilRebind(searchAdd, dispatch, 'on');
}
<commit_msg>Escape key to blur search field<commit_after>import { dispatch as d3_dispatch } from 'd3-dispatch';

import {
    event as d3_event,
    select as d3_select,
    selectAll as d3_selectAll
} from 'd3-selection';

import {
    modeAddArea,
    modeAddLine,
    modeAddPoint
} from '../modes';

import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg/index';
import { tooltip } from '../util/tooltip';
import { uiPresetFavorite } from './preset_favorite';
import { uiPresetIcon } from './preset_icon';
import { utilKeybinding, utilNoAuto, utilRebind } from '../util';

export function uiSearchAdd(context) {
    var dispatch = d3_dispatch('choose');
    var presets;
    var search = d3_select(null), popover = d3_select(null), list = d3_select(null);

    var shownGeometry = ['area', 'line', 'point', 'vertex'];

    function searchAdd(selection) {

        presets = context.presets().matchAnyGeometry(shownGeometry);

        var searchWrap = selection
            .append('div')
            .attr('class', 'search-wrap');

        search = searchWrap
            .append('input')
            .attr('class', 'search-input')
            .attr('placeholder', t('modes.add_feature.title'))
            .attr('type', 'search')
            .call(utilNoAuto)
            .on('mousedown', function() {
                search.attr('clicking', true);
            })
            .on('mouseup', function() {
                search.attr('clicking', null);
            })
            .on('focus', function() {
                if (search.attr('clicking')) {
                    search.attr('focusing', true);
                    search.attr('clicking', null);
                } else {
                    search.node().setSelectionRange(0, search.property('value').length);
                }
                popover.classed('hide', false);
            })
            .on('blur', function() {
                popover.classed('hide', true);
            })
            .on('click', function() {
                if (search.attr('focusing')) {
                    search.node().setSelectionRange(0, search.property('value').length);
                    search.attr('focusing', null);
                }
            })
            .on('keypress', keypress)
            .on('keydown', keydown)
            .on('input', searchInput);

        searchWrap
            .call(svgIcon('#iD-icon-search', 'search-icon pre-text'));

        popover = selection
            .append('div')
            .attr('class', 'popover fillL hide')
            .on('mousedown', function() {
                // don't blur the search input (and thus close results)
                d3_event.preventDefault();
                d3_event.stopPropagation();
            });

        list = popover
            .append('div')
            .attr('class', 'list');//
            //.call(drawList, context.presets().defaults(geometry, 36));

        context.features().on('change.search-add', updateForFeatureHiddenState);

        context.keybinding().on('tab', function() {
            search.node().focus();
            d3_event.preventDefault();
            d3_event.stopPropagation();
        });
    }

    function keypress() {
        if (d3_event.keyCode === utilKeybinding.keyCodes.enter) {
            popover.selectAll('.list .list-item.focused button.choose')
                .each(function(d) { d.choose.call(this); });
            d3_event.preventDefault();
            d3_event.stopPropagation();
        }
    }

    function keydown() {

        var nextFocus,
            priorFocus,
            parentSubsection;
        if (d3_event.keyCode === utilKeybinding.keyCodes['↓'] ||
            d3_event.keyCode === utilKeybinding.keyCodes.tab && !d3_event.shiftKey) {
            d3_event.preventDefault();
            d3_event.stopPropagation();

            priorFocus = popover.selectAll('.list .list-item.focused');
            if (priorFocus.empty()) {
                nextFocus = popover.selectAll('.list > .list-item:first-child');
            } else {
                nextFocus = popover.selectAll('.list .list-item.focused + .list-item');
                if (nextFocus.empty()) {
                    nextFocus = d3_select(priorFocus.nodes()[0].nextElementSibling)
                        .selectAll('.list-item:first-child');
                }
                if (nextFocus.empty()) {
                    parentSubsection = priorFocus.nodes()[0].closest('.list .subsection');
                    if (parentSubsection && parentSubsection.nextElementSibling) {
                        nextFocus = d3_select(parentSubsection.nextElementSibling);
                    }
                }
            }
            if (!nextFocus.empty()) {
                focusListItem(nextFocus);
                priorFocus.classed('focused', false);
            }

        } else if (d3_event.keyCode === utilKeybinding.keyCodes['↑'] ||
            d3_event.keyCode === utilKeybinding.keyCodes.tab && d3_event.shiftKey) {
            d3_event.preventDefault();
            d3_event.stopPropagation();

            priorFocus = popover.selectAll('.list .list-item.focused');
            if (!priorFocus.empty()) {

                nextFocus = d3_select(priorFocus.nodes()[0].previousElementSibling);
                if (!nextFocus.empty() && !nextFocus.classed('list-item')) {
                    nextFocus = nextFocus.selectAll('.list-item:last-child');
                }
                if (nextFocus.empty()) {
                    parentSubsection = priorFocus.nodes()[0].closest('.list .subsection');
                    if (parentSubsection && parentSubsection.previousElementSibling) {
                        nextFocus = d3_select(parentSubsection.previousElementSibling);
                    }
                }
                if (!nextFocus.empty()) {
                    focusListItem(nextFocus);
                    priorFocus.classed('focused', false);
                }
            }
        } else if (d3_event.keyCode === utilKeybinding.keyCodes.esc) {
            search.node().blur();
            d3_event.preventDefault();
            d3_event.stopPropagation();
        }
    }

    function searchInput() {

        popover.selectAll('.subsection').remove();

        var value = search.property('value');
        var results;
        if (value.length) {
            results = presets.search(value, shownGeometry);
        } else {
            results = context.presets().recent(shownGeometry, 36);
        }

        list.call(drawList, results);

        popover.selectAll('.list .list-item.focused')
            .classed('focused', false);
        focusListItem(popover.selectAll('.list > .list-item:first-child'));
    }

    function focusListItem(selection) {
        if (!selection.empty()) {
            selection.classed('focused', true);
            // scroll to keep the focused item visible
            scrollPopoverToShow(selection);
        }
    }

    function scrollPopoverToShow(selection) {
        if (selection.empty()) return;

        var node = selection.nodes()[0];
        var popoverNode = popover.node();

        if (node.offsetTop < popoverNode.scrollTop) {
            popoverNode.scrollTop = node.offsetTop;

        } else if (node.offsetTop + node.offsetHeight > popoverNode.scrollTop + popoverNode.offsetHeight &&
            node.offsetHeight < popoverNode.offsetHeight) {
            popoverNode.scrollTop = node.offsetTop + node.offsetHeight - popoverNode.offsetHeight;
        }
    }

    function itemForPreset(preset) {
        if (preset.members) {
            return CategoryItem(preset);
        }
        var supportedGeometry = preset.geometry.filter(function(geometry) {
            return shownGeometry.indexOf(geometry) !== -1;
        }).sort();
        var vertexIndex = supportedGeometry.indexOf('vertex');
        if (vertexIndex !== -1 && supportedGeometry.indexOf('point') !== -1) {
            // both point and vertex allowed, just show point
            supportedGeometry.splice(vertexIndex, 1);
        }
        if (supportedGeometry.length === 1) {
            return AddablePresetItem(preset, supportedGeometry[0]);
        }
        return MultiGeometryPresetItem(preset, supportedGeometry);
    }

    function drawList(list, presets) {

        var collection = presets.collection.map(function(preset) {
            return itemForPreset(preset);
        });

        var items = list.selectAll('.list-item')
            .data(collection, function(d) { return d.preset.id; });

        items.order();

        items.exit()
            .remove();

        items.enter();
        drawItems(items.enter());

        updateForFeatureHiddenState();
    }

    function drawItems(selection) {

        var row = selection
            .append('div')
            .attr('class', 'list-item')
            .attr('id', function(d) {
                var id = 'search-add-list-item-preset-' + d.preset.id.replace(/[^a-zA-Z\d:]/g, '-');
                if (d.geometry) {
                    id += '-' + d.geometry;
                }
                return id;
            })
            .on('mouseover', function() {
                list.selectAll('.list-item.focused')
                    .classed('focused', false);
                d3_select(this)
                    .classed('focused', true);
            })
            .on('mouseout', function() {
                d3_select(this)
                    .classed('focused', false);
            });

        row.append('button')
            .attr('class', 'choose')
            .on('click', function(d) {
                d.choose.call(this);
            });

        row.each(function(d) {
            d3_select(this).call(
                uiPresetIcon()
                    .geometry(d.geometry)
                    .preset(d.preset)
                    .sizeClass('small')
            );
        });
        var label = row.append('div')
            .attr('class', 'label');

        label.each(function(d) {
            if (!d.geometry) {
                d3_select(this)
                    .call(svgIcon((textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
            }
        });

        label.append('span')
            .text(function(d) {
                if (d.isSubitem) {
                    if (d.preset.setTags({}, d.geometry).building) {
                        return t('presets.presets.building.name');
                    }
                    return t('modes.add_' + d.geometry + '.title');
                }
                return d.preset.name();
            });

        row.each(function(d) {
            if (d.geometry) {
                var presetFavorite = uiPresetFavorite(d.preset, d.geometry, context, 'accessory');
                d3_select(this).call(presetFavorite.button);
            }
        });
    }

    function updateForFeatureHiddenState() {

        var listItem = d3_selectAll('.search-add .popover .list-item');

        // remove existing tooltips
        listItem.selectAll('button.choose').call(tooltip().destroyAny);

        listItem.each(function(item, index) {
            if (!item.geometry) return;

            var hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, item.geometry);
            var isHiddenPreset = !!hiddenPresetFeaturesId;

            var button = d3_select(this).selectAll('button.choose');

            d3_select(this).classed('disabled', isHiddenPreset);
            button.classed('disabled', isHiddenPreset);

            if (isHiddenPreset) {
                var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
                var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
                var tooltipObj = { features: t('feature.' + hiddenPresetFeaturesId + '.description') };
                button.call(tooltip('dark')
                    .html(true)
                    .title(t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
                    .placement(index < 2 ? 'bottom' : 'top')
                );
            }
        });
    }

    function chooseExpandable(item, itemSelection) {

        var shouldExpand = !itemSelection.classed('expanded');

        itemSelection.classed('expanded', shouldExpand);

        var iconName = shouldExpand ?
            '#iD-icon-down' : (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward');
        itemSelection.selectAll('.label svg.icon use')
            .attr('href', iconName);

        if (shouldExpand) {
            var subitems = item.subitems();
            var selector = '#' + itemSelection.node().id + ' + *';
            item.subsection = d3_select(itemSelection.node().parentElement).insert('div', selector)
                .attr('class', 'subsection');
            var subitemsEnter = item.subsection.selectAll('.list-item')
                .data(subitems)
                .enter();
            drawItems(subitemsEnter);
            updateForFeatureHiddenState();
            scrollPopoverToShow(item.subsection);
        } else {
            item.subsection.remove();
        }
    }

    function CategoryItem(preset) {
        var item = {};
        item.subsection = d3_select(null);
        item.preset = preset;
        item.choose = function() {
            var selection = d3_select(this);
            if (selection.classed('disabled')) return;
            chooseExpandable(item, d3_select(selection.node().closest('.list-item')));
        };
        item.subitems = function() {
            return preset.members.matchAnyGeometry(shownGeometry).collection.map(function(preset) {
                return itemForPreset(preset);
            });
        };
        return item;
    }

    function MultiGeometryPresetItem(preset, geometries) {

        var item = {};
        item.subsection = d3_select(null);
        item.preset = preset;
        item.geometries = geometries;
        item.choose = function() {
            var selection = d3_select(this);
            if (selection.classed('disabled')) return;
            chooseExpandable(item, d3_select(selection.node().closest('.list-item')));
        };
        item.subitems = function() {
            return geometries.map(function(geometry) {
                return AddablePresetItem(preset, geometry, true);
            });
        };
        return item;
    }

    function AddablePresetItem(preset, geometry, isSubitem) {
        var item = {};
        item.isSubitem = isSubitem;
        item.preset = preset;
        item.geometry = geometry;
        item.choose = function() {
            if (d3_select(this).classed('disabled')) return;

            var markerClass = 'add-preset add-' + geometry +
                ' add-preset-' + preset.name().replace(/\s+/g, '_') + '-' + geometry;
            var modeInfo = {
                button: markerClass,
                preset: preset,
                geometry: geometry
            };
            var mode;
            switch (geometry) {
                case 'point':
                case 'vertex':
                    mode = modeAddPoint(context, modeInfo);
                    break;
                case 'line':
                    mode = modeAddLine(context, modeInfo);
                    break;
                case 'area':
                    mode = modeAddArea(context, modeInfo);
            }
            search.node().blur();
            context.presets().choose(preset);
            context.enter(mode);
        };
        return item;
    }

    return utilRebind(searchAdd, dispatch, 'on');
}
<|endoftext|>


<commit_before>import React from 'react';
import { push } from 'react-router-redux';
import { connect } from 'react-redux';
import SnapshotSelector from './SnapshotSelector';
import { fetchTopicSnapshotsList, filterBySnapshot } from '../../../actions/topicActions';
import composeAsyncWidget from '../../util/composeAsyncWidget';
import filteredLocation from '../../util/composeAsyncWidget';

class SnapshotSelectorContainer extends React.Component {
  componentWillReceiveProps(nextProps) {
    if (nextProps.topicId !== this.props.topicId) {
      const { fetchData } = this.props;
      fetchData(nextProps.topicId, nextProps.snapshotId);
    }
  }
  getStyles() {
    const styles = {
      root: {
        backgroundColor: '#303030',
        color: '#ffffff',
      },
      right: {
        float: 'right',
      },
    };
    return styles;
  }
  render() {
    const { snapshots, onSnapshotSelected, snapshotId } = this.props;
    const styles = this.getStyles();
    return (

          <SnapshotSelector selectedId={snapshotId}
            snapshots={snapshots} onSnapshotSelected={onSnapshotSelected}
          />

    );
  }
}

SnapshotSelectorContainer.propTypes = {
  // from parent
  topicId: React.PropTypes.number.isRequired,
  location: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  onSnapshotSelected: React.PropTypes.func.isRequired,
  // from mergeProps
  asyncFetch: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  snapshots: React.PropTypes.array.isRequired,
  snapshotId: React.PropTypes.number,
};

const mapStateToProps = (state) => ({
  fetchStatus: state.topics.selected.snapshots.fetchStatus,
  snapshots: state.topics.selected.snapshots.list,
  snapshotId: state.topics.selected.filters.snapshotId,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  fetchData: (topicId, snapshotId) => {
    if (topicId !== null) {
      dispatch(fetchTopicSnapshotsList(topicId))
        .then((response) => {
          if (snapshotId === null) {
            const newSnapshotId = response.list[0].snapshots_id;
            const newLocation = filteredLocation(ownProps.location, newSnapshotId, null);
            dispatch(push(newLocation));
            dispatch(filterBySnapshot(newSnapshotId));
          }
        });
    }
  },
  onSnapshotSelected: (snapshotId) => {
    const newLocation = filteredLocation(ownProps.location, snapshotId, null);
    dispatch(push(newLocation));
    dispatch(filterBySnapshot(snapshotId));
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      dispatchProps.fetchData(ownProps.topicId, stateProps.snapshotId);
    },
  });
}

export default
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(
    composeAsyncWidget(
      SnapshotSelectorContainer
    )
  );
<commit_msg>fix location helper import<commit_after>import React from 'react';
import { push } from 'react-router-redux';
import { connect } from 'react-redux';
import SnapshotSelector from './SnapshotSelector';
import { fetchTopicSnapshotsList, filterBySnapshot } from '../../../actions/topicActions';
import composeAsyncWidget from '../../util/composeAsyncWidget';
import { filteredLocation } from '../../util/paging';

class SnapshotSelectorContainer extends React.Component {
  componentWillReceiveProps(nextProps) {
    if (nextProps.topicId !== this.props.topicId) {
      const { fetchData } = this.props;
      fetchData(nextProps.topicId, nextProps.snapshotId);
    }
  }
  getStyles() {
    const styles = {
      root: {
        backgroundColor: '#303030',
        color: '#ffffff',
      },
      right: {
        float: 'right',
      },
    };
    return styles;
  }
  render() {
    const { snapshots, onSnapshotSelected, snapshotId } = this.props;
    const styles = this.getStyles();
    return (

          <SnapshotSelector selectedId={snapshotId}
            snapshots={snapshots} onSnapshotSelected={onSnapshotSelected}
          />

    );
  }
}

SnapshotSelectorContainer.propTypes = {
  // from parent
  topicId: React.PropTypes.number.isRequired,
  location: React.PropTypes.object.isRequired,
  // from dispatch
  fetchData: React.PropTypes.func.isRequired,
  onSnapshotSelected: React.PropTypes.func.isRequired,
  // from mergeProps
  asyncFetch: React.PropTypes.func.isRequired,
  // from state
  fetchStatus: React.PropTypes.string.isRequired,
  snapshots: React.PropTypes.array.isRequired,
  snapshotId: React.PropTypes.number,
};

const mapStateToProps = (state) => ({
  fetchStatus: state.topics.selected.snapshots.fetchStatus,
  snapshots: state.topics.selected.snapshots.list,
  snapshotId: state.topics.selected.filters.snapshotId,
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  fetchData: (topicId, snapshotId) => {
    if (topicId !== null) {
      dispatch(fetchTopicSnapshotsList(topicId))
        .then((response) => {
          if (snapshotId === null) {
            const newSnapshotId = response.list[0].snapshots_id;
            const newLocation = filteredLocation(ownProps.location, newSnapshotId, null);
            dispatch(push(newLocation));
            dispatch(filterBySnapshot(newSnapshotId));
          }
        });
    }
  },
  onSnapshotSelected: (snapshotId) => {
    const newLocation = filteredLocation(ownProps.location, snapshotId, null);
    dispatch(push(newLocation));
    dispatch(filterBySnapshot(snapshotId));
  },
});

function mergeProps(stateProps, dispatchProps, ownProps) {
  return Object.assign({}, stateProps, dispatchProps, ownProps, {
    asyncFetch: () => {
      dispatchProps.fetchData(ownProps.topicId, stateProps.snapshotId);
    },
  });
}

export default
  connect(mapStateToProps, mapDispatchToProps, mergeProps)(
    composeAsyncWidget(
      SnapshotSelectorContainer
    )
  );
<|endoftext|>


<commit_before>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 */

'use strict';

var openiLogger  = require('openi-logger');
var openiUtils   = require('openi-cloudlet-utils');
var zmq          = require('m2nodehandler');
var querystring  = require('querystring');
var https        = require('https');
var loglet      = require('loglet');
loglet = loglet.child({component: 'type-api'});

var allowedTypes = ['boolean', 'int', 'float', 'string', 'url', 'attachment', 'date', 'timestamp', 'hexadecimal', 'base64'];

var init = function(logger_params) {
   this.logger = openiLogger(logger_params);
};

var validateType = function(msg, callback) {

   var type     = msg.json;
   var subTypes = [];
   var errs     = [];
   var type_val;

   if ( !('@reference' in  type) || '' === type['@reference']) {
      errs.push('Required property @reference is missing.');
   }
   if ( !('@context' in  type) || !Array.isArray(type['@context']) || 0 === type['@context'].length ){
      errs.push('Required property @context is missing.');
   }

   for ( var i in type['@context']) {

      if(type['@context'].hasOwnProperty(i)) {
         var entry = type['@context'][i];
         var name  = entry['@property_name'];

         if ( !('@property_name' in  entry) || '' === entry['@property_name']) {
            errs.push('In \'' + name + '\' required parameter @property_name is missing.');
         }

         if ( !('@context_id' in  entry) || '' === entry['@context_id']) {
            errs.push('In \'' + name + '\' required parameter @context_id is missing.');
         }

         if ( !( '@openi_type' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @openi_type is missing.');
         }

         if ( !( '@required' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @required is missing.');
         }

         if ( !( '@multiple' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @multiple is missing.');
         }

         if ( '@allowed_values' in entry ) {
            if ( Object.prototype.toString.call( entry['@allowed_values'] ) !== '[object Array]' || 0 === entry['@allowed_values'].length ){
               errs.push('In \'' + name + '\' @allowed_values array is empty');
            }
            else{
               type = entry['@openi_type'];
               for (var allowed_entry in entry['@allowed_values']){
                  //use validator to check that allowed_entry is of apt type
               }
            }
         }

         if (undefined === entry['@openi_type'] || -1 === allowedTypes.indexOf(entry['@openi_type'].toLowerCase())) {

            type_val = entry['@openi_type'];

            if ( openiUtils.isTypeId(type_val) ){
               subTypes.push(type_val);
            }
            else{
               errs.push(entry['@openi_type'].toLowerCase() + ' is not a valid type.');
            }
         }
      }
   }

   if (errs.length > 0){
      callback( {valid:false, errors:errs});
      loglet.error(errs);
   }
   else if (0 === subTypes.length){
      callback( {valid:true});
   }
   else{
      type_val = subTypes.pop();
      validateSubType(msg, type_val, subTypes, callback);
   }
};

var validateSubType = function(msg, type_id, subTypes, callback){

   var type = "https://" + msg.headers.host + "/api/v1/types/" + type_id;

   var request = https.get(type, function(res) {

      var typeStr2 = '';

      res.on('data', function (chunk) {
         typeStr2 += chunk;
      });

      res.on('end', function () {

         var type_json = {};

         try{
            type_json = JSON.parse(typeStr2);
         }
         catch(e){
            loglet.error(e);
            return callback( {valid:false, error:[ type + ' cannot be found.']});
         }

         if (type_json["@id"] === type_id ){
            if (subTypes.length > 0){
               var new_type = subTypes.pop();
               validateSubType(msg, new_type, subTypes, callback);
            }
            else{
               return callback( {valid:true});
            }
         }
         else{
            return callback( {valid:false, error:[ type + ' is not a valid type.']});
         }
      });

   }).on('error', function(e) {
         loglet.error(e);
         return  callback( {valid:false, error:[ type + ' is not a valid type.' ]});
      }).setTimeout( 5000, function( ) {
         return callback( {valid:false, error:[ type + ' could not be found.']});
      });
};

var genPostMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   if (undefined === msg.json || null === msg.json){

      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Empty body.' });

      return;
   }

   var typeId = 't_' + openiUtils.hash(msg.json);

   var typeRestUrl = "/api/v1/types/" + typeId;

   msg.json['@id']            = typeId;
   msg.json['@location']      = typeRestUrl;
   msg.json['_date_created'] = new Date().toJSON();

   validateType(msg, function(validCheck){

      if (!validCheck.valid) {

         var errorString = (undefined === validCheck.errors)  ? "" : validCheck.errors.join(" ");

         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type object isn\'t properly structured : ' + errorString});

         return;
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'POST',
               'database'    : typeId,
               'object_name' : 'meta',
               'object_data' : msg.json,
               'id'          : typeId,
               'bucket'      : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   });
};

var genGetMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   var query = querystring.parse(msg.headers.QUERY);

   if ( msg.path === '/api/v1/types' || msg.path === '/api/v1/types/' ) {

      var limit  = (undefined !== query.limit)  ? Number(query.limit)   : 30;
      var offset = (undefined !== query.offset) ? Number(query.offset)  :  0;
      var prev   = msg.headers.URI.replace("offset="+offset, "offset="+ (((offset - limit) < 0) ? 0 : (offset - limit)));
      var next   = msg.headers.URI.replace("offset="+offset, "offset="+ (offset + limit));

      var meta = {
         "limit"       : limit,
         "offset"      : offset,
         "total_count" : 0,
         "prev"        : (0 === offset)? null : prev,
         "next"        : next
      };

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'VIEW',
               'design_doc'   : 'type_views',
               'view_name'    : 'types_list',
               'meta'         : meta,
               'filter_show'  : query['only_show_properties'],
               'content-type' : query['content-type'],
               'resp_type'    : 'type',
               'id_only'      : ('true' === query.id_only),
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   } else if ( msg.path === '/api/v1/types/stats' ) {

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'VIEW',
               'design_doc'   : 'type_views',
               'view_name'    : 'types_usage',
               'group'        : 0,
               'resp_type'    : 'type_stats',
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   } else {

      var type =   openiUtils.extractTypeId(msg.path);

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'GET',
               'database'     : type,
               'id'           : type,
               'content-type' : query['content-type'],
               'resp_type'    : 'type',
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   }
};

var genPatchMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   return senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Feature temporarily disabled' });

   if (undefined === msg.json || null === msg.json) {

      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type array is empty' });

      return;
   }

   var actions = [];

   for ( var i =0; i < msg.json.length; i++) {
      var entry       = msg[i];
      var typeId      = 't_' + openiUtils.hash(entry);
      var typeRestUrl = "/api/v1/types/" + typeId;

      if (!validateType(entry)) {

         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type object isn\'t properly structured' });

         return;
      }

      entry['@id']            = typeId;
      entry['@location']      = typeRestUrl;
      entry['_date_created']  = new Date().toJSON();

      actions.push({
         'action'      : 'POST',
         'database'    : typeId,
         'object_name' : 'meta',
         'object_data' : entry,
         'id'          : typeId,
         'resp_type'   : 'type',
         'bucket'      : 'types'
      });
   }

   senderToDao.send({
      'dao_actions'  : actions,
      'mongrel_sink' : terminal_handler,
      'clients'      : [
         {
            'uuid' : msg.uuid,
            'connId' : msg.connId
         }
      ]
   });
};

var processMongrel2Message = function (msg, senderToDao, senderToClient, terminal_handler) {

   this.logger.logMongrel2Message(msg);

   for(var key in msg.json) {
      if(msg.json.hasOwnProperty(key)) {
         if(msg.json[key] === null) {
            delete msg.json[key];
         }
      }
   }

   switch(msg.headers['METHOD']) {
      case 'POST':
         genPostMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      case 'GET':
         genGetMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      case 'PATCH':
         genPatchMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      default:
         break;
   }
};

module.exports.init                   = init;
module.exports.processMongrel2Message = processMongrel2Message;<commit_msg>Bug fix all over again<commit_after>/*
 * openi_data_api
 * openi-ict.eu
 *
 * Copyright (c) 2013 dmccarthy
 */

'use strict';

var openiLogger  = require('openi-logger');
var openiUtils   = require('openi-cloudlet-utils');
var zmq          = require('m2nodehandler');
var querystring  = require('querystring');
var https        = require('https');
var loglet      = require('loglet');
loglet = loglet.child({component: 'type-api'});

var allowedTypes = ['boolean', 'int', 'float', 'string', 'url', 'attachment', 'date', 'timestamp', 'hexadecimal', 'base64'];

var init = function(logger_params) {
   this.logger = openiLogger(logger_params);
};

var validateType = function(msg, callback) {

   var type     = msg.json;
   var subTypes = [];
   var errs     = [];
   var type_val;

   if ( !('@reference' in  type) || '' === type['@reference']) {
      errs.push('Required property @reference is missing.');
   }
   if ( !('@context' in  type) || !Array.isArray(type['@context']) || 0 === type['@context'].length ){
      errs.push('Required property @context is missing.');
   }

   for ( var i in type['@context']) {

      if(type['@context'].hasOwnProperty(i)) {
         var entry = type['@context'][i];
         var name  = entry['@property_name'];

         if ( !('@property_name' in  entry) || '' === entry['@property_name']) {
            errs.push('In \'' + name + '\' required parameter @property_name is missing.');
         }

         if ( !('@context_id' in  entry) || '' === entry['@context_id']) {
            errs.push('In \'' + name + '\' required parameter @context_id is missing.');
         }

         if ( !( '@openi_type' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @openi_type is missing.');
         }

         if ( !( '@required' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @required is missing.');
         }

         if ( !( '@multiple' in entry) ) {
            errs.push('In \'' + name + '\' required parameter @multiple is missing.');
         }

         if ( '@allowed_values' in entry ) {
            if ( Object.prototype.toString.call( entry['@allowed_values'] ) !== '[object Array]' || 0 === entry['@allowed_values'].length ){
               errs.push('In \'' + name + '\' @allowed_values array is empty');
            }
            else{
               //type = entry['@openi_type'];
               for (var allowed_entry in entry['@allowed_values']){
                  //use validator to check that allowed_entry is of apt type
               }
            }
         }

         if (undefined === entry['@openi_type'] || -1 === allowedTypes.indexOf(entry['@openi_type'].toLowerCase())) {

            type_val = entry['@openi_type'];

            if ( openiUtils.isTypeId(type_val) ){
               subTypes.push(type_val);
            }
            else{
               errs.push(entry['@openi_type'].toLowerCase() + ' is not a valid type.');
            }
         }
      }
   }

   if (errs.length > 0){
      callback( {valid:false, errors:errs});
      loglet.error(errs);
   }
   else if (0 === subTypes.length){
      callback( {valid:true});
   }
   else{
      type_val = subTypes.pop();
      validateSubType(msg, type_val, subTypes, callback);
   }
};

var validateSubType = function(msg, type_id, subTypes, callback){

   var type = "https://" + msg.headers.host + "/api/v1/types/" + type_id;

   var request = https.get(type, function(res) {

      var typeStr2 = '';

      res.on('data', function (chunk) {
         typeStr2 += chunk;
      });

      res.on('end', function () {

         var type_json = {};

         try{
            type_json = JSON.parse(typeStr2);
         }
         catch(e){
            loglet.error(e);
            return callback( {valid:false, error:[ type + ' cannot be found.']});
         }

         if (type_json["@id"] === type_id ){
            if (subTypes.length > 0){
               var new_type = subTypes.pop();
               validateSubType(msg, new_type, subTypes, callback);
            }
            else{
               return callback( {valid:true});
            }
         }
         else{
            return callback( {valid:false, error:[ type + ' is not a valid type.']});
         }
      });

   }).on('error', function(e) {
         loglet.error(e);
         return  callback( {valid:false, error:[ type + ' is not a valid type.' ]});
      }).setTimeout( 5000, function( ) {
         return callback( {valid:false, error:[ type + ' could not be found.']});
      });
};

var genPostMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   if (undefined === msg.json || null === msg.json){

      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Empty body.' });

      return;
   }

   var typeId = 't_' + openiUtils.hash(msg.json);

   var typeRestUrl = "/api/v1/types/" + typeId;

   msg.json['@id']            = typeId;
   msg.json['@location']      = typeRestUrl;
   msg.json['_date_created'] = new Date().toJSON();

   validateType(msg, function(validCheck){

      if (!validCheck.valid) {

         var errorString = (undefined === validCheck.errors)  ? "" : validCheck.errors.join(" ");

         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type object isn\'t properly structured : ' + errorString});

         return;
      }

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'      : 'POST',
               'database'    : typeId,
               'object_name' : 'meta',
               'object_data' : msg.json,
               'id'          : typeId,
               'bucket'      : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   });
};

var genGetMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   var query = querystring.parse(msg.headers.QUERY);

   if ( msg.path === '/api/v1/types' || msg.path === '/api/v1/types/' ) {

      var limit  = (undefined !== query.limit)  ? Number(query.limit)   : 30;
      var offset = (undefined !== query.offset) ? Number(query.offset)  :  0;
      var prev   = msg.headers.URI.replace("offset="+offset, "offset="+ (((offset - limit) < 0) ? 0 : (offset - limit)));
      var next   = msg.headers.URI.replace("offset="+offset, "offset="+ (offset + limit));

      var meta = {
         "limit"       : limit,
         "offset"      : offset,
         "total_count" : 0,
         "prev"        : (0 === offset)? null : prev,
         "next"        : next
      };

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'VIEW',
               'design_doc'   : 'type_views',
               'view_name'    : 'types_list',
               'meta'         : meta,
               'filter_show'  : query['only_show_properties'],
               'content-type' : query['content-type'],
               'resp_type'    : 'type',
               'id_only'      : ('true' === query.id_only),
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   } else if ( msg.path === '/api/v1/types/stats' ) {

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'VIEW',
               'design_doc'   : 'type_views',
               'view_name'    : 'types_usage',
               'group'        : 0,
               'resp_type'    : 'type_stats',
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   } else {

      var type =   openiUtils.extractTypeId(msg.path);

      senderToDao.send({
         'dao_actions'      : [
            {
               'action'       : 'GET',
               'database'     : type,
               'id'           : type,
               'content-type' : query['content-type'],
               'resp_type'    : 'type',
               'bucket'       : 'types'
            }
         ],
         'mongrel_sink' : terminal_handler,
         'clients'      : [
            {
               'uuid' : msg.uuid,
               'connId' : msg.connId
            }
         ]
      });
   }
};

var genPatchMessage = function(msg, senderToDao, senderToClient, terminal_handler) {

   return senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Feature temporarily disabled' });

   if (undefined === msg.json || null === msg.json) {

      senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type array is empty' });

      return;
   }

   var actions = [];

   for ( var i =0; i < msg.json.length; i++) {
      var entry       = msg[i];
      var typeId      = 't_' + openiUtils.hash(entry);
      var typeRestUrl = "/api/v1/types/" + typeId;

      if (!validateType(entry)) {

         senderToClient.send(msg.uuid, msg.connId, zmq.status.BAD_REQUEST_400, zmq.standard_headers.json, {'error' : 'Type object isn\'t properly structured' });

         return;
      }

      entry['@id']            = typeId;
      entry['@location']      = typeRestUrl;
      entry['_date_created']  = new Date().toJSON();

      actions.push({
         'action'      : 'POST',
         'database'    : typeId,
         'object_name' : 'meta',
         'object_data' : entry,
         'id'          : typeId,
         'resp_type'   : 'type',
         'bucket'      : 'types'
      });
   }

   senderToDao.send({
      'dao_actions'  : actions,
      'mongrel_sink' : terminal_handler,
      'clients'      : [
         {
            'uuid' : msg.uuid,
            'connId' : msg.connId
         }
      ]
   });
};

var processMongrel2Message = function (msg, senderToDao, senderToClient, terminal_handler) {

   this.logger.logMongrel2Message(msg);

   for(var key in msg.json) {
      if(msg.json.hasOwnProperty(key)) {
         if(msg.json[key] === null) {
            delete msg.json[key];
         }
      }
   }

   switch(msg.headers['METHOD']) {
      case 'POST':
         genPostMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      case 'GET':
         genGetMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      case 'PATCH':
         genPatchMessage(msg, senderToDao, senderToClient, terminal_handler);
         break;
      default:
         break;
   }
};

module.exports.init                   = init;
module.exports.processMongrel2Message = processMongrel2Message;<|endoftext|>


<commit_before>/*
 * 1.1 Is Unique: Implement an algorithm to determine if a string has all unique characters.
 * What if you cannot use additional data structures?
 */

// test > 1.1 determine if a string has all unique characters
// # isUnique('nick') == true (nick has all unique charactares)
// # isUnique('balestra') == false (nick hasnt all unique charactares)
exports.isUnique = function(str) {
  var charHash = {};

  return str.split('').reduce(function(prev, current) {
    if (prev && charHash[current] === undefined) {
      charHash[current] = current;
      return true;
    }
    return false;
  }, true);
};
<commit_msg>minor change on isUnique<commit_after>/*
 * 1.1 Is Unique: Implement an algorithm to determine if a string has all unique characters.
 * What if you cannot use additional data structures?
 */

// test > 1.1 determine if a string has all unique characters
// # isUnique('nick') == true (nick has all unique charactares)
// # isUnique('balestra') == false (nick hasnt all unique charactares)
exports.isUnique = function(str) {
  var charHash = {};

  return str.split('').reduce(function(prev, current) {
    if (prev && !charHash[current]) {
      charHash[current] = true;
      return prev;
    }
    return false;
  }, true);
};
<|endoftext|>


<commit_before>'use strict';

require('../globals');

module.exports = binning;

function binning(dataTable, encoding, opt) {
  opt = opt || {};

  if (!dataTable.transform) dataTable.transform = [];

  encoding.forEach(function(field, encType) {
    if (encoding.bin(encType)) {
      dataTable.transform.push({
        type: 'bin',
        field: 'data.' + field.name,
        output: 'data.bin_' + field.name,
        maxbins: encoding.bin(encType).maxbins
      });
    }
  });
}
<commit_msg>correct use encoding.field in bin<commit_after>'use strict';

require('../globals');

module.exports = binning;

function binning(dataTable, encoding, opt) {
  opt = opt || {};

  if (!dataTable.transform) dataTable.transform = [];

  encoding.forEach(function(field, encType) {
    if (encoding.bin(encType)) {
      dataTable.transform.push({
        type: 'bin',
        field: encoding.field(encType, false, /*nofn*/ true),
        output: encoding.field(encType),
        maxbins: encoding.bin(encType).maxbins
      });
    }
  });
}
<|endoftext|>


<commit_before>/****************
Utility functions for the adapters
*/
var md5 = require('md5');
var extend = require('xtend');
var _ = require('lodash');
var uuid = require('uuid');

//bypass the Logging, if sails is not available
var logger = 'undefined' !== typeof sails ? sails.log : console;
if (!logger.debug)
  logger.debug = console.log;

module.exports = {
  /*Convert a config block to a nano specific connection string */
  urlForConfig: function(config) {
    var schema = 'http';
    if (config.https) schema += 's';

    var auth = '';
    if (config.username && config.password) {
      auth = encodeURIComponent(config.username) + ':' + encodeURIComponent(config.password) + '@';
    }

    return [schema, '://', auth, config.host, ':', config.port, '/'].join('');
  },
  /*shorthand to create/update views.
  use update=false, to replace existing doc */
  createView: function(db, viewObj, designDoc, cb, update) {
    if (!update)
      update = true;
    var key = '_design/' + designDoc;
    db.get(key, function(err, existing) {
      if (err && err['statusCode'] != 404) // any other error than not found, return
        return cb(err);
      var hash = module.exports.getHash(viewObj);
      if (update && existing && existing.doc_hash == hash) {
        logger.info('Hash matches view for ' + key + '. Skipping update');
        return cb();
      }

      if (!update) { //the view has to be replaced
        var tmp = {};
        tmp._rev = existing._rev;
        existing = tmp; //just retain the _rev parameter so that it updates
      }
      var obj = existing ? _.merge(existing, viewObj) : viewObj;

      module.exports.insert(db, obj, key, function(err, response) {
        if (err) {
          return cb(err);
        }
        logger.info('design document ' + key + ' added.');
        cb();
      }, hash);
    });

  },
  /* shortcut to build a view by passing view name and map function */
  buildView: function(name, map_func) {
    var view = {
      "views": {}
    };
    view.views[name] = {};
    view.views[name]["map"] = map_func;
    return view;
  },

  /* create a name for the view */
  getViewName: function(where) {
    return ['by'].concat(Object.keys(where).sort()).join('_');
  },
  /* create map function for the view */

  createViewMap: function(where, collectionName) {
    var keys = Object.keys(where).sort();
    var if_condition = ['if ('];
    keys.map(function(key, index) {
      if_condition = if_condition.concat(['doc.' + key, ' &&']);
      //if (index < keys.length - 1) //last element, dont add &&
      //  if_condition = if_condition.concat(['&&', ' ']);
    });
    //add final doctype filter
    if_condition = if_condition.concat(['doc.doc_type == "', collectionName, '"']);

    if_condition = if_condition.concat([')']);
    var emit = ['{', 'emit(['];
    keys.map(function(key, index) {
      emit = emit.concat(['doc.' + key]);
      if (index < keys.length - 1) //last element, dont add comma
        emit = emit.concat([',', ' ']);
    });
    emit = emit.concat([']', ', ', 'doc._id', ');', '}', ]);

    var body = if_condition.concat(emit).join('');
    var func = 'function(doc) {' + body + '}';
    logger.info('Generated emit function: ', func);

    return func;
  },
  getHash: function(data) {
    return data !== null && typeof data === 'object' ? md5(JSON.stringify(data)) : md5(data + ''); //quick string conversion
  },

  /*Quick helper for insert, to avoid dual computation,
  if a hash is passed, just use the passed hash */
  insert: function(db, obj, key, cb, hash) {
    if (!hash)
      hash = module.exports.getHash(obj);
    obj.doc_hash = hash;
    db.insert(obj, key, function(err, body, header) {
      if (err) {
        return cb(err);
      }
      //attach the _id and rev properties
      obj._id = body.id;
      obj._rev = body.rev;
      obj = module.exports.preprocess(obj);
      cb(null, obj); //switch the id back
    });
  },
  /* Prepare a document to be added to the database.
  Essentially, fix the id column.
  TODO: Convert any datetime objects to arrays here
  TODO: Eventually introduce logtime
  */
  prepare: function(obj, type, metadata) {
    var doc = extend({}, obj);
    if (doc.id) {
      doc._id = doc.id;
      doc._attachments = doc.attachments;
      delete doc.id;
    } else {
      doc._id = uuid.v4();
      doc._attachments = {};
    }
    doc.doc_type = type;
    return doc;
  },
  //preprocess the results of a query, primarily to fix the id,
  // and reverse the damages of prepare() above
  preprocess: function(obj, metadata) {
    var doc = extend({}, obj);
    doc.id = doc._id.split('/')[1]; //TODO: invalid assumption of only single / in id
    doc.attachments = doc._attachments;
    delete doc._attachments;
    delete doc._id;
    return doc;
  },

  delete: function(db, obj, key, cb) {
    //delete is just updating the document with _delete prop set to true
    //TODO: Find if this is the best choice.
    obj._deleted = true;
    return module.exports.update(db, obj, key, cb);
  },

  update: function(db, obj, key, cb) {
    if (obj._rev) //the object has a revision id, just try inserting
      return module.exports.insert(db, obj, key, cb);
    //if object does not have a rev id, continue to get it
    db.get(key, function(err, existing) {
      if (err || !existing) { // update should not be called on non existent objects
        if (!err && !existing)
          err = new Error('The object does not exist/not found');
        return cb(err);
      }
      obj._rev = existing._rev;
      module.exports.insert(db, obj, key, cb);
    });
  },

  /* Check if the key is present in database, if it is and check_hash is false
     Update. If check_hash is true, compare the calculated hash of data with
     the existing hash, if different, Update.
     If the key is not present, insert
     */
  getAttachmentData: function(db, obj, key, cb) {
    var filename = obj.filename;
    db.attachment.get(key, filename, function(err, body){
      if(err && err['statusCode'] != 404)
        return cb(err,null);
      return cb(null, body);
    });
  },

  upsert: function(db, obj, key, cb, check_hash) {
    db.get(key, function(err, existing) {
      if (err && err['statusCode'] != 404) // any other error than not found, return
        return cb(err);
      var hash = module.exports.getHash(obj);
      if (existing && check_hash && existing.doc_hash == hash) {
        logger.info('Hash matches for ' + key + '. Skipping');
        return cb(null, existing);
      }
      if (existing)
        obj._rev = existing._rev;
      module.exports.insert(db, obj, key, cb, hash);
    });

  }

};
<commit_msg>minor fix<commit_after>/****************
Utility functions for the adapters
*/
var md5 = require('md5');
var extend = require('xtend');
var _ = require('lodash');
var uuid = require('uuid');

//bypass the Logging, if sails is not available
var logger = 'undefined' !== typeof sails ? sails.log : console;
if (!logger.debug)
  logger.debug = console.log;

module.exports = {
  /*Convert a config block to a nano specific connection string */
  urlForConfig: function(config) {
    var schema = 'http';
    if (config.https) schema += 's';

    var auth = '';
    if (config.username && config.password) {
      auth = encodeURIComponent(config.username) + ':' + encodeURIComponent(config.password) + '@';
    }

    return [schema, '://', auth, config.host, ':', config.port, '/'].join('');
  },
  /*shorthand to create/update views.
  use update=false, to replace existing doc */
  createView: function(db, viewObj, designDoc, cb, update) {
    if (!update)
      update = true;
    var key = '_design/' + designDoc;
    db.get(key, function(err, existing) {
      if (err && err['statusCode'] != 404) // any other error than not found, return
        return cb(err);
      var hash = module.exports.getHash(viewObj);
      if (update && existing && existing.doc_hash == hash) {
        logger.info('Hash matches view for ' + key + '. Skipping update');
        return cb();
      }

      if (!update) { //the view has to be replaced
        var tmp = {};
        tmp._rev = existing._rev;
        existing = tmp; //just retain the _rev parameter so that it updates
      }
      var obj = existing ? _.merge(existing, viewObj) : viewObj;

      module.exports.insert(db, obj, key, function(err, response) {
        if (err) {
          return cb(err);
        }
        logger.info('design document ' + key + ' added.');
        cb();
      }, hash);
    });

  },
  /* shortcut to build a view by passing view name and map function */
  buildView: function(name, map_func) {
    var view = {
      "views": {}
    };
    view.views[name] = {};
    view.views[name]["map"] = map_func;
    return view;
  },

  /* create a name for the view */
  getViewName: function(where) {
    return ['by'].concat(Object.keys(where).sort()).join('_');
  },
  /* create map function for the view */

  createViewMap: function(where, collectionName) {
    var keys = Object.keys(where).sort();
    var if_condition = ['if ('];
    keys.map(function(key, index) {
      if_condition = if_condition.concat(['doc.' + key, ' &&']);
      //if (index < keys.length - 1) //last element, dont add &&
      //  if_condition = if_condition.concat(['&&', ' ']);
    });
    //add final doctype filter
    if_condition = if_condition.concat(['doc.doc_type == "', collectionName, '"']);

    if_condition = if_condition.concat([')']);
    var emit = ['{', 'emit(['];
    keys.map(function(key, index) {
      emit = emit.concat(['doc.' + key]);
      if (index < keys.length - 1) //last element, dont add comma
        emit = emit.concat([',', ' ']);
    });
    emit = emit.concat([']', ', ', 'doc._id', ');', '}', ]);

    var body = if_condition.concat(emit).join('');
    var func = 'function(doc) {' + body + '}';
    logger.info('Generated emit function: ', func);

    return func;
  },
  getHash: function(data) {
    return data !== null && typeof data === 'object' ? md5(JSON.stringify(data)) : md5(data + ''); //quick string conversion
  },

  /*Quick helper for insert, to avoid dual computation,
  if a hash is passed, just use the passed hash */
  insert: function(db, obj, key, cb, hash) {
    if (!hash)
      hash = module.exports.getHash(obj);
    obj.doc_hash = hash;
    db.insert(obj, key, function(err, body, header) {
      if (err) {
        return cb(err);
      }
      //attach the _id and rev properties
      obj._id = body.id;
      obj._rev = body.rev;
      obj = module.exports.preprocess(obj);
      cb(null, obj); //switch the id back
    });
  },
  /* Prepare a document to be added to the database.
  Essentially, fix the id column.
  TODO: Convert any datetime objects to arrays here
  TODO: Eventually introduce logtime
  */
  prepare: function(obj, type, metadata) {
    var doc = extend({}, obj);
    if (doc.id) {
      doc._id = doc.id;
      doc._attachments = doc.attachments;
      delete doc.id;
      delete doc.attachments;
    } else {
      doc._id = uuid.v4();
      doc._attachments = {};
    }
    doc.doc_type = type;
    return doc;
  },
  //preprocess the results of a query, primarily to fix the id,
  // and reverse the damages of prepare() above
  preprocess: function(obj, metadata) {
    var doc = extend({}, obj);
    doc.id = doc._id.split('/')[1]; //TODO: invalid assumption of only single / in id
    doc.attachments = doc._attachments;
    delete doc._attachments;
    delete doc._id;
    return doc;
  },

  delete: function(db, obj, key, cb) {
    //delete is just updating the document with _delete prop set to true
    //TODO: Find if this is the best choice.
    obj._deleted = true;
    return module.exports.update(db, obj, key, cb);
  },

  update: function(db, obj, key, cb) {
    if (obj._rev) //the object has a revision id, just try inserting
      return module.exports.insert(db, obj, key, cb);
    //if object does not have a rev id, continue to get it
    db.get(key, function(err, existing) {
      if (err || !existing) { // update should not be called on non existent objects
        if (!err && !existing)
          err = new Error('The object does not exist/not found');
        return cb(err);
      }
      obj._rev = existing._rev;
      module.exports.insert(db, obj, key, cb);
    });
  },

  /* Check if the key is present in database, if it is and check_hash is false
     Update. If check_hash is true, compare the calculated hash of data with
     the existing hash, if different, Update.
     If the key is not present, insert
     */
  getAttachmentData: function(db, obj, key, cb) {
    var filename = obj.filename;
    db.attachment.get(key, filename, function(err, body){
      if(err && err['statusCode'] != 404)
        return cb(err,null);
      return cb(null, body);
    });
  },

  upsert: function(db, obj, key, cb, check_hash) {
    db.get(key, function(err, existing) {
      if (err && err['statusCode'] != 404) // any other error than not found, return
        return cb(err);
      var hash = module.exports.getHash(obj);
      if (existing && check_hash && existing.doc_hash == hash) {
        logger.info('Hash matches for ' + key + '. Skipping');
        return cb(null, existing);
      }
      if (existing)
        obj._rev = existing._rev;
      module.exports.insert(db, obj, key, cb, hash);
    });

  }

};
<|endoftext|>


<commit_before>"use strict";

(function() {

walContext.Composition = function( wCtx ) {
	this.wCtx = wCtx;
	this.wSamples = [];
	this.lastSample = null;
	this.isPlaying =
	this.isPaused = false;
	this.duration =
	this.startedTime =
	this._currentTime = 0;
	this.fnOnended =
	this.fnOnpaused = function() {};
};

function updateLastSample( compo ) {
	var end, last = null, maxend = 0;
	compo.wSamples.forEach( function( ws ) {
		end = ws.getEndTime();
		if ( end > maxend ) {
			maxend = end;
			last = ws;
		}
	} );
	compo.lastSample = last;
	compo.duration = last ? last.getEndTime() : 0;
}

function updateTimeout( compo ) {
	clearTimeout( compo.playTimeoutId );
	var sec = compo.duration && 1000 * ( compo.duration - compo.currentTime() );
	if ( sec <= 0 ) {
		compo.onended();
	} else {
		compo.playTimeoutId = setTimeout( compo.onended.bind( compo ), sec );
	}
}

function startSampleFrom( ws, compoOffset ) {
	var start = ws.when - compoOffset,
		offset = start >= 0 ? ws.offset : ws.offset - start;
	ws.start( start, Math.max( offset, 0 ) );
}

function updateInLive( compo, ws, action, oldLast ) {
	var ct = compo.currentTime();
	if ( ws.getEndTime() > ct && action !== "rm" ) {
		ws.load();
		startSampleFrom( ws, ct );
	}
	if ( compo.lastSample !== oldLast || action === "mv" ) {
		updateTimeout( compo );
	}
}

function softStop( compo ) {
	compo.wSamples.forEach( function( ws ) {
		ws.stop();
	});
}

function softLoad( compo ) {
	var ct = compo.currentTime();
	compo.wSamples.forEach( function( ws ) {
		if ( ws.getEndTime() > ct ) {
			ws.load();
		}
	});
}

function softPlay( compo ) {
	var ct = compo.currentTime();
	compo.wSamples.forEach( function( ws ) {
		if ( ws.getEndTime() > ct ) {
			startSampleFrom( ws, ct );
		}
	});
	updateTimeout( compo );
}

walContext.Composition.prototype = {
	addSamples: function( wsamples ) {
		var that = this;
		wsamples.forEach( function( ws ) {
			if ( that.wSamples.indexOf( ws ) < 0 ) {
				that.wSamples.push( ws );
				ws.setComposition( ws );
				that.update( ws );
			}
		});
		return this;
	},
	removeSamples: function( wsamples ) {
		var ind, that = this;
		wsamples.forEach( function( ws ) {
			ind = that.wSamples.indexOf( ws );
			if ( ind > -1 ) {
				that.wSamples.splice( ind, 1 );
				ws.setComposition( null );
				that.update( ws, "rm" );
			}
		});
		return this;
	},
	update: function( ws, action ) {
		var that = this,
			oldLast = this.lastSample,
			save;

		updateLastSample( this );
		if ( this.isPlaying ) {
			if ( ws.started ) {
				save = ws.fnOnended;
				ws.onended( function() {
					updateInLive( that, ws, action, oldLast );
					save();
					ws.onended( save );
				});
				ws.stop();
			} else {
				updateInLive( this, ws, action, oldLast );
			}
		}
		return this;
	},
	currentTime: function( sec ) {
		if ( !arguments.length ) {
			return this._currentTime +
				( this.isPlaying && wa.wctx.ctx.currentTime - this.startedTime );
		}
		if ( this.isPlaying ) {
			softStop( this );
		}
		this._currentTime = Math.max( 0, Math.min( sec, this.duration ) );
		if ( this.isPlaying ) {
			this.startedTime = this.wCtx.ctx.currentTime;
			softLoad( this );
			softPlay( this );
		}
		return this;
	},
	play: function() {
		if ( !this.isPlaying ) {
			this.isPlaying = true;
			this.isPaused = false;
			this.startedTime = wa.wctx.ctx.currentTime;
			softLoad( this );
			softPlay( this );
		}
		return this;
	},
	stop: function() {
		if ( this.isPlaying || this.isPaused ) {
			softStop( this );
			this.onended();
		}
		return this;
	},
	pause: function() {
		if ( this.isPlaying ) {
			this.isPlaying = false;
			this.isPaused = true;
			this._currentTime += wa.wctx.ctx.currentTime - this.startedTime;
			this.startedTime = 0;
			softStop( this );
			clearTimeout( this.playTimeoutId );
			this.fnOnpaused();
		}
	},
	onended: function( fn ) {
		if ( typeof fn === "function" ) {
			this.fnOnended = fn;
		} else {
			clearTimeout( this.playTimeoutId );
			this.isPlaying =
			this.isPaused = false;
			this.startedTime =
			this._currentTime = 0;
			this.fnOnended();
		}
		return this;
	}
};

})();<commit_msg>composition: quick and temporary fix for gridsound (to let the possibility to modify the BPM in live)<commit_after>"use strict";

(function() {

walContext.Composition = function( wCtx ) {
	this.wCtx = wCtx;
	this.wSamples = [];
	this.lastSample = null;
	this.isPlaying =
	this.isPaused = false;
	this.duration =
	this.startedTime =
	this._currentTime = 0;
	this.fnOnended =
	this.fnOnpaused = function() {};
};

function updateLastSample( compo ) {
	var end, last = null, maxend = 0;
	compo.wSamples.forEach( function( ws ) {
		end = ws.getEndTime();
		if ( end > maxend ) {
			maxend = end;
			last = ws;
		}
	} );
	compo.lastSample = last;
	compo.duration = last ? last.getEndTime() : 0;
}

function updateTimeout( compo ) {
	clearTimeout( compo.playTimeoutId );
	updateLastSample( compo ); // <-- this line is here just for fix a pb in GS...
	var sec = compo.duration && compo.duration - compo.currentTime();
	if ( sec <= 0 ) {
		compo.onended();
	} else {
		compo.playTimeoutId = setTimeout( compo.onended.bind( compo ), sec * 1000 );
	}
}

function startSampleFrom( ws, compoOffset ) {
	var start = ws.when - compoOffset,
		offset = start >= 0 ? ws.offset : ws.offset - start;
	ws.start( start, Math.max( offset, 0 ) );
}

function updateInLive( compo, ws, action, oldLast ) {
	var ct = compo.currentTime();
	if ( ws.getEndTime() > ct && action !== "rm" ) {
		ws.load();
		startSampleFrom( ws, ct );
	}
	if ( compo.lastSample !== oldLast || action === "mv" ) {
		updateTimeout( compo );
	}
}

function softStop( compo ) {
	clearTimeout( compo.playTimeoutId );
	compo.wSamples.forEach( function( ws ) {
		ws.stop();
	});
}

function softLoad( compo ) {
	var ct = compo.currentTime();
	compo.wSamples.forEach( function( ws ) {
		if ( ws.getEndTime() > ct ) {
			ws.load();
		}
	});
}

function softPlay( compo ) {
	var ct = compo.currentTime();
	compo.wSamples.forEach( function( ws ) {
		if ( ws.getEndTime() > ct ) {
			startSampleFrom( ws, ct );
		}
	});
	updateTimeout( compo );
}

walContext.Composition.prototype = {
	addSamples: function( wsamples ) {
		var that = this;
		wsamples.forEach( function( ws ) {
			if ( that.wSamples.indexOf( ws ) < 0 ) {
				that.wSamples.push( ws );
				ws.setComposition( ws );
				that.update( ws );
			}
		});
		return this;
	},
	removeSamples: function( wsamples ) {
		var ind, that = this;
		wsamples.forEach( function( ws ) {
			ind = that.wSamples.indexOf( ws );
			if ( ind > -1 ) {
				that.wSamples.splice( ind, 1 );
				ws.setComposition( null );
				that.update( ws, "rm" );
			}
		});
		return this;
	},
	update: function( ws, action ) {
		var that = this,
			oldLast = this.lastSample,
			save;

		updateLastSample( this );
		if ( this.isPlaying ) {
			if ( ws.started ) {
				save = ws.fnOnended;
				ws.onended( function() {
					updateInLive( that, ws, action, oldLast );
					save();
					ws.onended( save );
				});
				ws.stop();
			} else {
				updateInLive( this, ws, action, oldLast );
			}
		}
		return this;
	},
	currentTime: function( sec ) {
		if ( !arguments.length ) {
			return this._currentTime +
				( this.isPlaying && wa.wctx.ctx.currentTime - this.startedTime );
		}
		if ( this.isPlaying ) {
			softStop( this );
		}
		this._currentTime = Math.max( 0, Math.min( sec, this.duration ) );
		if ( this.isPlaying ) {
			this.startedTime = this.wCtx.ctx.currentTime;
			softLoad( this );
			softPlay( this );
		}
		return this;
	},
	play: function() {
		if ( !this.isPlaying ) {
			this.isPlaying = true;
			this.isPaused = false;
			this.startedTime = wa.wctx.ctx.currentTime;
			softLoad( this );
			softPlay( this );
		}
		return this;
	},
	stop: function() {
		if ( this.isPlaying || this.isPaused ) {
			softStop( this );
			this.onended();
		}
		return this;
	},
	pause: function() {
		if ( this.isPlaying ) {
			this.isPlaying = false;
			this.isPaused = true;
			this._currentTime += wa.wctx.ctx.currentTime - this.startedTime;
			this.startedTime = 0;
			softStop( this );
			this.fnOnpaused();
		}
	},
	onended: function( fn ) {
		if ( typeof fn === "function" ) {
			this.fnOnended = fn;
		} else {
			this.isPlaying =
			this.isPaused = false;
			this.startedTime =
			this._currentTime = 0;
			this.fnOnended();
		}
		return this;
	}
};

})();<|endoftext|>


<commit_before>'use strict';

module.exports.check = function check (obj, type, empty, cb, errMsg) {
  if (obj == null || obj === empty || typeof(obj) !== type) {
    if (cb != null)
      return cb(new Error(errMsg));
    else
      throw new Error(errMsg);
  }
};
<commit_msg>lib/helper: small refactor<commit_after>'use strict';

var callError = module.exports.callError = function callError (errMsg, cb) {
  if (cb != null)
    return cb(new Error(errMsg));
  else
    throw new Error(errMsg);
};

module.exports.check = function check (obj, type, empty, errMsg, cb) {
  if (errMsg == null || typeof errMsg !== 'string' || errMsg === '')
    return callError('errMsg needs to be defined', cb);

  if (obj == null || typeof(obj) !== type)
    return callError(errMsg, cb);

  if (empty != null && obj === empty)
    return callError(errMsg, cb);
};

<|endoftext|>