<commit_before>'use strict';

(function() {

  angular.module('ncsaas').directive('responsiveTable', responsiveTable);

  responsiveTable.$inject = ['$timeout', '$interval', '$compile', '$filter', 'ENV', 'features'];

  /*
    Controller should define tableOptions object with list of columns in `columns` field.
    Each column has following format:

    - `title` is required field; it is passed to `translate` filter for i18n;

    - `render` is required field; this is a function which accepts row as
      it's single argument and returns HTML code;

    - `feature` is optional field; it allows to toggle display of
      field according to configuration parameter `toBeFeatures`.
  */

  function responsiveTable($timeout, $interval, $compile, $filter, ENV, features) {
    return {
      restrict: 'E',
      scope: {
        controller: '=tableCtrl',
        isVisible: '='
      },
      template: '

',
      link: function(scope, element) {
        var options = scope.controller.tableOptions;
        var table;

        scope.$watch('controller.tableOptions', function(newTableOptions) {
          if (table) {
            // Table should be initialized once
            return;
          }
          if (newTableOptions && newTableOptions.columns) {
            options = newTableOptions;
            if (options.hiddenColumns) {
              options.columns = options.columns.filter(function(column) {
                return options.hiddenColumns.indexOf(column.id) === -1;
              });
            }
            table = initTable();
            connectRowButtons(table);

            if (!newTableOptions.disableAutoUpdate) {
              connectWatcher(table);
            }
            registerEvents(table);
          }
        });

        // http://www.gyrocode.com/articles/jquery-datatables-column-width-issues-with-bootstrap-tabs
        scope.$watch('isVisible', function(value) {
          if (table) {
            $timeout(function() {
              table.columns.adjust().responsive.recalc();
            })
          }
        });

        function initTable() {
          var buttons = getButtons();
          var columns = getColumns();
          var dom = getDom();

          var tableOptions = {
            responsive: true,
            processing: true,
            serverSide: true,
            ordering: !!options.enableOrdering,
            autoWidth: false,
            ajax: serverDataTableCallback,
            dom: dom,
            buttons: buttons && buttons,
            columns: columns,
            language: {
              emptyTable: $filter('translate')(options.noDataText),
              zeroRecords: $filter('translate')(options.noMatchesText)
            },
            fnDrawCallback: function() {
              $(element).find('tr').each(function(index, element) {
                $compile(element)(scope);
              });
            }
          };

          if (options.scrollY) {
            tableOptions.scrollY = options.scrollY;
          }

          if (options.scrollCollapse) {
            tableOptions.scrollCollapse = options.scrollCollapse;
          }

          return $(element.find('.table')[0]).DataTable(tableOptions);
        }

        function getDom() {
          if (options.disableButtons) {
            return 'Tgitp';
          } else if (options.disableSearch) {
            return '<"html5buttons"B>rgitp'
          } else {
            return '<"html5buttons"B>lTfgitp';
          }
        }

        function getButtons() {
          if (options.disableButtons) {
            return;
          }

          var exportButtons = getExportButtons(
            options.columns.length,
            options.rowActions,
            ['copyHtml5', 'csvHtml5', 'excelHtml5', 'pdfHtml5', 'print']
          );
          var exportCollection = {
            extend: 'collection',
            text: ' Export as ',
            autoClose: true,
            fade: 0,
            buttons: exportButtons
          };
          var buttons = [exportCollection];
          if (options.tableActions) {
            var tableButtons = getTableButtons(options.tableActions);
            buttons = buttons.concat(tableButtons);
          }
          buttons.push({
            text: ' Refresh',
            action: function() {
              $timeout(function() {
                scope.controller.resetCache();
              });
            }
          });
          return buttons;
        }

        function getColumns() {
          var columns = options.columns.map(function(column) {
            var title = $filter('translate')(title);
            function render(data, type, row, meta) {
              return column.render(row);
            };
            return angular.extend({}, column, {render: render, title: title});
          });
          columns = columns.filter(function(column) {
            return !column.feature || features.isVisible(column.feature);
          });
          if (options.rowActions && options.rowActions.length) {
            var actionColumn = getActionColumn(options.rowActions, options.actionsColumnWidth);
            columns.push(actionColumn);
          }
          return columns;
        }

        function connectWatcher(table) {
          scope.$watchCollection('controller.list', function() {
            table.draw(false);
          });

          var timer = $interval(
            scope.controller.resetCache.bind(scope.controller),
            ENV.countersTimerInterval * 1000
          );
          scope.$on('$destroy', function() {
            $interval.cancel(timer);
          });
        }

        function getTableButtons(actions) {
          return actions.map(function(action) {
            return {
              text: action.name,
              action: function() {
                $timeout(function() {
                  action.callback();
                });
              },
              className: action.disabled && 'disabled' || '',
              titleAttr: action.titleAttr
            };
          });
        }

        function registerEvents(table) {
          table.on('click', 'tr', function() {
            var tr = $(this).closest('tr');
            var row = table.row(tr);
            var rowShown = false;
            table.rows().every(function(rowIndex){
              if (table.row(rowIndex).child.isShown()) {
                rowShown = true;
              }
            });
            if (rowShown) {
              scope.controller.toggleRefresh(true);
            }
            else {
              scope.controller.toggleRefresh(false);
            }
          });
        }

        function connectRowButtons(table) {
          if (!options.rowActions || options.rowActions instanceof Function) {
            return;
          }
          table.on('click', 'button', function(event) {
            $(this).blur();
            var target = $(event.target).closest('button');
            var rowIndex = parseInt(target.attr('row-index'));
            var actionIndex = parseInt(target.attr('action-index'));
            var action = options.rowActions[actionIndex];
            var row = scope.controller.list[rowIndex];
            $timeout(function() {
              if (action.isDisabled && action.isDisabled(row)) {
                return;
              }
              action.callback.apply(scope, [row]);
            });
          });
        }

        function getActionColumn(spec, width) {
          return {
            title: 'Actions',
            orderable: false,
            className: 'actions text-center all',
            render: function(data, type, row, meta) {
              if (options.rowActions instanceof Function) {
                return options.rowActions.call(scope.controller, row);
              }
              var template = '<button title="{tooltip}" class="btn btn-default btn-sm {cls}" {disabled} row-index="{row}" action-index="{action}">{name}</button>';
              var buttons = spec.map(function(action, index) {
                var disabled = action.isDisabled && action.isDisabled(row) && 'disabled' || '';
                var tooltip = action.tooltip && action.tooltip(row) || '';
                return template.replace('{disabled}', disabled)
                               .replace('{cls}', action.className)
                               .replace('{row}', meta.row)
                               .replace('{action}', index)
                               .replace('{name}', action.name)
                               .replace('{tooltip}', tooltip);
              }).join('');
              return '
' + buttons + '
';
            },
            width: width
          };
        }

        function getExportButtons(columnsCount, rowActions, formats) {
          var title = document.title + ' - ' + moment().format('YYYY-MM-DD');
          var exportOptions = {};
          if (rowActions) {
            exportOptions.columns = range(columnsCount);
          }
          return formats.map(function(format) {
            return {
              extend: format,
              exportOptions: exportOptions,
              title: title
            };
          });
        }

        function range(n) {
          var x = [];
          for (var i = 0; i < n; i++) {
            x.push(i);
          }
          return x;
        }

        function serverDataTableCallback(request, drawCallback, settings) {
          var filter = {};
          request.order.forEach(function(orderItem) {
            var orderField = options.columns[orderItem.column].orderField;
            filter.o = orderItem.dir === 'asc' ? orderField : '-' + orderField;
          });
          scope.controller.requestLoad(request, filter).then(function(list) {
            var total = scope.controller.getTotal();
            drawCallback({
              draw: request.draw,
              recordsTotal: total,
              recordsFiltered: total,
              data: scope.controller.list
            });
          });
        }
      }
    };
  }
})();
<commit_msg>Fix typo.<commit_after>'use strict';

(function() {

  angular.module('ncsaas').directive('responsiveTable', responsiveTable);

  responsiveTable.$inject = ['$timeout', '$interval', '$compile', '$filter', 'ENV', 'features'];

  /*
    Controller should define tableOptions object with list of columns in `columns` field.
    Each column has following format:

    - `title` is required field; it is passed to `translate` filter for i18n;

    - `render` is required field; this is a function which accepts row as
      it's single argument and returns HTML code;

    - `feature` is optional field; it allows to toggle display of
      field according to configuration parameter `toBeFeatures`.
  */

  function responsiveTable($timeout, $interval, $compile, $filter, ENV, features) {
    return {
      restrict: 'E',
      scope: {
        controller: '=tableCtrl',
        isVisible: '='
      },
      template: '

',
      link: function(scope, element) {
        var options = scope.controller.tableOptions;
        var table;

        scope.$watch('controller.tableOptions', function(newTableOptions) {
          if (table) {
            // Table should be initialized once
            return;
          }
          if (newTableOptions && newTableOptions.columns) {
            options = newTableOptions;
            if (options.hiddenColumns) {
              options.columns = options.columns.filter(function(column) {
                return options.hiddenColumns.indexOf(column.id) === -1;
              });
            }
            table = initTable();
            connectRowButtons(table);

            if (!newTableOptions.disableAutoUpdate) {
              connectWatcher(table);
            }
            registerEvents(table);
          }
        });

        // http://www.gyrocode.com/articles/jquery-datatables-column-width-issues-with-bootstrap-tabs
        scope.$watch('isVisible', function(value) {
          if (table) {
            $timeout(function() {
              table.columns.adjust().responsive.recalc();
            })
          }
        });

        function initTable() {
          var buttons = getButtons();
          var columns = getColumns();
          var dom = getDom();

          var tableOptions = {
            responsive: true,
            processing: true,
            serverSide: true,
            ordering: !!options.enableOrdering,
            autoWidth: false,
            ajax: serverDataTableCallback,
            dom: dom,
            buttons: buttons && buttons,
            columns: columns,
            language: {
              emptyTable: $filter('translate')(options.noDataText),
              zeroRecords: $filter('translate')(options.noMatchesText)
            },
            fnDrawCallback: function() {
              $(element).find('tr').each(function(index, element) {
                $compile(element)(scope);
              });
            }
          };

          if (options.scrollY) {
            tableOptions.scrollY = options.scrollY;
          }

          if (options.scrollCollapse) {
            tableOptions.scrollCollapse = options.scrollCollapse;
          }

          return $(element.find('.table')[0]).DataTable(tableOptions);
        }

        function getDom() {
          if (options.disableButtons) {
            return 'Tgitp';
          } else if (options.disableSearch) {
            return '<"html5buttons"B>rgitp'
          } else {
            return '<"html5buttons"B>lTfgitp';
          }
        }

        function getButtons() {
          if (options.disableButtons) {
            return;
          }

          var exportButtons = getExportButtons(
            options.columns.length,
            options.rowActions,
            ['copyHtml5', 'csvHtml5', 'excelHtml5', 'pdfHtml5', 'print']
          );
          var exportCollection = {
            extend: 'collection',
            text: ' Export as ',
            autoClose: true,
            fade: 0,
            buttons: exportButtons
          };
          var buttons = [exportCollection];
          if (options.tableActions) {
            var tableButtons = getTableButtons(options.tableActions);
            buttons = buttons.concat(tableButtons);
          }
          buttons.push({
            text: ' Refresh',
            action: function() {
              $timeout(function() {
                scope.controller.resetCache();
              });
            }
          });
          return buttons;
        }

        function getColumns() {
          var columns = options.columns.map(function(column) {
            var title = $filter('translate')(column.title);
            function render(data, type, row, meta) {
              return column.render(row);
            };
            return angular.extend({}, column, {render: render, title: title});
          });
          columns = columns.filter(function(column) {
            return !column.feature || features.isVisible(column.feature);
          });
          if (options.rowActions && options.rowActions.length) {
            var actionColumn = getActionColumn(options.rowActions, options.actionsColumnWidth);
            columns.push(actionColumn);
          }
          return columns;
        }

        function connectWatcher(table) {
          scope.$watchCollection('controller.list', function() {
            table.draw(false);
          });

          var timer = $interval(
            scope.controller.resetCache.bind(scope.controller),
            ENV.countersTimerInterval * 1000
          );
          scope.$on('$destroy', function() {
            $interval.cancel(timer);
          });
        }

        function getTableButtons(actions) {
          return actions.map(function(action) {
            return {
              text: action.name,
              action: function() {
                $timeout(function() {
                  action.callback();
                });
              },
              className: action.disabled && 'disabled' || '',
              titleAttr: action.titleAttr
            };
          });
        }

        function registerEvents(table) {
          table.on('click', 'tr', function() {
            var tr = $(this).closest('tr');
            var row = table.row(tr);
            var rowShown = false;
            table.rows().every(function(rowIndex){
              if (table.row(rowIndex).child.isShown()) {
                rowShown = true;
              }
            });
            if (rowShown) {
              scope.controller.toggleRefresh(true);
            }
            else {
              scope.controller.toggleRefresh(false);
            }
          });
        }

        function connectRowButtons(table) {
          if (!options.rowActions || options.rowActions instanceof Function) {
            return;
          }
          table.on('click', 'button', function(event) {
            $(this).blur();
            var target = $(event.target).closest('button');
            var rowIndex = parseInt(target.attr('row-index'));
            var actionIndex = parseInt(target.attr('action-index'));
            var action = options.rowActions[actionIndex];
            var row = scope.controller.list[rowIndex];
            $timeout(function() {
              if (action.isDisabled && action.isDisabled(row)) {
                return;
              }
              action.callback.apply(scope, [row]);
            });
          });
        }

        function getActionColumn(spec, width) {
          return {
            title: 'Actions',
            orderable: false,
            className: 'actions text-center all',
            render: function(data, type, row, meta) {
              if (options.rowActions instanceof Function) {
                return options.rowActions.call(scope.controller, row);
              }
              var template = '<button title="{tooltip}" class="btn btn-default btn-sm {cls}" {disabled} row-index="{row}" action-index="{action}">{name}</button>';
              var buttons = spec.map(function(action, index) {
                var disabled = action.isDisabled && action.isDisabled(row) && 'disabled' || '';
                var tooltip = action.tooltip && action.tooltip(row) || '';
                return template.replace('{disabled}', disabled)
                               .replace('{cls}', action.className)
                               .replace('{row}', meta.row)
                               .replace('{action}', index)
                               .replace('{name}', action.name)
                               .replace('{tooltip}', tooltip);
              }).join('');
              return '
' + buttons + '
';
            },
            width: width
          };
        }

        function getExportButtons(columnsCount, rowActions, formats) {
          var title = document.title + ' - ' + moment().format('YYYY-MM-DD');
          var exportOptions = {};
          if (rowActions) {
            exportOptions.columns = range(columnsCount);
          }
          return formats.map(function(format) {
            return {
              extend: format,
              exportOptions: exportOptions,
              title: title
            };
          });
        }

        function range(n) {
          var x = [];
          for (var i = 0; i < n; i++) {
            x.push(i);
          }
          return x;
        }

        function serverDataTableCallback(request, drawCallback, settings) {
          var filter = {};
          request.order.forEach(function(orderItem) {
            var orderField = options.columns[orderItem.column].orderField;
            filter.o = orderItem.dir === 'asc' ? orderField : '-' + orderField;
          });
          scope.controller.requestLoad(request, filter).then(function(list) {
            var total = scope.controller.getTotal();
            drawCallback({
              draw: request.draw,
              recordsTotal: total,
              recordsFiltered: total,
              data: scope.controller.list
            });
          });
        }
      }
    };
  }
})();
<|endoftext|>


<commit_before>//  popup for ddsc including graph
//
//
//todo: move these classes to lizard.views
// Modal view that opens when clicking on a location

function format_value(value) {
  if (typeof(value) === 'undefined') {
    return '-';
  } else if (typeof(value) === "number") {
    return value.toFixed(2);
  } else {
    return value;
  }
}

Lizard.Map.LocationModalTimeseries = Backbone.Marionette.Layout.extend({
  template: '#location-modal-timeserie',
  initialize: function(options) {
  },
  events: {
    'click .graph-this': "drawGraph"
  },
  // One Timeserie has many Events. An Events list is only
  // loaded when it is explcitly chosen, with caching.
  drawGraph: function() {
    // Gets the element that is clicked and it's datasets
    var data_url = this.model.attributes.events;
    $('#modal-graph-wrapper').removeClass('hidden');
    $('#modal-graph-wrapper').find('.flot-graph').loadPlotData(data_url + '?eventsformat=flot');
  }
});

Lizard.Views.LocationModalPopupItem = Backbone.Marionette.ItemView.extend({
  initialize: function (options) {
    this.graphModel = options.graphModel;
  },
  template: '#location-modal-popup-item',
  tagName: 'li',
  events: {
    'click .add-graph-item' : "addGraphItem"
  },
  addGraphItem: function () {
    var self = this;
    this.model.fetch()
    .done(function (model) {
        var graphItem = new Lizard.Models.GraphItem({timeseries: model});
        self.graphModel.get('graphItems').add(graphItem);
    });
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationModalPopupList = Backbone.Marionette.CollectionView.extend({
  itemView: Lizard.Views.LocationModalPopupItem,
  tagName: 'ul',
  initialize: function (options) {
    this.graphModel = options.graphModel;
  },
  itemViewOptions: function (model) {
    return {
      graphModel: this.graphModel
    };
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationModalPopup = Backbone.Marionette.Layout.extend({
    template: '#location-modal-popup',
    primaryTimeseries: null,
    otherTimeseries: null,
    initialize: function (options) {
        this.primaryTimeseries = options.primaryTimeseries;
        this.otherTimeseries = options.otherTimeseries;
    },
    regions: {
        timeseriesRegion: '.modal-timeseries-region',
        graphRegion: '.modal-graph-region'
    },
    onRender: function (e) {
        var self = this;
        this.$el.find('.modal').modal();
        var dateRange = new Lizard.Models.DateRange({
          accountModel: account // pass the global account instance
        });
        var graphModel = new Lizard.Models.Graph({
            dateRange: dateRange
        });
        if (this.primaryTimeseries) {
            // refetch because the inline Serializer seems different from the Detail serializer
            this.primaryTimeseries.fetch()
            .done(function (model) {
                self.primaryTimeseries = model;
                var graphItem = new Lizard.Models.GraphItem({timeseries: model});
                graphModel.get('graphItems').add(graphItem);
            });
        }
        var graphView = new Lizard.Views.GraphAndLegendView({model: graphModel});
        this.graphRegion.show(graphView);

        var timeseriesView = new Lizard.Views.LocationModalPopupList({
            collection: this.otherTimeseries,
            graphModel: graphModel
        });
        this.timeseriesRegion.show(timeseriesView);
    }
});

ImageCarouselItemView = Backbone.Marionette.ItemView.extend({
  template: '#image-carousel-itemview',
  className: 'item',
  tagName: 'div',
  onRender: function() {
    var self = this;
    // If this is the first item, add the classname 'active'
    if(self.model.get('first')) {
      self.$el.attr('class', 'active item');

      var img_element = self.$el.find('img').first();

      var src = img_element.data('img-src');
      img_element.attr('src', src);
    }
  }
});
ImageCarouselCollectionView = Backbone.Marionette.CollectionView.extend({
  tagName: 'div',
  className: 'carousel-inner'
});
Lizard.Views.ImageCarouselModal = Backbone.Marionette.Layout.extend({
    template: '#image-carousel-popup',
    regions: {
        graphRegion: '.carousel-graph-region'
    },
    initialize: function (options) {
        this.imageTimeseriesCollection = options.imageTimeseries;
    },
    onRender: function (e) {
      var self = this;
      this.$el.find('.modal').modal();

      self.$el.on('slid', function(e) {
        var next = $(e.target).find('div .active');
        var img = next.find('img');
        img.attr('src', img.data('img-src'));
      });

      var url = self.imageTimeseriesCollection.where({'value_type': 'image'})[0].get('events');
      var eventsCollection = new Lizard.Collections.Events();
      eventsCollection.url = url + '?page_size=0';
      eventsCollection.fetch().done(function (collection, response) {
        console.log(collection);
        console.log(response);
        collection.models[0].set({'first': true}); // Set 'first' attribute on first model b/c Bootstrap Carousel needs to know this
        var carouselView = new ImageCarouselCollectionView({
          collection: collection,
          itemView: ImageCarouselItemView,
          emptyView: Lizard.Views.GraphLegendNoItems
        });
        self.graphRegion.show(carouselView);
      });
    }
});

TextTimeserieItemView = Backbone.Marionette.ItemView.extend({
  template: '#timeserie-text-itemview',
  className: 'item',
  tagName: 'div',
  onRender: function() {
    var self = this;
    // If this is the first item, add the classname 'active'
    if(self.model.get('first')) {
      self.$el.attr('class', 'active item');
    }
  }
});
TextTimeserieCollectionView = Backbone.Marionette.CollectionView.extend({
  tagName: 'div',
  className: 'text-timeserie-collection'
});
Lizard.Views.TextModal = Backbone.Marionette.Layout.extend({
    template: '#textmodal-popup',
    regions: {
        textRegion: '.text-region'
    },
    initialize: function (options) {
        this.textTimeseriesCollection = options.textTimeseries;
    },
    onRender: function (e) {
      var self = this;
      this.$el.find('.modal').modal();

      var url = self.textTimeseriesCollection.where({'value_type': 'text'})[0].get('events');
      var eventsCollection = new Lizard.Collections.Events();
      eventsCollection.url = url + '?page_size=0';
      eventsCollection.fetch().done(function (collection, response) {
        collection.models[0].set({'first': true}); // Set 'first' attribute on first model b/c Bootstrap needs to know this
        var textTimeserieCollectionView = new TextTimeserieCollectionView({
          collection: collection,
          itemView: TextTimeserieItemView,
          emptyView: Lizard.Views.GraphLegendNoItems
        });
        self.textRegion.show(textTimeserieCollectionView);
      });
    }
});

Lizard.Views.LocationPopupItem = Backbone.Marionette.ItemView.extend({
  template: '#location-popup-item',
  tagName: 'li',
  events: {
    'click .popup-toggle' : 'openModal',
    'click .image-popup-toggle' : 'openCarouselModal',
    'click .image-popup-text': 'openTextModal',
    'click .icon-comment' : 'openAnnotation'
  },
  openAnnotation: function(){
    Lizard.App.vent.trigger('makeAnnotation', this.model);
  },
  openModal: function(e) {
    var modalView = new Lizard.Views.LocationModalPopup({
        primaryTimeseries: this.model,
        otherTimeseries: this.model.collection
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  },
  openCarouselModal: function(e) {
    var modalView = new Lizard.Views.ImageCarouselModal({
      imageTimeseries: this.model.collection
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  },
  openTextModal: function(e) {
    var modalView = new Lizard.Views.TextModal({
      textTimeseries: this.model.collection
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationPopup = Backbone.Marionette.CollectionView.extend({
  itemView: Lizard.Views.LocationPopupItem,
  tagName: 'ul'
});

Lizard.geo.Popups.DdscTimeseries = {
  getPopupContent: function (location, region) {
    var url = settings.timeseries_url + '&location=' + location.get('uuid');
    var tsCollection = new Lizard.Collections.Timeseries();
    tsCollection.url = url;
    tsCollection.fetch().done(function (collection, response) {
      // console.log(collection.models[0].attributes);
        var popupView = new Lizard.Views.LocationPopup({
            collection: collection
        });

        var popupContent = popupView.render();

        region.show(popupContent);
    });
  }
};
<commit_msg>timeseries are now retrieved on basis of model not on the first in the collection<commit_after>//  popup for ddsc including graph
//
//
//todo: move these classes to lizard.views
// Modal view that opens when clicking on a location

function format_value(value) {
  if (typeof(value) === 'undefined') {
    return '-';
  } else if (typeof(value) === "number") {
    return value.toFixed(2);
  } else {
    return value;
  }
}

Lizard.Map.LocationModalTimeseries = Backbone.Marionette.Layout.extend({
  template: '#location-modal-timeserie',
  initialize: function(options) {
  },
  events: {
    'click .graph-this': "drawGraph"
  },
  // One Timeserie has many Events. An Events list is only
  // loaded when it is explcitly chosen, with caching.
  drawGraph: function() {
    // Gets the element that is clicked and it's datasets
    var data_url = this.model.attributes.events;
    $('#modal-graph-wrapper').removeClass('hidden');
    $('#modal-graph-wrapper').find('.flot-graph').loadPlotData(data_url + '?eventsformat=flot');
  }
});

Lizard.Views.LocationModalPopupItem = Backbone.Marionette.ItemView.extend({
  initialize: function (options) {
    this.graphModel = options.graphModel;
  },
  template: '#location-modal-popup-item',
  tagName: 'li',
  events: {
    'click .add-graph-item' : "addGraphItem"
  },
  addGraphItem: function () {
    var self = this;
    this.model.fetch()
    .done(function (model) {
        var graphItem = new Lizard.Models.GraphItem({timeseries: model});
        self.graphModel.get('graphItems').add(graphItem);
    });
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationModalPopupList = Backbone.Marionette.CollectionView.extend({
  itemView: Lizard.Views.LocationModalPopupItem,
  tagName: 'ul',
  initialize: function (options) {
    this.graphModel = options.graphModel;
  },
  itemViewOptions: function (model) {
    return {
      graphModel: this.graphModel
    };
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationModalPopup = Backbone.Marionette.Layout.extend({
    template: '#location-modal-popup',
    primaryTimeseries: null,
    otherTimeseries: null,
    initialize: function (options) {
        this.primaryTimeseries = options.primaryTimeseries;
        this.otherTimeseries = options.otherTimeseries;
    },
    regions: {
        timeseriesRegion: '.modal-timeseries-region',
        graphRegion: '.modal-graph-region'
    },
    onRender: function (e) {
        var self = this;
        this.$el.find('.modal').modal();
        var dateRange = new Lizard.Models.DateRange({
          accountModel: account // pass the global account instance
        });
        var graphModel = new Lizard.Models.Graph({
            dateRange: dateRange
        });
        if (this.primaryTimeseries) {
            // refetch because the inline Serializer seems different from the Detail serializer
            this.primaryTimeseries.fetch()
            .done(function (model) {
                self.primaryTimeseries = model;
                var graphItem = new Lizard.Models.GraphItem({timeseries: model});
                graphModel.get('graphItems').add(graphItem);
            });
        }
        var graphView = new Lizard.Views.GraphAndLegendView({model: graphModel});
        this.graphRegion.show(graphView);

        var timeseriesView = new Lizard.Views.LocationModalPopupList({
            collection: this.otherTimeseries,
            graphModel: graphModel
        });
        this.timeseriesRegion.show(timeseriesView);
    }
});

ImageCarouselItemView = Backbone.Marionette.ItemView.extend({
  template: '#image-carousel-itemview',
  className: 'item',
  tagName: 'div',
  onRender: function() {
    var self = this;
    // If this is the first item, add the classname 'active'
    if(self.model.get('first')) {
      self.$el.attr('class', 'active item');

      var img_element = self.$el.find('img').first();

      var src = img_element.data('img-src');
      img_element.attr('src', src);
    }
  }
});
ImageCarouselCollectionView = Backbone.Marionette.CollectionView.extend({
  tagName: 'div',
  className: 'carousel-inner'
});
Lizard.Views.ImageCarouselModal = Backbone.Marionette.Layout.extend({
    template: '#image-carousel-popup',
    regions: {
        graphRegion: '.carousel-graph-region'
    },
    initialize: function (options) {
        this.imageTimeseries = options.imageTimeseries;
    },
    onRender: function (e) {
      var self = this;
      this.$el.find('.modal').modal();

      self.$el.on('slid', function(e) {
        var next = $(e.target).find('div .active');
        var img = next.find('img');
        img.attr('src', img.data('img-src'));
      });

      var url = self.imageTimeseries.get('events');
      var eventsCollection = new Lizard.Collections.Events();
      eventsCollection.url = url + '?page_size=0';
      eventsCollection.fetch().done(function (collection, response) {
        console.log(collection);
        console.log(response);
        collection.models[0].set({'first': true}); // Set 'first' attribute on first model b/c Bootstrap Carousel needs to know this
        var carouselView = new ImageCarouselCollectionView({
          collection: collection,
          itemView: ImageCarouselItemView,
          emptyView: Lizard.Views.GraphLegendNoItems
        });
        self.graphRegion.show(carouselView);
      });
    }
});

TextTimeserieItemView = Backbone.Marionette.ItemView.extend({
  template: '#timeserie-text-itemview',
  className: 'item',
  tagName: 'div',
  onRender: function() {
    var self = this;
    // If this is the first item, add the classname 'active'
    if(self.model.get('first')) {
      self.$el.attr('class', 'active item');
    }
  }
});
TextTimeserieCollectionView = Backbone.Marionette.CollectionView.extend({
  tagName: 'div',
  className: 'text-timeserie-collection'
});
Lizard.Views.TextModal = Backbone.Marionette.Layout.extend({
    template: '#textmodal-popup',
    regions: {
        textRegion: '.text-region'
    },
    initialize: function (options) {
        this.textTimeseriesCollection = options.textTimeseries;
    },
    onRender: function (e) {
      var self = this;
      this.$el.find('.modal').modal();

      var url = self.textTimeseriesCollection.where({'value_type': 'text'})[0].get('events');
      var eventsCollection = new Lizard.Collections.Events();
      eventsCollection.url = url + '?page_size=0';
      eventsCollection.fetch().done(function (collection, response) {
        collection.models[0].set({'first': true}); // Set 'first' attribute on first model b/c Bootstrap needs to know this
        var textTimeserieCollectionView = new TextTimeserieCollectionView({
          collection: collection,
          itemView: TextTimeserieItemView,
          emptyView: Lizard.Views.GraphLegendNoItems
        });
        self.textRegion.show(textTimeserieCollectionView);
      });
    }
});

Lizard.Views.LocationPopupItem = Backbone.Marionette.ItemView.extend({
  template: '#location-popup-item',
  tagName: 'li',
  events: {
    'click .popup-toggle' : 'openModal',
    'click .image-popup-toggle' : 'openCarouselModal',
    'click .image-popup-text': 'openTextModal',
    'click .icon-comment' : 'openAnnotation'
  },
  openAnnotation: function(){
    Lizard.App.vent.trigger('makeAnnotation', this.model);
  },
  openModal: function(e) {
    var modalView = new Lizard.Views.LocationModalPopup({
        primaryTimeseries: this.model,
        otherTimeseries: this.model.collection
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  },
  openCarouselModal: function(e) {
    var modalView = new Lizard.Views.ImageCarouselModal({
      imageTimeseries: this.model
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  },
  openTextModal: function(e) {
    var modalView = new Lizard.Views.TextModal({
      textTimeseries: this.model.collection
    });
    Lizard.App.hidden.show(modalView);
    modalView.$el.find('.modal').on('hide', function () {
        Lizard.App.hidden.close();
    });
  }
});

// Modal view that opens when clicking on a location
Lizard.Views.LocationPopup = Backbone.Marionette.CollectionView.extend({
  itemView: Lizard.Views.LocationPopupItem,
  tagName: 'ul'
});

Lizard.geo.Popups.DdscTimeseries = {
  getPopupContent: function (location, region) {
    var url = settings.timeseries_url + '&location=' + location.get('uuid');
    var tsCollection = new Lizard.Collections.Timeseries();
    tsCollection.url = url;
    tsCollection.fetch().done(function (collection, response) {
      // console.log(collection.models[0].attributes);
        var popupView = new Lizard.Views.LocationPopup({
            collection: collection
        });

        var popupContent = popupView.render();

        region.show(popupContent);
    });
  }
};
<|endoftext|>


<commit_before>'use strict';

var _       = require('underscore');
var angular = require('angular');

var config = {
	common: {

		// TODO Do we need to have configurable states?
		// states for the ui-router
		states: {
			main:     'main.workspace',
			board:    'main.board',
			login:    'login',
			register: 'register'
		},

		// keys used by localStorage to hold information
		// about the users identity
		userKey:  'tbuser',
		tokenKey: 'tbtoken'
	},
	development: {

		// socket.io server host and port
		io: {
			_url: 'http://' + (process.env.HOSTNAME || 'localhost'),
			_port: 9001,
		},

		// restful api url and port
		api: {
			_url:     'http://' + (process.env.HOSTNAME || 'localhost'),
			_port:    9002,
			_version: 'api/v1'
		},

		// static content provider url and port
		// serves things like board images
		static: {
			_url: 'http://' + (process.env.HOSTNAME || 'localhost'),
			_port: 9002
		}
	},
	production: {
		io: {
			_url:  process.env.IO_URL,
			_port: process.env.IO_PORT
		},
		api: {
			_url:     process.env.API_URL,
			_port:    process.env.API_PORT,
			_version: 'api/v1'
		},
		static: {
			_url:  process.env.STATIC_URL,
			_port: process.env.STATIC_PORT
		}
	}
}

config = _.extend(config.common,
	config[process.env.NODE_ENV] || config.development);

config.io.url = function() {
	return this._url + ':' + this._port;
}

config.api.url = function() {
	return this._url + ':' + this._port + '/';
}

config.static.url = function() {
	return this._url + ':' + this._port;
}

module.exports = angular.module('tb.configuration', [ ])
	.value('Config', config);
<commit_msg>Use process.env.API_URL for the whole api url, including any versions and ports.<commit_after>'use strict';

var _       = require('underscore');
var angular = require('angular');

var config = {
	common: {

		// TODO Do we need to have configurable states?
		// states for the ui-router
		states: {
			main:     'main.workspace',
			board:    'main.board',
			login:    'login',
			register: 'register'
		},

		// keys used by localStorage to hold information
		// about the users identity
		userKey:  'tbuser',
		tokenKey: 'tbtoken'
	},
	development: {

		// socket.io server host and port
		io: {
			_url: 'http://' + (process.env.HOSTNAME || 'localhost'),
			_port: 9001,
		},

		// restful api url and port
		api: {
			_url:     'http://' + (process.env.HOSTNAME || 'localhost'),
			_port:    9002,
			_version: 'api/v1'
		},

		// static content provider url and port
		// serves things like board images
		static: {
			_url: 'http://' + (process.env.HOSTNAME || 'localhost'),
			_port: 9002
		}
	},
	production: {
		io: {
			_url:  process.env.IO_URL,
			_port: process.env.IO_PORT
		},
		api: {
			_url:     process.env.API_URL,
			_port:    process.env.API_PORT,
			_version: 'api/v1'
		},
		static: {
			_url:  process.env.STATIC_URL,
			_port: process.env.STATIC_PORT
		}
	}
}

config = _.extend(config.common,
	config[process.env.NODE_ENV] || config.development);

config.io.url = function() {
	return this._url + ':' + this._port;
}

config.api.url = function() {
	return this._url;
}

config.static.url = function() {
	return this._url + ':' + this._port;
}

module.exports = angular.module('tb.configuration', [ ])
	.value('Config', config);
<|endoftext|>


<commit_before>/**

  Experimental shader giving perspective effect on image with depthmap.

  Quality is controlled by defined profiles 1, 2 and 3.

  ---------------
  The MIT License

  Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

 */
'use strict';
PIXI.DepthPerspectiveFilter = function(texture, quality)
{
  PIXI.AbstractFilter.call( this );

  this.passes = [this];

  // set the uniforms
  this.uniforms = {
    displacementMap: {type: 'sampler2D', value:texture},
    scale:           {type: '1f', value:0.015},
    offset:          {type: '2f', value:{x:0, y:0}},
    mapDimensions:   {type: '2f', value:{x:1, y:5112}},
    dimensions:      {type: '4fv', value:[0,0,0,0]},
    focus:           {type: '1f', value:0.5}
  };

  if(texture.baseTexture.hasLoaded)
  {
    this.uniforms.mapDimensions.value.x = texture.width;
    this.uniforms.mapDimensions.value.y = texture.height;
  }
  else
  {
    this.boundLoadedFunction = this.onTextureLoaded.bind(this);

    texture.baseTexture.on('loaded', this.boundLoadedFunction);
  }

  this.fragmentSrc = [

'// Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy',
'precision mediump float;',
'',
'varying vec2 vTextureCoord;',
'varying vec4 vColor;',
'uniform sampler2D displacementMap;',
'uniform sampler2D uSampler;',
'uniform vec4 dimensions;',
'uniform vec2 mapDimensions;',
'uniform float scale;',
'uniform vec2 offset;',
'uniform float focus;',
'',
'#if !defined(QUALITY)',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define ENLARGE 1.5',
'  #define ANTIALIAS 1',
'  #define AA_TRIGGER 0.8',
'  #define AA_POWER 1.0',
'  #define AA_MAXITER 8.0',
'  #define MAXSTEPS 16.0',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 2',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 4.0',
'  #define ENLARGE 0.8',
'//   #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 3',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 6.0',
'  #define ENLARGE 1.0',
'  #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 4',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 16.0',
'  #define ENLARGE 1.5',
'  #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 5',
'',
'  #define METHOD 1',
'  #define CORRECT',
'  #define COLORAVG',
'  #define MAXSTEPS 40.0',
'  #define ENLARGE 1.5',
'//     #define ANTIALIAS 2',
'  #define AA_TRIGGER 0.8',
'  #define AA_POWER 1.0',
'  #define AA_MAXITER 8.0',
'  #define CONFIDENCE_MAX 4.5',
'',
'#endif',
'',
'',
'#define BRANCHLOOP  ',
'#define BRANCHSAMPLE ',
'#define DEBUG 0',
'// #define DEBUGBREAK 2',
'',
'#ifndef METHOD',
'  #define METHOD 1',
'#endif',
'#ifndef MAXSTEPS',
'  #define MAXSTEPS 8.0',
'#endif',
'#ifndef ENLARGE',
'  #define ENLARGE 1.2',
'#endif',
'#ifndef PERSPECTIVE',
'  #define PERSPECTIVE 0.0',
'#endif',
'#ifndef UPSCALE',
'  #define UPSCALE 1.06',
'#endif',
'#ifndef CONFIDENCE_MAX',
'  #define CONFIDENCE_MAX 0.2',
'#endif',
'#ifndef COMPRESSION',
'  #define COMPRESSION 0.8',
'#endif',
'',
'const float perspective = PERSPECTIVE;',
'const float upscale = UPSCALE;',
'// float steps = clamp( ceil( max(abs(offset.x), abs(offset.y)) * maxSteps ), 1.0, maxSteps);',
'float steps = MAXSTEPS;',
'',
'#ifdef COLORAVG',
'float maskPower = steps * 2.0;// 32.0;',
'#else ',
'float maskPower = steps * 1.0;// 32.0;',
'#endif',
'float correctPower = 1.0;//max(1.0, steps / 8.0);',
'',
'const float compression = COMPRESSION;',
'const float dmin = (1.0 - compression) / 2.0;',
'const float dmax = (1.0 + compression) / 2.0;',
'',
'const float vectorCutoff = 0.0 + dmin - 0.0001;',
'',
'float aspect = dimensions.x / dimensions.y;',
'vec2 scale2 = vec2(scale * min(1.0, 1.0 / aspect), scale * min(1.0, aspect)) * vec2(1, -1) * vec2(ENLARGE);',
'// mat2 baseVector = mat2(vec2(-focus * offset) * scale2, vec2(offset - focus * offset) * scale2);',
'mat2 baseVector = mat2(vec2((0.5 - focus) * offset - offset/2.0) * scale2, ',
'                       vec2((0.5 - focus) * offset + offset/2.0) * scale2);',
'',
'',
'void main(void) {',
'',
'  vec2 pos = (vTextureCoord - vec2(0.5)) / vec2(upscale) + vec2(0.5);',
'  mat2 vector = baseVector;',
'  // perspective shift',
'  vector[1] += (vec2(2.0) * pos - vec2(1.0)) * vec2(perspective);',
'  ',
'  float dstep = compression / (steps - 1.0);',
'  vec2 vstep = (vector[1] - vector[0]) / vec2((steps - 1.0)) ;',
'  ',
'  #ifdef COLORAVG',
'    vec4 colSum = vec4(0.0);',
'  #else',
'    vec2 posSum = vec2(0.0);',
'  #endif',
'',
'  float confidenceSum = 0.0;',
'  float minConfidence = dstep / 2.0;',
'    ',
'  #ifdef ANTIALIAS',
'    #ifndef AA_TRIGGER',
'      #define AA_TRIGGER 0.8',
'    #endif',
'    #if ANTIALIAS == 11 || ANTIALIAS == 12',
'      #ifndef AA_POWER',
'        #define AA_POWER 0.5',
'      #endif',
'      #ifndef AA_MAXITER',
'        #define AA_MAXITER 16.0',
'      #endif',
'      float loopStep = 1.0;',
'    #endif',
'    ',
'    #define LOOP_INDEX j',
'    float j = 0.0;',
'  #endif',
'',
'  #ifndef LOOP_INDEX',
'    #define LOOP_INDEX i',
'  #endif',
'',
'',
'  for(float i = 0.0; i < MAXSTEPS; ++i) {',
'    vec2 vpos = pos + vector[1] - LOOP_INDEX * vstep;',
'    float dpos = 0.5 + compression / 2.0 - LOOP_INDEX * dstep;',
'    #ifdef BRANCHLOOP',
'    if (dpos >= vectorCutoff && confidenceSum < CONFIDENCE_MAX) {',
'    #endif',
'      float depth = 1.0 - texture2D(displacementMap, vpos * vec2(1, -1) + vec2(0, 1)).r;',
'      depth = clamp(depth, dmin, dmax);',
'      float confidence;',
'',
'      #if METHOD == 1',
'        confidence = step(dpos, depth + 0.001);',
'',
'      #elif METHOD == 2',
'        confidence = 1.0 - abs(dpos - depth);',
'        if (confidence < 1.0 - minConfidence * 2.0) confidence = 0.0;',
'',
'      #elif METHOD == 5',
'        confidence = 1.0 - abs(dpos - depth);',
'        confidence = pow(confidence, maskPower);',
'',
'      #endif',
'',
'      #ifndef BRANCHLOOP',
'       confidence *= step(vectorCutoff, dpos);',
'       confidence *= step(confidenceSum, CONFIDENCE_MAX);',
'      #endif',
'        ',
'      #ifndef ANTIALIAS',
'      #elif ANTIALIAS == 1 // go back halfstep, go forward fullstep - branched',
'        if (confidence > AA_TRIGGER && i == j) {',
'          j -= 0.5;',
'        } else {',
'          j += 1.0;',
'        }',
'        // confidence *= CONFIDENCE_MAX / 3.0;',
'',
'      #elif ANTIALIAS == 2 // go back halfstep, go forward fullstep - mult',
'        j += 1.0 + step(AA_TRIGGER, confidence) ',
'             * step(i, j) * -1.5; ',
'        // confidence *= CONFIDENCE_MAX / 3.0;',
'',
'      #elif ANTIALIAS == 11',
'        if (confidence >= AA_TRIGGER && i == j && steps - i > 1.0) {',
'          loopStep = AA_POWER * 2.0 / min(AA_MAXITER, steps - i - 1.0);',
'          j -= AA_POWER + loopStep;',
'        }',
'        confidence *= loopStep;',
'        j += loopStep;',
'      #elif ANTIALIAS == 12',
'        float _if_aa = step(AA_TRIGGER, confidence)',
'                     * step(i, j)',
'                     * step(1.5, steps - i);',
'        loopStep = _if_aa * (AA_POWER * 2.0 / min(AA_MAXITER, max(0.1, steps - i - 1.0)) - 1.0) + 1.0;',
'        confidence *= loopStep;',
'        j += _if_aa * -(AA_POWER + loopStep) + loopStep;',
'      #endif',
'',
'        ',
'      #ifdef BRANCHSAMPLE',
'      if (confidence > 0.0) {',
'      #endif',
'        ',
'        #ifdef CORRECT',
'          #define CORRECTION_MATH +( ( vec2((depth - dpos) / (dstep * correctPower)) * vstep ))',
'        #else',
'          #define CORRECTION_MATH',
'        #endif',
'          ',
'        #ifdef COLORAVG    ',
'          colSum += texture2D(uSampler, vpos CORRECTION_MATH) * confidence;',
'        #else',
'          posSum += (vpos CORRECTION_MATH) * confidence;    ',
'        #endif',
'          confidenceSum += confidence;',
'          ',
'      #ifdef BRANCHSAMPLE',
'      }',
'      #endif',
'',
'        ',
'      #if DEBUG > 2',
'        gl_FragColor = vec4(vector[0] / 2.0 + 1.0, vector[1].xy / 2.0 + 1.0);',
'      #elif DEBUG > 1',
'        gl_FragColor = vec4(confidenceSum, depth, dpos, 0);',
'      #elif DEBUG > 0',
'        gl_FragColor = vec4(confidence, depth, dpos, 0);',
'      #endif',
'      #ifdef DEBUGBREAK ',
'      if (i == float(DEBUGBREAK)) {',
'          dpos = 0.0;',
'      }     ',
'      #endif',
'',
'    #ifdef BRANCHLOOP',
'    }',
'    #endif',
'  };',
'',
'  #if defined(COLORAVG) && DEBUG == 0',
'    gl_FragColor = colSum / vec4(confidenceSum);',
'  #elif !defined(COLORAVG) && DEBUG == 0',
'    gl_FragColor = texture2D(uSampler, posSum / confidenceSum);',
'  #endif',
'',
'}',

  ];

  this.quality = quality;
  if (quality) {
    this.fragmentSrc.unshift('#define QUALITY ' + quality);
  }
};

PIXI.DepthPerspectiveFilter.prototype = Object.create( PIXI.AbstractFilter.prototype );
PIXI.DepthPerspectiveFilter.prototype.constructor = PIXI.DepthPerspectiveFilter;

PIXI.DepthPerspectiveFilter.prototype.onTextureLoaded = function()
{
  this.uniforms.mapDimensions.value.x = this.uniforms.displacementMap.value.width;
  this.uniforms.mapDimensions.value.y = this.uniforms.displacementMap.value.height;

  this.uniforms.displacementMap.value.baseTexture.off('loaded', this.boundLoadedFunction);
};

/**
 * The texture used for the displacemtent map * must be power of 2 texture at the moment
 *
 * @property map
 * @type Texture
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'map', {
  get: function() {
    return this.uniforms.displacementMap.value;
  },
  set: function(value) {
    this.uniforms.displacementMap.value = value;
  }
});

/**
 * The multiplier used to scale the displacement result from the map calculation.
 *
 * @property scale
 * @type Point
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'scale', {
  get: function() {
    return this.uniforms.scale.value;
  },
  set: function(value) {
    this.uniforms.scale.value = value;
  }
});

/**
 * Focus point in paralax
 *
 * @property focus
 * @type float
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'focus', {
  get: function() {
    return this.uniforms.focus.value;
  },
  set: function(value) {
    this.uniforms.focus.value = Math.min(1,Math.max(0,value));
  }
});

/**
 * The offset used to move the displacement map.
 *
 * @property offset
 * @type Point
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'offset', {
  get: function() {
    return this.uniforms.offset.value;
  },
  set: function(value) {
    this.uniforms.offset.value = value;
  }
});<commit_msg>debugbreak<commit_after>/**

  Experimental shader giving perspective effect on image with depthmap.

  Quality is controlled by defined profiles 1, 2 and 3.

  ---------------
  The MIT License

  Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.

 */
'use strict';
PIXI.DepthPerspectiveFilter = function(texture, quality)
{
  PIXI.AbstractFilter.call( this );

  this.passes = [this];

  // set the uniforms
  this.uniforms = {
    displacementMap: {type: 'sampler2D', value:texture},
    scale:           {type: '1f', value:0.015},
    offset:          {type: '2f', value:{x:0, y:0}},
    mapDimensions:   {type: '2f', value:{x:1, y:5112}},
    dimensions:      {type: '4fv', value:[0,0,0,0]},
    focus:           {type: '1f', value:0.5}
  };

  if(texture.baseTexture.hasLoaded)
  {
    this.uniforms.mapDimensions.value.x = texture.width;
    this.uniforms.mapDimensions.value.y = texture.height;
  }
  else
  {
    this.boundLoadedFunction = this.onTextureLoaded.bind(this);

    texture.baseTexture.on('loaded', this.boundLoadedFunction);
  }

  this.fragmentSrc = [

'// Copyright (c) 2014 Rafał Lindemann. http://panrafal.github.com/depthy',
'precision mediump float;',
'',
'varying vec2 vTextureCoord;',
'varying vec4 vColor;',
'uniform sampler2D displacementMap;',
'uniform sampler2D uSampler;',
'uniform vec4 dimensions;',
'uniform vec2 mapDimensions;',
'uniform float scale;',
'uniform vec2 offset;',
'uniform float focus;',
'',
'#if !defined(QUALITY)',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define ENLARGE 1.5',
'  #define ANTIALIAS 1',
'  #define AA_TRIGGER 0.8',
'  #define AA_POWER 1.0',
'  #define AA_MAXITER 8.0',
'  #define MAXSTEPS 16.0',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 2',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 4.0',
'  #define ENLARGE 0.8',
'//   #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 3',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 6.0',
'  #define ENLARGE 1.0',
'  #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 4',
'',
'  #define METHOD 1',
'  #define CORRECT',
'//     #define COLORAVG',
'  #define MAXSTEPS 16.0',
'  #define ENLARGE 1.5',
'  #define ANTIALIAS 2',
'  #define CONFIDENCE_MAX 2.5',
'',
'#elif QUALITY == 5',
'',
'  #define METHOD 1',
'  #define CORRECT',
'  #define COLORAVG',
'  #define MAXSTEPS 40.0',
'  #define ENLARGE 1.5',
'//     #define ANTIALIAS 2',
'  #define AA_TRIGGER 0.8',
'  #define AA_POWER 1.0',
'  #define AA_MAXITER 8.0',
'  #define CONFIDENCE_MAX 4.5',
'',
'#endif',
'',
'',
'#define BRANCHLOOP  ',
'#define BRANCHSAMPLE ',
'#define DEBUG 0',
'//#define DEBUGBREAK 4',
'',
'#ifndef METHOD',
'  #define METHOD 1',
'#endif',
'#ifndef MAXSTEPS',
'  #define MAXSTEPS 8.0',
'#endif',
'#ifndef ENLARGE',
'  #define ENLARGE 1.2',
'#endif',
'#ifndef PERSPECTIVE',
'  #define PERSPECTIVE 0.0',
'#endif',
'#ifndef UPSCALE',
'  #define UPSCALE 1.06',
'#endif',
'#ifndef CONFIDENCE_MAX',
'  #define CONFIDENCE_MAX 0.2',
'#endif',
'#ifndef COMPRESSION',
'  #define COMPRESSION 0.8',
'#endif',
'',
'const float perspective = PERSPECTIVE;',
'const float upscale = UPSCALE;',
'// float steps = clamp( ceil( max(abs(offset.x), abs(offset.y)) * maxSteps ), 1.0, maxSteps);',
'float steps = MAXSTEPS;',
'',
'#ifdef COLORAVG',
'float maskPower = steps * 2.0;// 32.0;',
'#else ',
'float maskPower = steps * 1.0;// 32.0;',
'#endif',
'float correctPower = 1.0;//max(1.0, steps / 8.0);',
'',
'const float compression = COMPRESSION;',
'const float dmin = (1.0 - compression) / 2.0;',
'const float dmax = (1.0 + compression) / 2.0;',
'',
'const float vectorCutoff = 0.0 + dmin - 0.0001;',
'',
'float aspect = dimensions.x / dimensions.y;',
'vec2 scale2 = vec2(scale * min(1.0, 1.0 / aspect), scale * min(1.0, aspect)) * vec2(1, -1) * vec2(ENLARGE);',
'// mat2 baseVector = mat2(vec2(-focus * offset) * scale2, vec2(offset - focus * offset) * scale2);',
'mat2 baseVector = mat2(vec2((0.5 - focus) * offset - offset/2.0) * scale2, ',
'                       vec2((0.5 - focus) * offset + offset/2.0) * scale2);',
'',
'',
'void main(void) {',
'',
'  vec2 pos = (vTextureCoord - vec2(0.5)) / vec2(upscale) + vec2(0.5);',
'  mat2 vector = baseVector;',
'  // perspective shift',
'  vector[1] += (vec2(2.0) * pos - vec2(1.0)) * vec2(perspective);',
'  ',
'  float dstep = compression / (steps - 1.0);',
'  vec2 vstep = (vector[1] - vector[0]) / vec2((steps - 1.0)) ;',
'  ',
'  #ifdef COLORAVG',
'    vec4 colSum = vec4(0.0);',
'  #else',
'    vec2 posSum = vec2(0.0);',
'  #endif',
'',
'  float confidenceSum = 0.0;',
'  float minConfidence = dstep / 2.0;',
'    ',
'  #ifdef ANTIALIAS',
'    #ifndef AA_TRIGGER',
'      #define AA_TRIGGER 0.8',
'    #endif',
'    #if ANTIALIAS == 11 || ANTIALIAS == 12',
'      #ifndef AA_POWER',
'        #define AA_POWER 0.5',
'      #endif',
'      #ifndef AA_MAXITER',
'        #define AA_MAXITER 16.0',
'      #endif',
'      float loopStep = 1.0;',
'    #endif',
'    ',
'    #define LOOP_INDEX j',
'    float j = 0.0;',
'  #endif',
'',
'  #ifndef LOOP_INDEX',
'    #define LOOP_INDEX i',
'  #endif',
'',
'',
'  for(float i = 0.0; i < MAXSTEPS; ++i) {',
'    vec2 vpos = pos + vector[1] - LOOP_INDEX * vstep;',
'    float dpos = 0.5 + compression / 2.0 - LOOP_INDEX * dstep;',
'    #ifdef BRANCHLOOP',
'    if (dpos >= vectorCutoff && confidenceSum < CONFIDENCE_MAX) {',
'    #endif',
'      float depth = 1.0 - texture2D(displacementMap, vpos * vec2(1, -1) + vec2(0, 1)).r;',
'      depth = clamp(depth, dmin, dmax);',
'      float confidence;',
'',
'      #if METHOD == 1',
'        confidence = step(dpos, depth + 0.001);',
'',
'      #elif METHOD == 2',
'        confidence = 1.0 - abs(dpos - depth);',
'        if (confidence < 1.0 - minConfidence * 2.0) confidence = 0.0;',
'',
'      #elif METHOD == 5',
'        confidence = 1.0 - abs(dpos - depth);',
'        confidence = pow(confidence, maskPower);',
'',
'      #endif',
'',
'      #ifndef BRANCHLOOP',
'       confidence *= step(vectorCutoff, dpos);',
'       confidence *= step(confidenceSum, CONFIDENCE_MAX);',
'      #endif',
'        ',
'      #ifndef ANTIALIAS',
'      #elif ANTIALIAS == 1 // go back halfstep, go forward fullstep - branched',
'        if (confidence > AA_TRIGGER && i == j) {',
'          j -= 0.5;',
'        } else {',
'          j += 1.0;',
'        }',
'        // confidence *= CONFIDENCE_MAX / 3.0;',
'',
'      #elif ANTIALIAS == 2 // go back halfstep, go forward fullstep - mult',
'        j += 1.0 + step(AA_TRIGGER, confidence) ',
'             * step(i, j) * -1.5; ',
'        // confidence *= CONFIDENCE_MAX / 3.0;',
'',
'      #elif ANTIALIAS == 11',
'        if (confidence >= AA_TRIGGER && i == j && steps - i > 1.0) {',
'          loopStep = AA_POWER * 2.0 / min(AA_MAXITER, steps - i - 1.0);',
'          j -= AA_POWER + loopStep;',
'        }',
'        confidence *= loopStep;',
'        j += loopStep;',
'      #elif ANTIALIAS == 12',
'        float _if_aa = step(AA_TRIGGER, confidence)',
'                     * step(i, j)',
'                     * step(1.5, steps - i);',
'        loopStep = _if_aa * (AA_POWER * 2.0 / min(AA_MAXITER, max(0.1, steps - i - 1.0)) - 1.0) + 1.0;',
'        confidence *= loopStep;',
'        j += _if_aa * -(AA_POWER + loopStep) + loopStep;',
'      #endif',
'',
'        ',
'      #ifdef BRANCHSAMPLE',
'      if (confidence > 0.0) {',
'      #endif',
'        ',
'        #ifdef CORRECT',
'          #define CORRECTION_MATH +( ( vec2((depth - dpos) / (dstep * correctPower)) * vstep ))',
'        #else',
'          #define CORRECTION_MATH',
'        #endif',
'          ',
'        #ifdef COLORAVG    ',
'          colSum += texture2D(uSampler, vpos CORRECTION_MATH) * confidence;',
'        #else',
'          posSum += (vpos CORRECTION_MATH) * confidence;    ',
'        #endif',
'          confidenceSum += confidence;',
'          ',
'      #ifdef BRANCHSAMPLE',
'      }',
'      #endif',
'',
'        ',
'      #if DEBUG > 2',
'        gl_FragColor = vec4(vector[0] / 2.0 + 1.0, vector[1].xy / 2.0 + 1.0);',
'      #elif DEBUG > 1',
'        gl_FragColor = vec4(confidenceSum, depth, dpos, 0);',
'      #elif DEBUG > 0',
'        gl_FragColor = vec4(confidence, depth, dpos, 0);',
'      #endif',
'      #ifdef DEBUGBREAK ',
'      if (i == float(DEBUGBREAK)) {',
'          dstep = 1.0;',
'      }     ',
'      #endif',
'',
'    #ifdef BRANCHLOOP',
'    }',
'    #endif',
'  };',
'',
'  #if defined(COLORAVG) && DEBUG == 0',
'    gl_FragColor = colSum / vec4(confidenceSum);',
'  #elif !defined(COLORAVG) && DEBUG == 0',
'    gl_FragColor = texture2D(uSampler, posSum / confidenceSum);',
'  #endif',
'',
'}',

  ];

  this.quality = quality;
  if (quality) {
    this.fragmentSrc.unshift('#define QUALITY ' + quality);
  }
};

PIXI.DepthPerspectiveFilter.prototype = Object.create( PIXI.AbstractFilter.prototype );
PIXI.DepthPerspectiveFilter.prototype.constructor = PIXI.DepthPerspectiveFilter;

PIXI.DepthPerspectiveFilter.prototype.onTextureLoaded = function()
{
  this.uniforms.mapDimensions.value.x = this.uniforms.displacementMap.value.width;
  this.uniforms.mapDimensions.value.y = this.uniforms.displacementMap.value.height;

  this.uniforms.displacementMap.value.baseTexture.off('loaded', this.boundLoadedFunction);
};

/**
 * The texture used for the displacemtent map * must be power of 2 texture at the moment
 *
 * @property map
 * @type Texture
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'map', {
  get: function() {
    return this.uniforms.displacementMap.value;
  },
  set: function(value) {
    this.uniforms.displacementMap.value = value;
  }
});

/**
 * The multiplier used to scale the displacement result from the map calculation.
 *
 * @property scale
 * @type Point
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'scale', {
  get: function() {
    return this.uniforms.scale.value;
  },
  set: function(value) {
    this.uniforms.scale.value = value;
  }
});

/**
 * Focus point in paralax
 *
 * @property focus
 * @type float
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'focus', {
  get: function() {
    return this.uniforms.focus.value;
  },
  set: function(value) {
    this.uniforms.focus.value = Math.min(1,Math.max(0,value));
  }
});

/**
 * The offset used to move the displacement map.
 *
 * @property offset
 * @type Point
 */
Object.defineProperty(PIXI.DepthPerspectiveFilter.prototype, 'offset', {
  get: function() {
    return this.uniforms.offset.value;
  },
  set: function(value) {
    this.uniforms.offset.value = value;
  }
});<|endoftext|>


<commit_before>define(['backbone', 'List', 'SearchElementView', 'bootstrap'], function(Backbone, List, SearchElementView, bootstrap) {

  return Backbone.View.extend({

    tagName: 'div',
    className: 'workspace-search',

    initialize: function(atts, arr) {
      this.app = arr.app;
      this.appView = arr.appView;

      this.app.SearchElements.on('add remove', this.render, this);
    },

    template: _.template( $('#workspace-search-template').html() ),

    events: {
      'keyup .library-search-input': 'searchKeyup',
      'focus .library-search-input': 'focus',
      'blur .library-search-input': 'blur',
      'click #delete-button': 'deleteClick',
      'click #undo-button': 'undoClick',
      'click #redo-button': 'redoClick',
      'click #copy-button': 'copyClick',
      'click #paste-button': 'pasteClick',
      'click #zoomin-button': 'zoominClick',
      'click #zoomout-button': 'zoomoutClick',
      'click #zoomreset-button': 'zoomresetClick',
      'click #export-button': 'exportClick'
    },

    render: function(arg) {

      this.$el.html( this.template( this.model.toJSON() ) );

      this.$input = this.$('.library-search-input');
      this.$list = this.$('.search-list');

      this.$list.empty();

      var that = this;

      this.app.SearchElements.forEach(function(ele) {

        var eleView = new SearchElementView({ model: ele }, { appView: that.appView, app: that.app,
          click: function(e){ that.elementClick.call(that, e); } });

        eleView.render();
        that.$list.append( eleView.$el );

      });

      var options = {
          valueNames: [ 'name' ]
      };

      this.list = new List(this.el, options);

      var del = { show: 300 };

      // build button tooltips
      this.$el.find('#undo-button').tooltip({title: "Ctrl/Cmd Z", delay: del});
      this.$el.find('#redo-button').tooltip({title: "Ctrl/Cmd Y", delay: del});

      this.$el.find('#copy-button').tooltip({title: "Ctrl/Cmd C", delay: del});
      this.$el.find('#paste-button').tooltip({title: "Ctrl/Cmd V", delay: del});

      this.$el.find('#delete-button').tooltip({title: "Backspace/Delete", delay: del});

      this.$el.find('#zoomin-button').tooltip({title: "Ctrl/Cmd +", delay: del});
      this.$el.find('#zoomout-button').tooltip({title: "Ctrl/Cmd -", delay: del});
      this.$el.find('#zoomreset-button').tooltip({title: "Ctrl/Cmd 0", delay: del});

    },

    focus: function(event){
      this.$('.search-list').show();
      this.$('.library-search-input').select();
    },

    blur: function(event){
      var that = this;
      window.setTimeout(function(){
        that.$('.search-list').hide();
      }, 100);
    },

    currentWorkspace: function(){
      return this.app.get('workspaces').get( this.app.get('currentWorkspace') );
    },

    deleteClick: function(){
      this.currentWorkspace().removeSelected();
    },

    copyClick: function(){
      this.currentWorkspace().copy();
    },

    pasteClick: function(){
      this.currentWorkspace().paste();
    },

    undoClick: function(){
      this.currentWorkspace().undo();
    },

    redoClick: function(){
      this.currentWorkspace().redo();
    },

    zoomresetClick: function(){
      this.currentWorkspace().set('zoom', 1.0);
    },

    zoominClick: function(){
      this.currentWorkspace().zoomIn();
    },

    zoomoutClick: function(){
      this.currentWorkspace().zoomOut();
    },

    getWorkspaceCenter: function(){

      var w = this.appView.currentWorkspaceView.$el.width()
        , h = this.appView.currentWorkspaceView.$el.height()
        , ho = this.appView.currentWorkspaceView.$el.scrollTop()
        , wo = this.appView.currentWorkspaceView.$el.scrollLeft()
        , zoom = 1 / this.currentWorkspace().get('zoom');

      return [zoom * (wo + w / 2), zoom * (ho + h / 2)];
    },

    addNode: function(name){

      if (name === undefined ) return;

      this.currentWorkspace().addNodeByNameAndPosition( name, this.getWorkspaceCenter() );

    },

    objConverter: function(x, vertexOffset, index){

        if ( !x || !( x.vertices && x.faces ) ) return "";

        var text = "";

        // OBJ used 1-based indexing
        vertexOffset = vertexOffset + 1;

        x.vertices.forEach(function(v){

          text += "v"
          text += " " + v[0];
          text += " " + v[1];
          text += " " + v[2];
          text += "\n";

        });

        x.faces.forEach(function(f){

          text += "f"
          text += " " + (vertexOffset + f[0]);
          text += " " + (vertexOffset + f[1]);
          text += " " + (vertexOffset + f[2]);
          text += "\n";

        });

        return text;
    },

    stlConverter: function(x, vertexOffset, index){

        if ( !x || !( x.vertices && x.faces ) ) return "";

        var text = "solid s" + index + "\n";

        x.faces.forEach(function(f){

          var v1 = x.vertices[ f[0] ];
          var v2 = x.vertices[ f[1] ];
          var v3 = x.vertices[ f[2] ];
          var n = f[3];

          text += "facet normal"
          text += " " + n[0];
          text += " " + n[1];
          text += " " + n[2];
          text += "\n";

            text += "\touter loop\n";

              text += "\t\tvertex"
              text += " " + v1[0];
              text += " " + v1[1];
              text += " " + v1[2];
              text += "\n";

              text += "\t\tvertex"
              text += " " + v2[0];
              text += " " + v2[1];
              text += " " + v2[2];
              text += "\n";

              text += "\t\tvertex"
              text += " " + v3[0];
              text += " " + v3[1];
              text += " " + v3[2];
              text += "\n";

            text += "\tendloop\n";

          text += "\tendfacet\n";

        });

        text += "endsolid\n";

        return text;
    },

    exportClick: function(e){

      var res = this.getFileFromSelected( this.objConverter );

      var wsName = this.currentWorkspace().get('name');

      this.download(wsName + ".obj", res);

    },

    getFileFromSelected: function(converterFunc){

      var ws = this.currentWorkspace();

      var text = "";
      var vertexOffset = 0;

      return ws.get('nodes')
        .filter(function(x){ return x.get('selected'); })
        .map(function(x){ return x.get('prettyLastValue'); })
        .flatten()
        .reduce(function(a, x, i){

          var t = converterFunc(x, vertexOffset, i) || "";

          if ( x && x.vertices && x.vertices.length != undefined)
            vertexOffset += x.vertices.length;

          return a + t;

        }, text);

    },

    download: function(filename, text) {
        var pom = document.createElement('a');
        pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
        pom.setAttribute('download', filename);
        pom.click();
    },

    elementClick: function(ele){

      this.addNode( ele.model.get('name') );

    },

    searchKeyup: function(event) {

      // enter key causes first result to be inserted
      if ( event.keyCode === 13) {

        var nodeName = this.$list.find('.search-element').first().find('.name').first().html();
        if (nodeName === undefined ) return;

        this.addNode( nodeName );

      }

    }

  });

});

<commit_msg>Change default export format to STL<commit_after>define(['backbone', 'List', 'SearchElementView', 'bootstrap'], function(Backbone, List, SearchElementView, bootstrap) {

  return Backbone.View.extend({

    tagName: 'div',
    className: 'workspace-search',

    initialize: function(atts, arr) {
      this.app = arr.app;
      this.appView = arr.appView;

      this.app.SearchElements.on('add remove', this.render, this);
    },

    template: _.template( $('#workspace-search-template').html() ),

    events: {
      'keyup .library-search-input': 'searchKeyup',
      'focus .library-search-input': 'focus',
      'blur .library-search-input': 'blur',
      'click #delete-button': 'deleteClick',
      'click #undo-button': 'undoClick',
      'click #redo-button': 'redoClick',
      'click #copy-button': 'copyClick',
      'click #paste-button': 'pasteClick',
      'click #zoomin-button': 'zoominClick',
      'click #zoomout-button': 'zoomoutClick',
      'click #zoomreset-button': 'zoomresetClick',
      'click #export-button': 'exportClick'
    },

    render: function(arg) {

      this.$el.html( this.template( this.model.toJSON() ) );

      this.$input = this.$('.library-search-input');
      this.$list = this.$('.search-list');

      this.$list.empty();

      var that = this;

      this.app.SearchElements.forEach(function(ele) {

        var eleView = new SearchElementView({ model: ele }, { appView: that.appView, app: that.app,
          click: function(e){ that.elementClick.call(that, e); } });

        eleView.render();
        that.$list.append( eleView.$el );

      });

      var options = {
          valueNames: [ 'name' ]
      };

      this.list = new List(this.el, options);

      var del = { show: 300 };

      // build button tooltips
      this.$el.find('#undo-button').tooltip({title: "Ctrl/Cmd Z", delay: del});
      this.$el.find('#redo-button').tooltip({title: "Ctrl/Cmd Y", delay: del});

      this.$el.find('#copy-button').tooltip({title: "Ctrl/Cmd C", delay: del});
      this.$el.find('#paste-button').tooltip({title: "Ctrl/Cmd V", delay: del});

      this.$el.find('#delete-button').tooltip({title: "Backspace/Delete", delay: del});

      this.$el.find('#zoomin-button').tooltip({title: "Ctrl/Cmd +", delay: del});
      this.$el.find('#zoomout-button').tooltip({title: "Ctrl/Cmd -", delay: del});
      this.$el.find('#zoomreset-button').tooltip({title: "Ctrl/Cmd 0", delay: del});

    },

    focus: function(event){
      this.$('.search-list').show();
      this.$('.library-search-input').select();
    },

    blur: function(event){
      var that = this;
      window.setTimeout(function(){
        that.$('.search-list').hide();
      }, 100);
    },

    currentWorkspace: function(){
      return this.app.get('workspaces').get( this.app.get('currentWorkspace') );
    },

    deleteClick: function(){
      this.currentWorkspace().removeSelected();
    },

    copyClick: function(){
      this.currentWorkspace().copy();
    },

    pasteClick: function(){
      this.currentWorkspace().paste();
    },

    undoClick: function(){
      this.currentWorkspace().undo();
    },

    redoClick: function(){
      this.currentWorkspace().redo();
    },

    zoomresetClick: function(){
      this.currentWorkspace().set('zoom', 1.0);
    },

    zoominClick: function(){
      this.currentWorkspace().zoomIn();
    },

    zoomoutClick: function(){
      this.currentWorkspace().zoomOut();
    },

    getWorkspaceCenter: function(){

      var w = this.appView.currentWorkspaceView.$el.width()
        , h = this.appView.currentWorkspaceView.$el.height()
        , ho = this.appView.currentWorkspaceView.$el.scrollTop()
        , wo = this.appView.currentWorkspaceView.$el.scrollLeft()
        , zoom = 1 / this.currentWorkspace().get('zoom');

      return [zoom * (wo + w / 2), zoom * (ho + h / 2)];
    },

    addNode: function(name){

      if (name === undefined ) return;

      this.currentWorkspace().addNodeByNameAndPosition( name, this.getWorkspaceCenter() );

    },

    objConverter: function(x, vertexOffset, index){

        if ( !x || !( x.vertices && x.faces ) ) return "";

        var text = "";

        // OBJ used 1-based indexing
        vertexOffset = vertexOffset + 1;

        x.vertices.forEach(function(v){

          text += "v"
          text += " " + v[0];
          text += " " + v[1];
          text += " " + v[2];
          text += "\n";

        });

        x.faces.forEach(function(f){

          text += "f"
          text += " " + (vertexOffset + f[0]);
          text += " " + (vertexOffset + f[1]);
          text += " " + (vertexOffset + f[2]);
          text += "\n";

        });

        return text;
    },

    stlConverter: function(x, vertexOffset, index){

        if ( !x || !( x.vertices && x.faces ) ) return "";

        var text = "solid s" + index + "\n";

        x.faces.forEach(function(f){

          var v1 = x.vertices[ f[0] ];
          var v2 = x.vertices[ f[1] ];
          var v3 = x.vertices[ f[2] ];
          var n = f[3];

          text += "facet normal"
          text += " " + n[0];
          text += " " + n[1];
          text += " " + n[2];
          text += "\n";

            text += "\touter loop\n";

              text += "\t\tvertex"
              text += " " + v1[0];
              text += " " + v1[1];
              text += " " + v1[2];
              text += "\n";

              text += "\t\tvertex"
              text += " " + v2[0];
              text += " " + v2[1];
              text += " " + v2[2];
              text += "\n";

              text += "\t\tvertex"
              text += " " + v3[0];
              text += " " + v3[1];
              text += " " + v3[2];
              text += "\n";

            text += "\tendloop\n";

          text += "\tendfacet\n";

        });

        text += "endsolid\n";

        return text;
    },

    exportClick: function(e){

      var res = this.getFileFromSelected( this.stlConverter );

      var wsName = this.currentWorkspace().get('name');

      this.download(wsName + ".stl", res);

    },

    getFileFromSelected: function(converterFunc){

      var ws = this.currentWorkspace();

      var text = "";
      var vertexOffset = 0;

      return ws.get('nodes')
        .filter(function(x){ return x.get('selected'); })
        .map(function(x){ return x.get('prettyLastValue'); })
        .flatten()
        .reduce(function(a, x, i){

          var t = converterFunc(x, vertexOffset, i) || "";

          if ( x && x.vertices && x.vertices.length != undefined)
            vertexOffset += x.vertices.length;

          return a + t;

        }, text);

    },

    download: function(filename, text) {
        var pom = document.createElement('a');
        pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
        pom.setAttribute('download', filename);
        pom.click();
    },

    elementClick: function(ele){

      this.addNode( ele.model.get('name') );

    },

    searchKeyup: function(event) {

      // enter key causes first result to be inserted
      if ( event.keyCode === 13) {

        var nodeName = this.$list.find('.search-element').first().find('.name').first().html();
        if (nodeName === undefined ) return;

        this.addNode( nodeName );

      }

    }

  });

});

<|endoftext|>


<commit_before>class BrewTimerDialog {
  constructor(el, recipeId) {
    this.el = el;
    this.recipeId = recipeId;
    this.startEl = this.el.find(".brew-timer-start");
    this.pauseEl = this.el.find(".brew-timer-pause");
    this.resetEl = this.el.find(".brew-timer-reset");
    this.sounds = new Map();
    this.el
      .on('shown.bs.modal', this.setupDialog.bind(this))
      .on('hidden.bs.modal', this.cancelDialog.bind(this));
  }

  setupDialog() {
    this.setupSounds();
    this.setupTimer();
    this.startEl.on("click", this.toggleTimer.bind(this));
    this.pauseEl.on("click", this.toggleTimer.bind(this));
    this.resetEl.on("click", this.resetTimer.bind(this));
    // TODO: Only when modal is active
    $(window).on("keypress", this.keyPressed.bind(this));
  }

  cancelDialog() {
    this.startEl.off("click", this.toggleTimer.bind(this));
    this.pauseEl.off("click", this.toggleTimer.bind(this));
    this.resetEl.off("click", this.resetTimer.bind(this));
    $(window).off("keypress", this.keyPressed.bind(this));
  }

  setupTimer() {
    if (!this.timer) {
      $.get(`/recipe_steps/${this.recipeId}`, this.storeRecipeSteps.bind(this));
    }
  }

  setupSounds() {
    if (this.sounds.size > 0) {
      return;
    }
    window.assets.audio.forEach((path, key) => {
      this.sounds.set(key, new Howl({ src: [path] }));
    });
  }

  play() {
    this.getRandomItem(this.sounds).play();
  }

  getRandomItem(iterable) {
    return iterable.get([...iterable.keys()][Math.floor(Math.random() * iterable.size)]);
  }

  storeRecipeSteps(data) {
    const stepType = this.el.data("stepType");
    const steps = data[stepType + "_steps"];
    this.timer = new BrewTimer(this.el.find('.timer-content'), steps, stepType);
    this.timer.addEventListener("brewtimer.done", (event) => {
      this.togglePlayButton();
      this.play();
    });
  }

  keyPressed(ev) {
    if ((ev.keyCode === 0) || (ev.keyCode === 32)) {
      this.toggleTimer();
    }
    return false;
  }

  togglePlayButton() {
    this.startEl.toggleClass("hidden");
    this.pauseEl.toggleClass("hidden");
  }

  resetTimer() {
    if (this.timer) {
      this.timer.reset();
      this.togglePlayButton();
    }
    return false;
  }

  toggleTimer() {
    if (this.timer) {
      this.timer.toggle();
      this.togglePlayButton();
    }
    return false;
  }
}

(typeof exports !== 'undefined' && exports !== null ? exports : this).BrewTimerDialog = BrewTimerDialog;
<commit_msg>Only trigger on keypress when timer is visible<commit_after>class BrewTimerDialog {
  constructor(el, recipeId) {
    this.el = el;
    this.recipeId = recipeId;
    this.startEl = this.el.find(".brew-timer-start");
    this.pauseEl = this.el.find(".brew-timer-pause");
    this.resetEl = this.el.find(".brew-timer-reset");
    this.sounds = new Map();
    this.el
      .on('shown.bs.modal', this.setupDialog.bind(this))
      .on('hidden.bs.modal', this.cancelDialog.bind(this));
  }

  setupDialog() {
    this.setupSounds();
    this.setupTimer();
    this.startEl.on("click", this.toggleTimer.bind(this));
    this.pauseEl.on("click", this.toggleTimer.bind(this));
    this.resetEl.on("click", this.resetTimer.bind(this));
    $(window).on("keypress", this.keyPressed.bind(this));
  }

  cancelDialog() {
    this.startEl.off("click", this.toggleTimer.bind(this));
    this.pauseEl.off("click", this.toggleTimer.bind(this));
    this.resetEl.off("click", this.resetTimer.bind(this));
    $(window).off("keypress", this.keyPressed.bind(this));
  }

  setupTimer() {
    if (!this.timer) {
      $.get(`/recipe_steps/${this.recipeId}`, this.storeRecipeSteps.bind(this));
    }
  }

  setupSounds() {
    if (this.sounds.size > 0) {
      return;
    }
    window.assets.audio.forEach((path, key) => {
      this.sounds.set(key, new Howl({ src: [path] }));
    });
  }

  play() {
    this.getRandomItem(this.sounds).play();
  }

  getRandomItem(iterable) {
    return iterable.get([...iterable.keys()][Math.floor(Math.random() * iterable.size)]);
  }

  storeRecipeSteps(data) {
    const stepType = this.el.data("stepType");
    const steps = data[stepType + "_steps"];
    this.timer = new BrewTimer(this.el.find('.timer-content'), steps, stepType);
    this.timer.addEventListener("brewtimer.done", (event) => {
      this.togglePlayButton();
      this.play();
    });
  }

  keyPressed(ev) {
    if (this.el.is(":hidden")) {
      return;
    }
    if ((ev.keyCode === 0) || (ev.keyCode === 32)) {
      this.toggleTimer();
    }
    return false;
  }

  togglePlayButton() {
    this.startEl.toggleClass("hidden");
    this.pauseEl.toggleClass("hidden");
  }

  resetTimer() {
    if (this.timer) {
      this.timer.reset();
      this.togglePlayButton();
    }
    return false;
  }

  toggleTimer() {
    if (this.timer) {
      this.timer.toggle();
      this.togglePlayButton();
    }
    return false;
  }
}

(typeof exports !== 'undefined' && exports !== null ? exports : this).BrewTimerDialog = BrewTimerDialog;
<|endoftext|>