<commit_before>angular.module('media_manager')
.controller('WorkspaceController', ['$scope',
                                    '$timeout',
                                    '$log',
                                    '$routeParams',
                                    '$location',
                                    '$uibModal',
                                    'Droplet',
                                    'Course',
                                    'Collection',
                                    'CourseCache',
                                    'CollectionBehavior',
                                    'ImageLightBox',
                                    'Breadcrumbs',
                                    'Notifications',
                                    'Preferences',
                                    'AppConfig',
                                    function($scope,
                                      $timeout,
                                      $log,
                                      $routeParams,
                                      $location,
                                      $uibModal,
                                      Droplet,
                                      Course,
                                      Collection,
                                      CourseCache,
                                      CollectionBehavior,
                                      ImageLightBox,
                                      Breadcrumbs,
                                      Notifications,
                                      Preferences,
                                      AppConfig){

  var wc = this;

  wc.imagelb = ImageLightBox;

  var dragEnabled = true;
  wc.dragControlListeners1 = {
    accept: function(sourceItemHandleScope, destSortableScope){
      return dragEnabled;
    }
  };

  /*
    accept: function (sourceItemHandleScope, destSortableScope) {
      console.log("accept");
      return dragEnabled;
    },
    orderChanged: function(event){
      console.log("orderChanged");
      // disable ordering
      dragEnabled = false;

      var updates = [];
      lc.collections.forEach(function(item, index, arr){
        var d = $q.defer();
        var newsort = index + 1;
        if(item.sort_order != newsort){
          arr[index].sort_order = newsort;
          updates.push(Collection.update({id: item.id}, arr[index]).$promise);

        }
      });
      $q.all(updates).then(function(){
        // enable ordering
        dragEnabled = true;
      });

    }
  };
  */

  wc.imageView = function(id){
    $location.path('/image/' + id);
  };

  wc.isActiveCollection = function(id){
    return id == wc.collection.id;
  };

  wc.loadActiveCollection = function() {
    var self = this, collection;
    if($routeParams.collectionId !== undefined){
      self.isLoadingCollection.status = true;
      collection = Collection.get({id: $routeParams.collectionId});
      collection.$promise.then(function(collection) {
        wc.collection.images.sort(function(a, b){
          var x = a['sort_order'];
          var y = b['sort_order'];
          return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
        self.isLoadingCollection.status = false;
      });
    } else {
      //wc.collection = [];
      collection = new Collection();
      collection.images = [];
      collection.title = "Untitled Collection";
    }
    return collection
  };

  wc.addToCollection = function(courseImage){
    wc.collection.images.push(courseImage);
    $timeout(function() {
      var $el = $("#image-collection-panel .panel-body")
      var el = $el[0];
      if ($el.css("overflow-x") == "auto") {
        el.scrollLeft = el.scrollWidth - el.clientWidth;
      }
    }, 0, false);
  };

  wc.removeFromCollection = function(imageIndex){
    wc.collection.images.splice(imageIndex, 1);
  };

  wc.cancelCollection = function() {
    $location.path('/collections/');
  };

  wc.saveCollection = function(){
    if($routeParams.collectionId){
      wc.updateCollection();
    } else {
      wc.createCollection();
    }
  };

  wc.updateCollection = function() {
    var self = this;

    $log.debug("update collection", wc.collection.id);
    wc.collection.description = wc.collection.description || "No description";
    wc.collection.course_image_ids = wc.collection.images.map(function(image) {
      // images could come from the image library, or already be part of the collection
      // and we want to make sure we're returning the image "id" property, not the collectionimage "id"
      image_prop_for = {
        "collectionimages": "course_image_id",
        "images": "id"
      };
      return image[image_prop_for[image.type]];
    })

    // put to update collection
    self.isSavingCollection.status = true;
    Collection.update({}, wc.collection, function(data){
      wc.notifications.clear();
      var collection = wc.loadActiveCollection();
      self.isLoadingCollection.status = true;
      collection.$promise.then(function(collection) {
        wc.collection = collection;
      }, function(response) {
        wc.notifications.error("Error loading collection: " + response);
      }).finally(function() {
        self.isLoadingCollection.status = false;
      });
    }, function(errorResponse) {
      $log.debug("Error updating collection:", errorResponse);
      wc.notifications.error("Error updating collection: " + errorResponse);
    }).$promise.then(function(data) {
      $log.debug("Collection updated: ", data)
      wc.notifications.success("Collection saved.");
    }).finally(function() {
      self.isSavingCollection.status = false;
    });
  };

  wc.createCollection = function() {
    $log.debug("create collection");
    wc.collection.course_id = AppConfig.course_id;
    wc.collection.course_image_ids = wc.collection.images.map(function(image){
      return image.id;
    });

    // post to save a new collection
    Collection.save({}, wc.collection, function(data){
      wc.collection.id = data.id;
      wc.courseCollections.push(wc.collection);
      $location.path('/collections/');
    });
  };

  // Fix the collection panel at the top of the screen
  wc.onDocumentScroll = (function() {
    var fixedPosition = false;
    var fixedCls = 'image-collection-fixed';
    var fixedSelector = "#image-collection-panel";
    var offsetSelector = "#image-collection-panel";
    var offsetTop = null;

    return function(event) {
      var windowTop = $(window).scrollTop();
      if (fixedPosition) {
        if (windowTop < offsetTop) {
          $(fixedSelector).removeClass(fixedCls);
          fixedPosition = false;
        }
      } else {
        if (offsetTop === null) {
          offsetTop = $(offsetSelector).offset().top;
        }
        if (offsetTop < windowTop) {
          $(fixedSelector).addClass(fixedCls);
          fixedPosition = true;
        }
      }
    };
  })();

  wc.onClickSortLibrary = function($event, choice) {
    $event.preventDefault();
    wc.sortLibrary(choice);
  };

  wc.sortLibrary = function(choice) {
    CourseCache.updateSort(choice.name, choice.dir).sortImages();
  };

  Breadcrumbs.home().addCrumb("Manage Collection", $location.url());

  CourseCache.load();

  wc.layout = Preferences.get(Preferences.UI_WORKSPACE_LAYOUT);
  wc.Droplet = Droplet;
  wc.courseImages = CourseCache.images;
  wc.courseCollections = CourseCache.collections;
  wc.isLoading = CourseCache.isLoading;
  wc.isSavingCollection = {status:false, msg:"Saving collection..."};
  wc.isLoadingCollection = {status:false, msg:"Loading collection..."};
  wc.collection = wc.loadActiveCollection();
  wc.canEdit = AppConfig.perms.edit;
  wc.filesToUpload = 0;
  wc.notifications = Notifications;
  wc.sortChoices = [
    {'label': 'Newest to Oldest', 'name': 'created', 'dir': 'desc'},
    {'label': 'Oldest to Newest', 'name': 'created', 'dir': 'asc'},
    //{'label': 'Last Updated', 'name': 'updated', 'dir': 'desc'},
    {'label': 'Title', 'name': 'title', 'dir': 'asc'},
  ];

  wc.sortLibrary(wc.sortChoices[0]);

  wc.notifications.clear();

  $scope.$on('$dropletReady', Droplet.onReady);
  $scope.$on('$dropletError', Droplet.onError(function(event, response) {
    wc.notifications.clear().error(response);
  }));
  $scope.$on('$dropletFileAdded', Droplet.onFileAdded(function(event, model) {
    wc.filesToUpload = Droplet.getTotalValid();
  }, function(event, model, msg) {
    wc.filesToUpload = Droplet.getTotalValid();
    wc.notifications.notify("warning", msg);
  }));
  $scope.$on('$dropletFileDeleted', Droplet.onFileDeleted(function() {
    wc.filesToUpload = Droplet.getTotalValid();
  }));
  $scope.$on('$dropletSuccess', Droplet.onSuccess(function(event, response, files) {
      if (angular.isArray(response)) {
        for(var i = 0; i < response.length; i++) {
          CourseCache.addImage(response[i]);
        }
      } else {
        CourseCache.addImage(response);
      }
      wc.filesToUpload = Droplet.getTotalValid();
      wc.notifications.clear().success("Images uploaded successfully");
  }));

  $scope.$watch('wc.layout', function(newVal, oldVal) {
    Preferences.set(Preferences.UI_WORKSPACE_LAYOUT, newVal);
  })

  //$(document).scroll(wc.onDocumentScroll);
}]);
<commit_msg>altering removeFromCollection to work with the id instead of the index<commit_after>angular.module('media_manager')
.controller('WorkspaceController', ['$scope',
                                    '$timeout',
                                    '$log',
                                    '$routeParams',
                                    '$location',
                                    '$uibModal',
                                    'Droplet',
                                    'Course',
                                    'Collection',
                                    'CourseCache',
                                    'CollectionBehavior',
                                    'ImageLightBox',
                                    'Breadcrumbs',
                                    'Notifications',
                                    'Preferences',
                                    'AppConfig',
                                    function($scope,
                                      $timeout,
                                      $log,
                                      $routeParams,
                                      $location,
                                      $uibModal,
                                      Droplet,
                                      Course,
                                      Collection,
                                      CourseCache,
                                      CollectionBehavior,
                                      ImageLightBox,
                                      Breadcrumbs,
                                      Notifications,
                                      Preferences,
                                      AppConfig){

  var wc = this;

  wc.imagelb = ImageLightBox;

  var dragEnabled = true;
  wc.dragControlListeners1 = {
    accept: function(sourceItemHandleScope, destSortableScope){
      return dragEnabled;
    },
    orderChanged: function(event){

    }
  };

  /*
    accept: function (sourceItemHandleScope, destSortableScope) {
      console.log("accept");
      return dragEnabled;
    },
    orderChanged: function(event){
      console.log("orderChanged");
      // disable ordering
      dragEnabled = false;

      var updates = [];
      lc.collections.forEach(function(item, index, arr){
        var d = $q.defer();
        var newsort = index + 1;
        if(item.sort_order != newsort){
          arr[index].sort_order = newsort;
          updates.push(Collection.update({id: item.id}, arr[index]).$promise);

        }
      });
      $q.all(updates).then(function(){
        // enable ordering
        dragEnabled = true;
      });

    }
  };
  */

  wc.imageView = function(id){
    $location.path('/image/' + id);
  };

  wc.isActiveCollection = function(id){
    return id == wc.collection.id;
  };

  wc.loadActiveCollection = function() {
    var self = this, collection;
    if($routeParams.collectionId !== undefined){
      self.isLoadingCollection.status = true;
      collection = Collection.get({id: $routeParams.collectionId});
      collection.$promise.then(function(collection) {
        wc.collection.images.sort(function(a, b){
          var x = a['sort_order'];
          var y = b['sort_order'];
          return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        });
        self.isLoadingCollection.status = false;
      });
    } else {
      //wc.collection = [];
      collection = new Collection();
      collection.images = [];
      collection.title = "Untitled Collection";
    }
    return collection
  };

  wc.addToCollection = function(courseImage){
    wc.collection.images.push(courseImage);
    $timeout(function() {
      var $el = $("#image-collection-panel .panel-body")
      var el = $el[0];
      if ($el.css("overflow-x") == "auto") {
        el.scrollLeft = el.scrollWidth - el.clientWidth;
      }
    }, 0, false);
  };

  wc.removeFromCollection = function(id){
    // note this needs to be a forEach/search instead of a splice because
    // ng-sortable won't work with "track by $index" enabled on the ng-repeat
    // https://github.com/a5hik/ng-sortable/issues/221
    wc.collection.images.forEach(function(item, index, arr){
      if(item.id == id){
        wc.collection.images.splice(index, 1);
        return false;
      }
    });
  };

  wc.cancelCollection = function() {
    $location.path('/collections/');
  };

  wc.saveCollection = function(){
    if($routeParams.collectionId){
      wc.updateCollection();
    } else {
      wc.createCollection();
    }
  };

  wc.updateCollection = function() {
    var self = this;

    $log.debug("update collection", wc.collection.id);
    wc.collection.description = wc.collection.description || "No description";
    wc.collection.course_image_ids = wc.collection.images.map(function(image) {
      // images could come from the image library, or already be part of the collection
      // and we want to make sure we're returning the image "id" property, not the collectionimage "id"
      image_prop_for = {
        "collectionimages": "course_image_id",
        "images": "id"
      };
      return image[image_prop_for[image.type]];
    })

    // put to update collection
    self.isSavingCollection.status = true;
    Collection.update({}, wc.collection, function(data){
      wc.notifications.clear();
      var collection = wc.loadActiveCollection();
      self.isLoadingCollection.status = true;
      collection.$promise.then(function(collection) {
        wc.collection = collection;
      }, function(response) {
        wc.notifications.error("Error loading collection: " + response);
      }).finally(function() {
        self.isLoadingCollection.status = false;
      });
    }, function(errorResponse) {
      $log.debug("Error updating collection:", errorResponse);
      wc.notifications.error("Error updating collection: " + errorResponse);
    }).$promise.then(function(data) {
      $log.debug("Collection updated: ", data)
      wc.notifications.success("Collection saved.");
    }).finally(function() {
      self.isSavingCollection.status = false;
    });
  };

  wc.createCollection = function() {
    $log.debug("create collection");
    wc.collection.course_id = AppConfig.course_id;
    wc.collection.course_image_ids = wc.collection.images.map(function(image){
      return image.id;
    });

    // post to save a new collection
    Collection.save({}, wc.collection, function(data){
      wc.collection.id = data.id;
      wc.courseCollections.push(wc.collection);
      $location.path('/collections/');
    });
  };

  // Fix the collection panel at the top of the screen
  wc.onDocumentScroll = (function() {
    var fixedPosition = false;
    var fixedCls = 'image-collection-fixed';
    var fixedSelector = "#image-collection-panel";
    var offsetSelector = "#image-collection-panel";
    var offsetTop = null;

    return function(event) {
      var windowTop = $(window).scrollTop();
      if (fixedPosition) {
        if (windowTop < offsetTop) {
          $(fixedSelector).removeClass(fixedCls);
          fixedPosition = false;
        }
      } else {
        if (offsetTop === null) {
          offsetTop = $(offsetSelector).offset().top;
        }
        if (offsetTop < windowTop) {
          $(fixedSelector).addClass(fixedCls);
          fixedPosition = true;
        }
      }
    };
  })();

  wc.onClickSortLibrary = function($event, choice) {
    $event.preventDefault();
    wc.sortLibrary(choice);
  };

  wc.sortLibrary = function(choice) {
    CourseCache.updateSort(choice.name, choice.dir).sortImages();
  };

  Breadcrumbs.home().addCrumb("Manage Collection", $location.url());

  CourseCache.load();

  wc.layout = Preferences.get(Preferences.UI_WORKSPACE_LAYOUT);
  wc.Droplet = Droplet;
  wc.courseImages = CourseCache.images;
  wc.courseCollections = CourseCache.collections;
  wc.isLoading = CourseCache.isLoading;
  wc.isSavingCollection = {status:false, msg:"Saving collection..."};
  wc.isLoadingCollection = {status:false, msg:"Loading collection..."};
  wc.collection = wc.loadActiveCollection();
  wc.canEdit = AppConfig.perms.edit;
  wc.filesToUpload = 0;
  wc.notifications = Notifications;
  wc.sortChoices = [
    {'label': 'Newest to Oldest', 'name': 'created', 'dir': 'desc'},
    {'label': 'Oldest to Newest', 'name': 'created', 'dir': 'asc'},
    //{'label': 'Last Updated', 'name': 'updated', 'dir': 'desc'},
    {'label': 'Title', 'name': 'title', 'dir': 'asc'},
  ];

  wc.sortLibrary(wc.sortChoices[0]);

  wc.notifications.clear();

  $scope.$on('$dropletReady', Droplet.onReady);
  $scope.$on('$dropletError', Droplet.onError(function(event, response) {
    wc.notifications.clear().error(response);
  }));
  $scope.$on('$dropletFileAdded', Droplet.onFileAdded(function(event, model) {
    wc.filesToUpload = Droplet.getTotalValid();
  }, function(event, model, msg) {
    wc.filesToUpload = Droplet.getTotalValid();
    wc.notifications.notify("warning", msg);
  }));
  $scope.$on('$dropletFileDeleted', Droplet.onFileDeleted(function() {
    wc.filesToUpload = Droplet.getTotalValid();
  }));
  $scope.$on('$dropletSuccess', Droplet.onSuccess(function(event, response, files) {
      if (angular.isArray(response)) {
        for(var i = 0; i < response.length; i++) {
          CourseCache.addImage(response[i]);
        }
      } else {
        CourseCache.addImage(response);
      }
      wc.filesToUpload = Droplet.getTotalValid();
      wc.notifications.clear().success("Images uploaded successfully");
  }));

  $scope.$watch('wc.layout', function(newVal, oldVal) {
    Preferences.set(Preferences.UI_WORKSPACE_LAYOUT, newVal);
  })

  //$(document).scroll(wc.onDocumentScroll);
}]);
<|endoftext|>


<commit_before>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;
var numFormat = d3.format(",");

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);
        //loadCSV('data.csv')
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    formatData();

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        formatData();

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        d['key'] = d['Group'];
        d['values'] = [];

        _.each(d, function(v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d['values'].push({ 'label': k, 'amt': +v });
            delete d[k];
        });

        delete d['Group'];
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function(config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = config['data'].length;
    var numGroupBars = config['data'][0]['values'].length;

    var barHeight = parseInt(graphicConfig.barHeight || 25, 10);
    var barGapInner = parseInt(graphicConfig.barGap || 2, 10);
    var barGap = parseInt(graphicConfig.barGap || 10, 10);
    var labelWidth = parseInt(graphicConfig.labelWidth || 85, 10);
    var labelMargin = parseInt(graphicConfig.labelMargin || 6, 10);
    var valueGap = parseInt(graphicConfig.valueGap || 6, 10);
    var groupHeight = (barHeight * numGroupBars) + (barGapInner * (numGroupBars - 1)) + 20;

    var margins = {
        top: 0,
        right: 15,
        bottom: 20,
        left: (labelWidth + labelMargin)
    };

    if (graphicConfig.marginTop) {
        margins.top = parseInt(graphicConfig.marginTop, 10);
    }

    if (graphicConfig.marginRight) {
        margins.right = parseInt(graphicConfig.marginRight, 10);
    }

    if (graphicConfig.marginBottom) {
        margins.bottom = parseInt(graphicConfig.marginBottom, 10);
    }

    if (graphicConfig.marginLeft) {
        margins.left = parseInt(graphicConfig.marginLeft, 10);
    }

    var ticksX = parseInt(graphicConfig.ticksX || 7, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins['left'] - margins['right'];
    var chartHeight = (((((barHeight + barGapInner) * numGroupBars) - barGapInner) + barGap) * numGroups) - barGap + barGapInner;

    chartHeight = (groupHeight + 20) * (numGroups - 1);
    /*
     * Create D3 scale objects.
     */
    var min = d3.min(config['data'], function(d) {
        return d3.min(d['values'], function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in graphicConfig) {
        min = parseFloat(graphicConfig.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(config['data'], function(d) {
        return d3.max(d['values'], function(v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    })

    if ('maxX' in graphicConfig) {
        max = parseFloat(graphicConfig.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([
            min,
            max
        ])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(graphicConfig, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(config['data'][0]['values'], labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function(d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    };

    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(config['data'])
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function(d, i) {
                return makeTranslate(0, (groupHeight + barGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('rect')
            .attr('x', function(d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + (barGapInner * i);
            })
            .attr('width', function(d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .style('fill', function(d) {
            	return colorScale(d[labelColumn]);
            })
            .attr('class', function(d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(config['data'], function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label)
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            'width': labelWidth + 'px',
            'top': margins['top'] + 'px',
            'left': '0'
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / 2);
                var top = (groupHeight + barGap) * index;

                if (i & 1 === 1) {
                    top += barHeight + barGapInner;
                }

                if (i == 0) {
                    top = 0;
                }

                top += 20;

                return formatStyle({
                    'width': (labelWidth - 10) + 'px',
                    'height': barHeight + 'px',
                    'left': '0px',
                    'top': top + 'px;'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//['key']);
            })
            .append('span')
                .text(function(d) {
                    return d;//['key']
                });

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('text')
            .text(function(d) {
                return (graphicConfig.prefixX || '') + numFormat(d[valueColumn]) + (graphicConfig.suffixX || '');
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + barGapInner;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength()

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true)
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true)
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true)
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true)
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<commit_msg>* Highlight in grouped bar chart<commit_after>// Global config
var GRAPHIC_DEFAULT_WIDTH = 600;
var MOBILE_THRESHOLD = 500;

// Global vars
var pymChild = null;
var isMobile = false;
var graphicData = null;
var graphicConfig = null;
var numFormat = d3.format(",");

/*
 * Initialize the graphic.
 */
var onWindowLoaded = function() {
    if (Modernizr.svg) {
        graphicConfig = GRAPHIC_CONFIG;
        loadLocalData(GRAPHIC_DATA);
        //loadCSV('data.csv')
    } else {
        pymChild = new pym.Child({});
    }
}

/*
 * Load graphic data from a local source.
 */
var loadLocalData = function(data) {
    graphicData = data;

    formatData();

    pymChild = new pym.Child({
        renderCallback: render
    });
}

/*
 * Load graphic data from a CSV.
 */
var loadCSV = function(url) {
    d3.csv(GRAPHIC_DATA_URL, function(error, data) {
        graphicData = data;

        formatData();

        pymChild = new pym.Child({
            renderCallback: render
        });
    });
}

/*
 * Format graphic data for processing by D3.
 */
var formatData = function() {
    graphicData.forEach(function(d) {
        d['key'] = d['Group'];
        d['values'] = [];

        _.each(d, function(v, k) {
            if (_.contains(['Group', 'key', 'values'], k)) {
                return;
            }

            d['values'].push({ 'label': k, 'amt': +v });
            delete d[k];
        });

        delete d['Group'];
    });
}

/*
 * Render the graphic(s). Called by pym with the container width.
 */
var render = function(containerWidth) {
    if (!containerWidth) {
        containerWidth = GRAPHIC_DEFAULT_WIDTH;
    }

    if (containerWidth <= MOBILE_THRESHOLD) {
        isMobile = true;
    } else {
        isMobile = false;
    }

    // Render the chart!
    renderGroupedBarChart({
        container: '#graphic',
        width: containerWidth,
        data: graphicData
    });

    // Update iframe
    if (pymChild) {
        pymChild.sendHeight();
    }
}

/*
 * Render a bar chart.
 */
var renderGroupedBarChart = function(config) {
    /*
     * Setup chart container.
     */
    var labelColumn = 'label';
    var valueColumn = 'amt';

    var numGroups = config['data'].length;
    var numGroupBars = config['data'][0]['values'].length;

    var barHeight = parseInt(graphicConfig.barHeight || 25, 10);
    var barGapInner = parseInt(graphicConfig.barGap || 2, 10);
    var barGap = parseInt(graphicConfig.barGap || 10, 10);
    var labelWidth = parseInt(graphicConfig.labelWidth || 85, 10);
    var labelMargin = parseInt(graphicConfig.labelMargin || 6, 10);
    var valueGap = parseInt(graphicConfig.valueGap || 6, 10);
    var groupHeight = (barHeight * numGroupBars) + (barGapInner * (numGroupBars - 1)) + 30;

    var margins = {
        top: 0,
        right: 15,
        bottom: 20,
        left: (labelWidth + labelMargin)
    };

    if (graphicConfig.marginTop) {
        margins.top = parseInt(graphicConfig.marginTop, 10);
    }

    if (graphicConfig.marginRight) {
        margins.right = parseInt(graphicConfig.marginRight, 10);
    }

    if (graphicConfig.marginBottom) {
        margins.bottom = parseInt(graphicConfig.marginBottom, 10);
    }

    if (graphicConfig.marginLeft) {
        margins.left = parseInt(graphicConfig.marginLeft, 10);
    }

    var ticksX = parseInt(graphicConfig.ticksX || 7, 10);
    var roundTicksFactor = parseInt(graphicConfig.roundTicksFactor || 5, 10);

    // Clear existing graphic (for redraw)
    var containerElement = d3.select(config['container']);
    containerElement.html('');

    /*
     * Create the root SVG element.
     */
    var chartWrapper = containerElement.append('div')
        .attr('class', 'graphic-wrapper');

     // Calculate actual chart dimensions
    var innerWidth = chartWrapper.node().getBoundingClientRect().width;
    var chartWidth = innerWidth - margins['left'] - margins['right'];
    var chartHeight = (((((barHeight + barGapInner) * numGroupBars) - barGapInner) + barGap) * numGroups) - barGap + barGapInner;

    chartHeight = (groupHeight + 20) * (numGroups - 1);
    /*
     * Create D3 scale objects.
     */
    var min = d3.min(config['data'], function(d) {
        return d3.min(d['values'], function(v) {
            return Math.floor(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    });

    if ('minX' in graphicConfig) {
        min = parseFloat(graphicConfig.minX, 10);
    } else if (min > 0) {
        min = 0;
    }

    var max = d3.max(config['data'], function(d) {
        return d3.max(d['values'], function(v) {
            return Math.ceil(v[valueColumn] / roundTicksFactor) * roundTicksFactor;
        });
    })

    if ('maxX' in graphicConfig) {
        max = parseFloat(graphicConfig.maxX, 10);
    }

    var xScale = d3.scale.linear()
        .domain([
            min,
            max
        ])
        .range([0, chartWidth]);

    var yScale = d3.scale.linear()
        .range([chartHeight, 0]);

    var colorList = colorArray(graphicConfig, monochromeColors);
    var colorScale = d3.scale.ordinal()
        .domain(_.pluck(config['data'][0]['values'], labelColumn))
        .range(colorList);

    var chartElement = chartWrapper.append('svg')
        .attr('width', chartWidth + margins['left'] + margins['right'])
        .attr('height', chartHeight + margins['top'] + margins['bottom'])
        .append('g')
        .attr('transform', 'translate(' + margins['left'] + ',' + margins['top'] + ')');

    /*
     * Create D3 axes.
     */
    var xAxis = d3.svg.axis()
        .scale(xScale)
        .orient('bottom')
        .ticks(ticksX)
        .tickFormat(function(d) {
            return d.toFixed(0) + '%';
        });

    /*
     * Render axes to chart.
     */
    // chartElement.append('g')
    //     .attr('class', 'x axis')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxis);

    /*
     * Render grid to chart.
     */
    var xAxisGrid = function() {
        return xAxis;
    };

    // chartElement.append('g')
    //     .attr('class', 'x grid')
    //     .attr('transform', makeTranslate(0, chartHeight))
    //     .call(xAxisGrid()
    //         .tickSize(-chartHeight, 0, 0)
    //         .tickFormat('')
    //     );

    /*
     * Render bars to chart.
     */
    var barGroups = chartElement.selectAll('.bars')
        .data(config['data'])
        .enter()
        .append('g')
            .attr('class', 'g bars')
            .attr('transform', function(d, i) {
                return makeTranslate(0, (groupHeight + barGap) * i + 20);
            });

    barGroups.selectAll('rect')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('rect')
            .attr('x', function(d) {
                if (d[valueColumn] >= 0) {
                    return xScale(0);
                }

                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + (barGapInner * i);
            })
            .attr('width', function(d) {
                return Math.abs(xScale(0) - xScale(d[valueColumn]));
            })
            .attr('height', barHeight)
            .attr('fill', function(d) {
            	return colorScale(d[labelColumn]);
            })
            .attr('class', function(d) {
                return 'y-' + d[labelColumn];
            });

    barGroups.append('text')
            .attr('x', 0)
            .attr('y', -8)
            .attr('class', 'group-label')
            .text(function (d) { return d.key; });

    var labelData = [];
    _.each(config['data'], function (d) {
        _.each(d.values, function (e) {
            labelData.push(e.label)
        });
    });

    /*
     * Render bar labels.
     */
    chartWrapper.append('ul')
        .attr('class', 'labels')
        .attr('style', formatStyle({
            'width': labelWidth + 'px',
            'top': margins['top'] + 'px',
            'left': '0'
        }))
        .selectAll('li')
        .data(labelData)
        .enter()
        .append('li')
            .attr('style', function(d,i) {
                var index = Math.floor(i / 2);
                var top = (groupHeight + barGap) * index;

                if (i & 1 === 1) {
                    top += barHeight + barGapInner;
                }

                if (i == 0) {
                    top = 0;
                }

                top += 20;

                return formatStyle({
                    'width': (labelWidth - 10) + 'px',
                    'height': barHeight + 'px',
                    'left': '0px',
                    'top': top + 'px;'
                });
            })
            .attr('class', function(d,i) {
                return classify(d);//['key']);
            })
            .append('span')
                .text(function(d) {
                    return d;//['key']
                });

    if (graphicConfig.theme == "highlight") {
        chartWrapper.on("mousemove", function (e) {
            var pos = d3.mouse(chartWrapper.node())
            var gh = groupHeight + 10;
            var index = Math.floor(pos[1] / gh);
            var relativeY = pos[1] - index * gh;
            var barIndex = Math.floor((relativeY - 20) / (barHeight + barGapInner));

            chartWrapper.selectAll(".value text.over").classed('over', false);
            chartWrapper.selectAll(".bars rect").attr('fill', function (d, i) {
                return colorScale(i);
            });

            if (relativeY <= 20) {
                // in the group heading
                return;
            }

            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") rect:nth-child("+(barIndex+1)+")")
                .attr('fill', highlightColor);
            chartWrapper.selectAll(".bars:nth-child("+(index+1)+") .value text.out:nth-child("+(barIndex+1)+")")
                .classed('over', true);
        });
    }

    /*
     * Render bar values.
     */
    barGroups.append('g')
        .attr('class', 'value')
        .selectAll('text')
        .data(function(d) {
            return d['values'];
        })
        .enter()
        .append('text')
            .text(function(d) {
                return (graphicConfig.prefixX || '') + numFormat(d[valueColumn]) + (graphicConfig.suffixX || '');
            })
            .attr('x', function(d) {
                return xScale(d[valueColumn]);
            })
            .attr('y', function(d, i) {
                if (i == 0) {
                    return 0;
                }

                return (barHeight * i) + barGapInner;
            })
            .attr('dx', function(d) {
                var xStart = xScale(d[valueColumn]);
                var textWidth = this.getComputedTextLength()

                // Negative case
                if (d[valueColumn] < 0) {
                    var outsideOffset = -(valueGap + textWidth);

                    if (xStart + outsideOffset < 0) {
                        d3.select(this).classed('in', true)
                        return valueGap;
                    } else {
                        d3.select(this).classed('out', true)
                        return outsideOffset;
                    }
                // Positive case
                } else {
                    if (xStart + valueGap + textWidth > chartWidth) {
                        d3.select(this).classed('in', true)
                        return -(valueGap + textWidth);
                    } else {
                        d3.select(this).classed('out', true)
                        return valueGap;
                    }
                }
            })
            .attr('dy', (barHeight / 2) + 4);
}

/*
 * Initially load the graphic
 * (NB: Use window.load to ensure all images have loaded)
 */
window.onload = onWindowLoaded;
<|endoftext|>


<commit_before>const { insert, update } = require('../../../data/udify')
const ExposedError = require('../../../data/exposed-error')
const pageLayout = require('../queries/page-layout')

module.exports = async function updatePageLayout(obj, { pathname, input }, { log, state }) {
  // Find all component ids
  const [ results ] = await state.dbPool.execute('SELECT id FROM bm_web_page_layouts WHERE pathname = ? LIMIT 1',
    [ pathname ])

  if (!results.length) throw new ExposedError('Page Layout does not exist')

  const conn = await state.dbPool.getConnection()

  try {
    await conn.beginTransaction()

    const devices = Object.keys(input)
    const components = []

    devices.forEach(device => {
      // @TODO Validate component is allowed in this pathname
      input[device].forEach(({ id, component, x, y, w, textAlign, colour, meta }) => {
        const data = {
          pathname
        , device
        , component
        , x
        , y
        , w
        , textAlign: textAlign || null
        , colour: textAlign || null
        , meta: textAlign || null
        }

        if (id) data.id = id

        components.push(data)
      })
    })

    await Promise.each(components, (component) => {
      if (component.id) {
        return update(conn, 'bm_web_page_layouts', component, { id: component.id })
      }

      return insert(conn, 'bm_web_page_layouts', component)
    })

    await conn.commit()
  } catch (e) {
    log.error(e)

    if (!conn.connection._fatalError) {
      conn.rollback()
    }

    throw e
  } finally {
    conn.release()
  }

  return pageLayout(obj, { pathname }, { state })
}
<commit_msg>Fixed meta and colour page layout updates<commit_after>const { insert, update } = require('../../../data/udify')
const ExposedError = require('../../../data/exposed-error')
const pageLayout = require('../queries/page-layout')

module.exports = async function updatePageLayout(obj, { pathname, input }, { log, state }) {
  // Find all component ids
  const [ results ] = await state.dbPool.execute('SELECT id FROM bm_web_page_layouts WHERE pathname = ? LIMIT 1',
    [ pathname ])

  if (!results.length) throw new ExposedError('Page Layout does not exist')

  const conn = await state.dbPool.getConnection()

  try {
    await conn.beginTransaction()

    const devices = Object.keys(input)
    const components = []

    devices.forEach(device => {
      // @TODO Validate component is allowed in this pathname
      input[device].forEach(({ id, component, x, y, w, textAlign, colour, meta }) => {
        const data = {
          pathname
        , device
        , component
        , x
        , y
        , w
        , textAlign: textAlign || null
        , colour: colour || null
        , meta: meta || null
        }

        if (id) data.id = id

        components.push(data)
      })
    })

    await Promise.each(components, (component) => {
      if (component.id) {
        return update(conn, 'bm_web_page_layouts', component, { id: component.id })
      }

      return insert(conn, 'bm_web_page_layouts', component)
    })

    await conn.commit()
  } catch (e) {
    log.error(e)

    if (!conn.connection._fatalError) {
      conn.rollback()
    }

    throw e
  } finally {
    conn.release()
  }

  return pageLayout(obj, { pathname }, { state })
}
<|endoftext|>


<commit_before>import JSONAPIAdapter from 'ember-data/adapters/json-api';

export default JSONAPIAdapter.extend({
  host: 'https://api.twitch.tv',
  namespace: 'kraken',
  dataType: 'jsonp',

  dataForRequest() {
    const data = this._super(...arguments);
    return data || {};
  },

  /**
   * Currently, `ajaxOptions` is still the only place to alter
   * the `dataType` request parameter
   * @see: https://github.com/emberjs/data/pull/4357
   */
  ajaxOptions() {
    const hash = this._super(...arguments);

    hash.dataType = this.get('dataType');
    hash.traditional = true;

    return hash;
  }
});
<commit_msg>remove setting `traditional` param on ajax hash<commit_after>import JSONAPIAdapter from 'ember-data/adapters/json-api';

export default JSONAPIAdapter.extend({
  host: 'https://api.twitch.tv',
  namespace: 'kraken',
  dataType: 'jsonp',

  dataForRequest() {
    const data = this._super(...arguments);
    return data || {};
  },

  /**
   * Currently, `ajaxOptions` is still the only place to alter
   * the `dataType` request parameter
   * @see: https://github.com/emberjs/data/pull/4357
   */
  ajaxOptions() {
    const hash = this._super(...arguments);
    hash.dataType = this.get('dataType');

    return hash;
  }
});
<|endoftext|>


<commit_before>var config = require('config');
var convert = require('convert');
var debug = require('debug')(config.application() + ':route');
var model = require('model');
var defaults = require('model-defaults');
var session = require('session');

/**
 * MPS to MPH
 */

var MPS_TO_MPH = 2.23694;

/**
 * Expose `Route`
 */

var Route = module.exports = model('Route')
  .use(defaults({
    carData: false,
    transit: []
  }))
  .attr('id')
  .attr('access')
  .attr('bikeCalories')
  .attr('bikeDistance')
  .attr('bikeTime')
  .attr('carData')
  .attr('calories')
  .attr('cost')
  .attr('costDifference')
  .attr('driveDistance')
  .attr('egress')
  .attr('emissions')
  .attr('emissionsDifference')
  .attr('hasTransit')
  .attr('modes')
  .attr('pounds')
  .attr('score')
  .attr('stats')
  .attr('time')
  .attr('timeCost')
  .attr('transfers')
  .attr('transitCost')
  .attr('transit')
  .attr('trips')
  .attr('walkCalories')
  .attr('walkDistance')
  .attr('walkTime');

/**
 * Update scoring
 */

Route.prototype.rescore = function(scorer) {
  var data = scorer.processOption(this.toJSON());

  for (var i in data) {
    if (this.hasOwnProperty(i) && i !== 'transitCost') {
      this[i](data[i]);
    }
  }

  this.emit('change average', this.average());
  this.emit('change bikeTime', this.bikeTime());
  this.emit('change calculatedCost', this.calculatedCost());
  this.emit('change calculatedCalories', this.calculatedCalories());
  this.emit('change transitCosts', this.transitCosts());
  this.emit('change tripsPerYear', this.tripsPerYear());
  this.emit('change carParkingCost', this.carParkingCost());
  this.emit('change vmtRate', this.vmtRate());
  this.emit('change walkTime', this.walkTime());
};

/**
 * Set car data
 */

Route.prototype.setCarData = function(data) {
  var m = this.tripm();

  this.carData(true);
  this.costDifference(toFixed((data.cost * m - this.cost() * m) / 1000, 1));
  this.pounds(this.calories() * m / 3500 | 0);
  this.timeCost((m * (this.time() / 60 / 24)) | 0);
  this.emissionsDifference(data.emissions / 2000 | 0);
};

/**
 * Average trip length in minutes
 */

Route.prototype.average = function() {
  if (this.hasTransit() || this.modes().indexOf('car') === -1)
    return Math.round(this.time());
  return Math.round(this.time() * 1.35);
};

/**
 * Freeflow
 */

Route.prototype.freeflowTime = function() {
  if (this.hasTransit() || this.modes().indexOf('car') === -1)
    return false;
  return Math.round(this.time());
};

/**
 * Has transit?
 */

Route.prototype.hasTransit = function() {
  return this.transit().length > 0;
};

/**
 * Days
 */

Route.prototype.tripsPerYear = function() {
  return session.plan().tripsPerYear();
};

/**
 * Trip multiplier
 */

Route.prototype.tripm = function() {
  var plan = session.plan();
  return plan.per_year() ? plan.tripsPerYear() : 1;
};

/**
 * Cost
 */

Route.prototype.calculatedCost = function() {
  if (this.cost() === 0) return false;
  var cost = 0;
  if (this.transitCost()) cost += this.transitCost();
  if (this.modes().indexOf('car') !== -1) {
    cost += this.vmtRate() * this.driveDistances();
    cost += this.carParkingCost();
  }

  var total = cost * this.tripm();
  if (total > 1000) {
    return toFixed(total / 1000, 1) + 'k';
  } else if (total > 100) {
    return total | 0;
  } else {
    return total.toFixed(2);
  }
};

/**
 * Transit Cost
 */

Route.prototype.transitCosts = function() {
  if (!this.transitCost()) return false;
  return this.transitCost().toFixed(2);
};

/**
 * Calories
 */

Route.prototype.calculatedCalories = function() {
  if (this.calories() === 0) return false;

  var cals = walkingCaloriesBurned(this.walkSpeed(), this.weight(), (this.walkDistances() / this.walkSpeed()));
  if (this.modes().indexOf('bicycle') !== -1) {
    cals += bikingCaloriesBurned(this.bikeSpeed(), this.weight(), (this.bikeDistances() / this.bikeSpeed()));
  }
  var total = cals * this.tripm();
  return total > 1000 ? toFixed(total / 1000, 1) + 'k' : total | 0;
};

/**
 * Frequency
 */

Route.prototype.frequency = function() {
  var trips = this.trips();
  if (!trips) return false;

  var plan = session.plan();
  var start = plan.start_time();
  var end = plan.end_time();

  return Math.round(60 / (trips / (end - start)));
};

/**
 * Walk/Bike distances rounded
 */

Route.prototype.driveDistances = function() {
  return this.distances('car', 'driveDistance');
};

Route.prototype.bikeDistances = function() {
  return this.distances('bicycle', 'bikeDistance');
};

Route.prototype.walkDistances = function() {
  return this.distances('walk', 'walkDistance');
};

Route.prototype.distances = function(mode, val) {
  if (this.modes().indexOf(mode) === -1) return false;
  return convert.metersToMiles(this[val]());
};

/**
 * Walk/bike speed in MPH
 */

Route.prototype.bikeSpeedMph = function() {
  return toFixed(this.bikeSpeed() * MPS_TO_MPH, 1);
};

Route.prototype.walkSpeedMph = function() {
  return toFixed(this.walkSpeed() * MPS_TO_MPH, 1);
};

/**
 * Walk/bike time in minutes
 */

Route.prototype.bikeTime = function() {
  var d = this.bikeDistance();
  var s = this.bikeSpeed();
  var t = d / s;

  if (t < 60) return '< 1';
  else return parseInt(t / 60);
};

Route.prototype.walkTime = function() {
  var d = this.walkDistance();
  var s = this.walkSpeed();
  var t = d / s;

  if (t < 60) return '< 1';
  else return parseInt(t / 60);
};

/**
 * Retrieve from scorer
 */

Route.prototype.bikeSpeed = function() {
  return session.plan().scorer().rates.bikeSpeed;
};

Route.prototype.walkSpeed = function() {
  return session.plan().scorer().rates.walkSpeed;
};

Route.prototype.vmtRate = function() {
  return session.plan().scorer().rates.mileageRate;
};

Route.prototype.weight = function() {
  return session.plan().scorer().rates.weight;
};

Route.prototype.carParkingCost = function() {
  return session.plan().scorer().rates.carParkingCost;
};

/**
 * Walking Calories
 *
 * CB = [0.0215 x KPH3 - 0.1765 x KPH2 + 0.8710 x KPH + 1.4577] x WKG x T
 * http://www.shapesense.com/fitness-exercise/calculators/walking-calorie-burn-calculator.aspx
 */

function walkingCaloriesBurned(mps, wkg, hours) {
  var kph = mps / 1000 * 60 * 60;
  var kph2 = kph * kph;
  var kph3 = kph2 * kph;
  return (0.0215 * kph3 - 0.1765 * kph2 + 0.8710 * kph) * wkg * hours;
}

/**
 * Biking Calories
 *
 * http://en.wikipedia.org/wiki/Bicycle_performance
 */

var GRADE = 1;
var GRAVITY = 9.8;
var K1 = 0.0053; // frictional losses
var K2 = 0.185; // aerodynamic drag
var WATTS_TO_CALS_PER_SECOND = 0.2388;

function bikingCaloriesBurned(mps, wkg, hours) {
  var mps3 = Math.pow(mps, 3);
  var seconds = hours * 60 * 60;
  var watts = GRAVITY * wkg * mps * (K1 + GRADE) + K2 * mps3;
  return watts * WATTS_TO_CALS_PER_SECOND * seconds;
}

function toFixed(n, f) {
  var m = Math.pow(10, f);
  return ((n * m) | 0) / m;
}
<commit_msg>Remove reference to per_year<commit_after>var config = require('config');
var convert = require('convert');
var debug = require('debug')(config.application() + ':route');
var model = require('model');
var defaults = require('model-defaults');
var session = require('session');

/**
 * MPS to MPH
 */

var MPS_TO_MPH = 2.23694;

/**
 * Expose `Route`
 */

var Route = module.exports = model('Route')
  .use(defaults({
    carData: false,
    transit: []
  }))
  .attr('id')
  .attr('access')
  .attr('bikeCalories')
  .attr('bikeDistance')
  .attr('bikeTime')
  .attr('carData')
  .attr('calories')
  .attr('cost')
  .attr('costDifference')
  .attr('driveDistance')
  .attr('egress')
  .attr('emissions')
  .attr('emissionsDifference')
  .attr('hasTransit')
  .attr('modes')
  .attr('pounds')
  .attr('score')
  .attr('stats')
  .attr('time')
  .attr('timeCost')
  .attr('transfers')
  .attr('transitCost')
  .attr('transit')
  .attr('trips')
  .attr('walkCalories')
  .attr('walkDistance')
  .attr('walkTime');

/**
 * Update scoring
 */

Route.prototype.rescore = function(scorer) {
  var data = scorer.processOption(this.toJSON());

  for (var i in data) {
    if (this.hasOwnProperty(i) && i !== 'transitCost') {
      this[i](data[i]);
    }
  }

  this.emit('change average', this.average());
  this.emit('change bikeTime', this.bikeTime());
  this.emit('change calculatedCost', this.calculatedCost());
  this.emit('change calculatedCalories', this.calculatedCalories());
  this.emit('change transitCosts', this.transitCosts());
  this.emit('change tripsPerYear', this.tripsPerYear());
  this.emit('change carParkingCost', this.carParkingCost());
  this.emit('change vmtRate', this.vmtRate());
  this.emit('change walkTime', this.walkTime());
};

/**
 * Set car data
 */

Route.prototype.setCarData = function(data) {
  var m = this.tripm();

  this.carData(true);
  this.costDifference(toFixed((data.cost * m - this.cost() * m) / 1000, 1));
  this.pounds(this.calories() * m / 3500 | 0);
  this.timeCost((m * (this.time() / 60 / 24)) | 0);
  this.emissionsDifference(data.emissions / 2000 | 0);
};

/**
 * Average trip length in minutes
 */

Route.prototype.average = function() {
  if (this.hasTransit() || this.modes().indexOf('car') === -1)
    return Math.round(this.time());
  return Math.round(this.time() * 1.35);
};

/**
 * Freeflow
 */

Route.prototype.freeflowTime = function() {
  if (this.hasTransit() || this.modes().indexOf('car') === -1)
    return false;
  return Math.round(this.time());
};

/**
 * Has transit?
 */

Route.prototype.hasTransit = function() {
  return this.transit().length > 0;
};

/**
 * Days
 */

Route.prototype.tripsPerYear = function() {
  return session.plan().tripsPerYear();
};

/**
 * Trip multiplier
 */

Route.prototype.tripm = function() {
  return session.plan().tripsPerYear();
};

/**
 * Cost
 */

Route.prototype.calculatedCost = function() {
  if (this.cost() === 0) return false;
  var cost = 0;
  if (this.transitCost()) cost += this.transitCost();
  if (this.modes().indexOf('car') !== -1) {
    cost += this.vmtRate() * this.driveDistances();
    cost += this.carParkingCost();
  }

  var total = cost * this.tripm();
  if (total > 1000) {
    return toFixed(total / 1000, 1) + 'k';
  } else if (total > 100) {
    return total | 0;
  } else {
    return total.toFixed(2);
  }
};

/**
 * Transit Cost
 */

Route.prototype.transitCosts = function() {
  if (!this.transitCost()) return false;
  return this.transitCost().toFixed(2);
};

/**
 * Calories
 */

Route.prototype.calculatedCalories = function() {
  if (this.calories() === 0) return false;

  var cals = walkingCaloriesBurned(this.walkSpeed(), this.weight(), (this.walkDistances() / this.walkSpeed()));
  if (this.modes().indexOf('bicycle') !== -1) {
    cals += bikingCaloriesBurned(this.bikeSpeed(), this.weight(), (this.bikeDistances() / this.bikeSpeed()));
  }
  var total = cals * this.tripm();
  return total > 1000 ? toFixed(total / 1000, 1) + 'k' : total | 0;
};

/**
 * Frequency
 */

Route.prototype.frequency = function() {
  var trips = this.trips();
  if (!trips) return false;

  var plan = session.plan();
  var start = plan.start_time();
  var end = plan.end_time();

  return Math.round(60 / (trips / (end - start)));
};

/**
 * Walk/Bike distances rounded
 */

Route.prototype.driveDistances = function() {
  return this.distances('car', 'driveDistance');
};

Route.prototype.bikeDistances = function() {
  return this.distances('bicycle', 'bikeDistance');
};

Route.prototype.walkDistances = function() {
  return this.distances('walk', 'walkDistance');
};

Route.prototype.distances = function(mode, val) {
  if (this.modes().indexOf(mode) === -1) return false;
  return convert.metersToMiles(this[val]());
};

/**
 * Walk/bike speed in MPH
 */

Route.prototype.bikeSpeedMph = function() {
  return toFixed(this.bikeSpeed() * MPS_TO_MPH, 1);
};

Route.prototype.walkSpeedMph = function() {
  return toFixed(this.walkSpeed() * MPS_TO_MPH, 1);
};

/**
 * Walk/bike time in minutes
 */

Route.prototype.bikeTime = function() {
  var d = this.bikeDistance();
  var s = this.bikeSpeed();
  var t = d / s;

  if (t < 60) return '< 1';
  else return parseInt(t / 60);
};

Route.prototype.walkTime = function() {
  var d = this.walkDistance();
  var s = this.walkSpeed();
  var t = d / s;

  if (t < 60) return '< 1';
  else return parseInt(t / 60);
};

/**
 * Retrieve from scorer
 */

Route.prototype.bikeSpeed = function() {
  return session.plan().scorer().rates.bikeSpeed;
};

Route.prototype.walkSpeed = function() {
  return session.plan().scorer().rates.walkSpeed;
};

Route.prototype.vmtRate = function() {
  return session.plan().scorer().rates.mileageRate;
};

Route.prototype.weight = function() {
  return session.plan().scorer().rates.weight;
};

Route.prototype.carParkingCost = function() {
  return session.plan().scorer().rates.carParkingCost;
};

/**
 * Walking Calories
 *
 * CB = [0.0215 x KPH3 - 0.1765 x KPH2 + 0.8710 x KPH + 1.4577] x WKG x T
 * http://www.shapesense.com/fitness-exercise/calculators/walking-calorie-burn-calculator.aspx
 */

function walkingCaloriesBurned(mps, wkg, hours) {
  var kph = mps / 1000 * 60 * 60;
  var kph2 = kph * kph;
  var kph3 = kph2 * kph;
  return (0.0215 * kph3 - 0.1765 * kph2 + 0.8710 * kph) * wkg * hours;
}

/**
 * Biking Calories
 *
 * http://en.wikipedia.org/wiki/Bicycle_performance
 */

var GRADE = 1;
var GRAVITY = 9.8;
var K1 = 0.0053; // frictional losses
var K2 = 0.185; // aerodynamic drag
var WATTS_TO_CALS_PER_SECOND = 0.2388;

function bikingCaloriesBurned(mps, wkg, hours) {
  var mps3 = Math.pow(mps, 3);
  var seconds = hours * 60 * 60;
  var watts = GRAVITY * wkg * mps * (K1 + GRADE) + K2 * mps3;
  return watts * WATTS_TO_CALS_PER_SECOND * seconds;
}

function toFixed(n, f) {
  var m = Math.pow(10, f);
  return ((n * m) | 0) / m;
}
<|endoftext|>


<commit_before>import $ from 'jquery';
import { isPresent } from '@ember/utils';
import Mixin from '@ember/object/mixin';
import { observer } from '@ember/object';
import { schedule, bind } from '@ember/runloop';

function onRenderObserver(/*keys..., fn*/) {
  var args = Array.prototype.slice.call(arguments);
  var fn = args.slice(-1)[0];
  var keys = args.slice(0, -1);

  var observerFunction = function() {
    if (this._state !== 'inDOM') {
      // don't schedule unless inDOM
      return;
    }
    schedule('render', this, function() {
      // don't run unless still inDOM
      if (this._state === 'inDOM') {
        fn.call(this);
      }
    });
  };

  return observer.apply(null, keys.concat([observerFunction]));
}

export default Mixin.create( {
  colpickLayout: 'hex',
  colorScheme: 'dark',
  classNames: [ 'ember-col-pick' ],
  flat: true, // [true/false] render as popup (true) rendering inline (false)
  value: null,
  previewValue: null,
  useHashtag: false,

  _colpick: undefined,

  configDidChange: onRenderObserver('colorScheme', 'colpickLayout', 'flat', function(){
    this._tearDownColpick();
    this.rerender();
  }),

  valueDidChange: onRenderObserver('value', function() {
    var value = this.get('value');
    if (this._colpick && value) {
      this._colpick.colpickSetColor(value);
    }
  }),

  _setupColpick: function() {
    var layout = this.get('colpickLayout');
    var colorScheme = this.get('colorScheme');

    if (layout && colorScheme) {
      var colpick = this._colpick = this.$().colpick({
        layout: layout,
        colorScheme: colorScheme,
        submit: 0,
        flat: this.get('flat'),
        onChange: bind(this, function(hsb, hex) {
          if (this.get('useHashtag')) {
            hex = '#' + hex;
          }

          this.set('previewValue', hex);

          if (this._isValidPreviewValue()) {
            this.set('value', hex);
          }
        }),
        onHide: bind(this, function(){
          // eslint-disable-next-line ember/closure-actions
          this.sendAction('onHide');
        })
      });

      colpick.find('input[type=text]').keyup(function() {
        var hexInputVal = this.value;
        if (hexInputVal.length === 6) {
          colpick.colpickSetColor(hexInputVal);
        }
      });

      var value = this.get('value');
      if (value) {
        colpick.colpickSetColor(value);
      }
    }
  },

  _isValidPreviewValue: function() {
    var previewHex = this.get('previewValue');
    var validityRegex = this.get('useHashtag') ? /^#[a-f0-9]{6}$/i : /^[a-f0-9]{6}$/i;
    return isPresent(previewHex.match(validityRegex));
  },

  popup: function() {
    if (this._state === 'inDOM') {
      return $('#' + this.$().data('colpickId'));
    }
  },

  didInsertElement: function () {
    this._super();
    this._setupColpick();
  },

  _tearDownColpick: function() {
    if (this._colpick) {
      this.popup().remove();
      this._colpick = undefined;
    }
  },

  willDestroyElement: function () {
    this._tearDownColpick();
    this._super();
  }
});

<commit_msg>Fix case where trying to remove element but component is being destroyed<commit_after>import $ from 'jquery';
import { isPresent } from '@ember/utils';
import Mixin from '@ember/object/mixin';
import { observer } from '@ember/object';
import { schedule, bind } from '@ember/runloop';

function onRenderObserver(/*keys..., fn*/) {
  var args = Array.prototype.slice.call(arguments);
  var fn = args.slice(-1)[0];
  var keys = args.slice(0, -1);

  var observerFunction = function() {
    if (this._state !== 'inDOM') {
      // don't schedule unless inDOM
      return;
    }
    schedule('render', this, function() {
      // don't run unless still inDOM
      if (this._state === 'inDOM') {
        fn.call(this);
      }
    });
  };

  return observer.apply(null, keys.concat([observerFunction]));
}

export default Mixin.create( {
  colpickLayout: 'hex',
  colorScheme: 'dark',
  classNames: [ 'ember-col-pick' ],
  flat: true, // [true/false] render as popup (true) rendering inline (false)
  value: null,
  previewValue: null,
  useHashtag: false,

  _colpick: undefined,

  configDidChange: onRenderObserver('colorScheme', 'colpickLayout', 'flat', function(){
    this._tearDownColpick();
    this.rerender();
  }),

  valueDidChange: onRenderObserver('value', function() {
    var value = this.get('value');
    if (this._colpick && value) {
      this._colpick.colpickSetColor(value);
    }
  }),

  _setupColpick: function() {
    var layout = this.get('colpickLayout');
    var colorScheme = this.get('colorScheme');

    if (layout && colorScheme) {
      var colpick = this._colpick = this.$().colpick({
        layout: layout,
        colorScheme: colorScheme,
        submit: 0,
        flat: this.get('flat'),
        onChange: bind(this, function(hsb, hex) {
          if (this.get('useHashtag')) {
            hex = '#' + hex;
          }

          this.set('previewValue', hex);

          if (this._isValidPreviewValue()) {
            this.set('value', hex);
          }
        }),
        onHide: bind(this, function(){
          // eslint-disable-next-line ember/closure-actions
          this.sendAction('onHide');
        })
      });

      colpick.find('input[type=text]').keyup(function() {
        var hexInputVal = this.value;
        if (hexInputVal.length === 6) {
          colpick.colpickSetColor(hexInputVal);
        }
      });

      var value = this.get('value');
      if (value) {
        colpick.colpickSetColor(value);
      }
    }
  },

  _isValidPreviewValue: function() {
    var previewHex = this.get('previewValue');
    var validityRegex = this.get('useHashtag') ? /^#[a-f0-9]{6}$/i : /^[a-f0-9]{6}$/i;
    return isPresent(previewHex.match(validityRegex));
  },

  popup: function() {
    if (this._state === 'inDOM') {
      return $('#' + this.$().data('colpickId'));
    }
  },

  didInsertElement: function () {
    this._super();
    this._setupColpick();
  },

  _tearDownColpick: function() {
    if (this._colpick) {
      if (!this.isDestroying) {
        this.popup().remove();
      }
      this._colpick = undefined;
    }
  },

  willDestroyElement: function () {
    this._tearDownColpick();
    this._super();
  }
});

<|endoftext|>


<commit_before>React = require('react');

// Centralized display for all components
var DisplayBox = React.createClass({
  // Sets initial state properties to empty arrays to avoid undefined errors
  getInitialState: function() {
    return {
      amazon: {amazon: []},
      walmart: {walmart: []},
      bestbuy: {bestBuy: []},
    };
  },

  // Called when user submits a query
  handleQuerySubmit: function(query) {
    $.ajax({
      url: 'general-query',
      dataType: 'json',
      type: 'POST',
      data: query,
      success: function(data) {
        // Show Related Results after user submits query
        $('.related-results-display').removeClass('hidden');

        // Set the state to contain data for each separate API
        this.setState({
          amazon: data[0],
          walmart: data[1],
          bestbuy: data[2]
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('general-query', status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    return (

        <SearchForm onQuerySubmit={this.handleQuerySubmit} />

        <AmazonRelatedResultsDisplay data={this.state.amazon} />
        <WalmartRelatedResultsDisplay data={this.state.walmart} />
        <BestbuyRelatedResultsDisplay data={this.state.bestbuy} />

    );
  }
});

// Component for the query-submit form
var SearchForm = React.createClass({
  handleSubmit: function(e) {
    // Prevent page from reloading on submit
    e.preventDefault();

    // Grab query content from "ref" in input box
    var query = React.findDOMNode(this.refs.query).value.trim();

    // Passes the query to the central DisplayBox component
    // DisplayBox will make AJAX call and display results
    this.props.onQuerySubmit({query: query});

    // Clear the input box after submit
    React.findDOMNode(this.refs.query).value = '';
  },
  render: function() {
    return (

ShopChimp, at your service.

        <form className="query-form" onSubmit={this.handleSubmit}>
          <input type="text" placeholder="Enter a product" className="form-control" ref="query" />

          <button className="btn btn-primary">Submit</button>
        </form>

    );
  }
});

// Component that displays related results from Amazon API
var AmazonRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.amazon.map(function(result, index) {
      return (
        result
      );
    });
    return (

Amazon Related Results

        {resultNodes}

    );
  }
});

// Component that displays related results from Walmart API
var WalmartRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.walmart.map(function(result, index) {
      return (
        result
      );
    });
    return (

Walmart Related Results

        {resultNodes}

    );
  }
});

// Component that displays related results from Best Buy API
var BestbuyRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.bestBuy.map(function(result, index) {
      return (
        result
      );
    });
    return (

Best Buy Related Results

        {resultNodes}

    );
  }
});

// Home page container for the DisplayBox component
var Home = React.createClass({
	render: function() {
		return (

        <DisplayBox />

		);
	}
});

module.exports = Home;<commit_msg>(feat) start working on individual display components<commit_after>React = require('react');

// Centralized display for all components
var DisplayBox = React.createClass({
  // Sets initial state properties to empty arrays to avoid undefined errors
  getInitialState: function() {
    return {
      amazon: {amazon: []},
      walmart: {walmart: []},
      bestbuy: {bestBuy: []},
    };
  },

  // Called when user submits a query
  handleQuerySubmit: function(query) {
    $.ajax({
      url: 'general-query',
      dataType: 'json',
      type: 'POST',
      data: query,
      success: function(data) {
        // Show Related Results after user submits query
        $('.related-results-display').removeClass('hidden');

        // Set the state to contain data for each separate API
        this.setState({
          amazon: data[0],
          walmart: data[1],
          bestbuy: data[2]
        });
      }.bind(this),
      error: function(xhr, status, err) {
        console.error('general-query', status, err.toString());
      }.bind(this)
    });
  },
  render: function() {
    return (

        <SearchForm onQuerySubmit={this.handleQuerySubmit} />

        <AmazonRelatedResultsDisplay data={this.state.amazon} />
        <WalmartRelatedResultsDisplay data={this.state.walmart} />
        <BestbuyRelatedResultsDisplay data={this.state.bestbuy} />

    );
  }
});

// Component for the query-submit form
var SearchForm = React.createClass({
  handleSubmit: function(e) {
    // Prevent page from reloading on submit
    e.preventDefault();

    // Grab query content from "ref" in input box
    var query = React.findDOMNode(this.refs.query).value.trim();

    // Passes the query to the central DisplayBox component
    // DisplayBox will make AJAX call and display results
    this.props.onQuerySubmit({query: query});

    // Clear the input box after submit
    React.findDOMNode(this.refs.query).value = '';
  },
  render: function() {
    return (

ShopChimp, at your service.

        <form className="query-form" onSubmit={this.handleSubmit}>
          <input type="text" placeholder="Enter a product" className="form-control" ref="query" />

          <button className="btn btn-primary">Submit</button>
        </form>

    );
  }
});

// Component that displays related results from Amazon API
var AmazonRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.amazon.map(function(result, index) {
      return (
        result
      );
    });
    return (

Amazon Related Results

        {resultNodes}

    );
  }
});

// Component that displays related results from Walmart API
var WalmartRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.walmart.map(function(result, index) {
      return (
        <WalmartIndividualResultDisplay name={result.name} />
      );
    });
    return (

Walmart Related Results

        {resultNodes}

    );
  }
});

var WalmartIndividualResultDisplay = React.createClass({
  render: function() {
    return (

this.props.name

    );
  }
});

// Component that displays related results from Best Buy API
var BestbuyRelatedResultsDisplay = React.createClass({
  render: function() {
    var resultNodes = this.props.data.bestBuy.map(function(result, index) {
      return (
        result
      );
    });
    return (

Best Buy Related Results

        {resultNodes}

    );
  }
});

// Home page container for the DisplayBox component
var Home = React.createClass({
	render: function() {
		return (

        <DisplayBox />

		);
	}
});

module.exports = Home;<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('ember-kalendae', 'Integration | Component | ember kalendae', {
  integration: true
});

test('it allows block usage', function(assert) {
  assert.expect(1);

  this.render(hbs`
    {{#ember-kalendae}}
      template block text
    {{/ember-kalendae}}
  `);
  assert.ok(this.$().text().trim().indexOf('template block text') >= 0);
});

test('it initialises correctly without any config', function(assert) {
  assert.expect(1);

  this.set('kalendae', null);
  this.render(hbs`{{ember-kalendae instanceAPI=(action (mut kalendae))}}`);

  // it should initialise to today
  assert.equal(
    this.get('kalendae').getSelected(),
    new Date().toISOString().slice(0,10)
  );
});

test('it passes mode through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('mode', 'range');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      mode=mode
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.mode,
    this.get('mode')
  );
});

test('it passes months through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('months', '2');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      months=months
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.months,
    this.get('months')
  );
});

test('it passes weekStart through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('weekStart', '2');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      weekStart=weekStart
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.weekStart,
    this.get('weekStart')
  );
});

test('it passes direction through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('direction', 'past');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      direction=direction
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.direction,
    this.get('direction')
  );
});

test('it passes directionScrolling through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('directionScrolling', false);

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      directionScrolling=directionScrolling
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.directionScrolling,
    this.get('directionScrolling')
  );
});

test('it passes blackout through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('blackout', '20170725T132629Z');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      blackout=blackout
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.blackout,
    this.get('blackout')
  );
});

test('it passes viewStartDate through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('viewStartDate', '20170725T132629Z');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      viewStartDate=viewStartDate
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.viewStartDate,
    this.get('viewStartDate')
  );
});

test('it passes dateClassMap through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('dateClassMap', { foo: 'bar' });

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      dateClassMap=dateClassMap
    }}`
  );

  assert.deepEqual(
    this.get('kalendae').settings.dateClassMap,
    this.get('dateClassMap')
  );
});

test('it passes dayOutOfMonthClickable through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('dayOutOfMonthClickable', true);

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      dayOutOfMonthClickable=dayOutOfMonthClickable
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.dayOutOfMonthClickable,
    this.get('dayOutOfMonthClickable')
  );
});

<commit_msg>=it passes it passes dayHeaderClickable through to kalendae correctly<commit_after>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('ember-kalendae', 'Integration | Component | ember kalendae', {
  integration: true
});

test('it allows block usage', function(assert) {
  assert.expect(1);

  this.render(hbs`
    {{#ember-kalendae}}
      template block text
    {{/ember-kalendae}}
  `);
  assert.ok(this.$().text().trim().indexOf('template block text') >= 0);
});

test('it initialises correctly without any config', function(assert) {
  assert.expect(1);

  this.set('kalendae', null);
  this.render(hbs`{{ember-kalendae instanceAPI=(action (mut kalendae))}}`);

  // it should initialise to today
  assert.equal(
    this.get('kalendae').getSelected(),
    new Date().toISOString().slice(0,10)
  );
});

test('it passes mode through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('mode', 'range');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      mode=mode
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.mode,
    this.get('mode')
  );
});

test('it passes months through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('months', '2');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      months=months
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.months,
    this.get('months')
  );
});

test('it passes weekStart through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('weekStart', '2');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      weekStart=weekStart
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.weekStart,
    this.get('weekStart')
  );
});

test('it passes direction through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('direction', 'past');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      direction=direction
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.direction,
    this.get('direction')
  );
});

test('it passes directionScrolling through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('directionScrolling', false);

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      directionScrolling=directionScrolling
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.directionScrolling,
    this.get('directionScrolling')
  );
});

test('it passes blackout through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('blackout', '20170725T132629Z');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      blackout=blackout
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.blackout,
    this.get('blackout')
  );
});

test('it passes viewStartDate through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('viewStartDate', '20170725T132629Z');

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      viewStartDate=viewStartDate
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.viewStartDate,
    this.get('viewStartDate')
  );
});

test('it passes dateClassMap through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('dateClassMap', { foo: 'bar' });

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      dateClassMap=dateClassMap
    }}`
  );

  assert.deepEqual(
    this.get('kalendae').settings.dateClassMap,
    this.get('dateClassMap')
  );
});

test('it passes dayOutOfMonthClickable through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('dayOutOfMonthClickable', true);

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      dayOutOfMonthClickable=dayOutOfMonthClickable
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.dayOutOfMonthClickable,
    this.get('dayOutOfMonthClickable')
  );
});

test('it passes dayHeaderClickable through to kalendae correctly', function(assert) {
  assert.expect(1);

  this.set('dayHeaderClickable', true);

  this.render(hbs`
    {{ember-kalendae
      instanceAPI=(action (mut kalendae))
      dayHeaderClickable=dayHeaderClickable
    }}`
  );

  assert.equal(
    this.get('kalendae').settings.dayHeaderClickable,
    this.get('dayHeaderClickable')
  );
});

<|endoftext|>


<commit_before>import config from 'ember-get-config';
import Ember from 'ember';

/**
* Queries ElasticSearch to get mappings.
*
* @class EsMapper
* @constructor
*/
class EsMapper {

  constructor () {
    this.host = config.EsAdapter.host;
    this.namespace = config.EsAdapter.namespace;
  }

  /**
  * Gets the mapping from ElasticSearch in form of array.
  *
  * @method getMapping
  * @private
  * @return {Object} Object containing arrays of mappings.
  * e.g. {'post':[{mapping:'body', type:'string'}]}
  */
  getMapping() {
    if (!this.host || !this.namespace) {
      console.log('[EsMapper][getMapping]: Host or Namespace not defined.');
      return false;
    }

    let host = this.host,
        namespace = this.namespace;

    return new Ember.RSVP.Promise(function(resolve, reject) {
      fetch(host + '/'+ namespace + '/_mappings')
        .then((resp) => {
          let r = resp.json(),
              types = {};

          r.then((result) => {
              for (var i in result) {
                if (result[i]['mappings']) {
                  let _types = result[i]['mappings'];

                  for (var j in _types) {
                    if (_types[j]) {
                      let _properties = _types[j]['properties'];
                      let _propKeys = Object.keys(_properties);

                      types[j] = [];

                      for (var k=0; k < _propKeys.length; k++) {
                        types[j].push({
                          'mapping': _propKeys[k],
                          'type': _properties[_propKeys[k]].type
                        });
                      }

                      resolve(types);
                    }
                    else {
                      reject('[EsMapper][getMapping]: No Types Found.');
                    }
                  }
                }
                else {
                  reject('[EsMapper][getMapping]: No Mappings Found.');
                }
              }
            });

          });

    });
  }

  getTrue() {
    return true;
  }
}

export default EsMapper;
<commit_msg>added ability to get last {field} of es index. useful for getting an ID to create new content<commit_after>import config from 'ember-get-config';
import Ember from 'ember';

/**
* ElasticSearch Tools for interfacing
*
* @class EsTools
* @constructor
*/
class EsTools {

  constructor () {
    this.host = config.EsAdapter.host;
    this.namespace = config.EsAdapter.namespace;
  }

  /**
  * Gets the mapping from ElasticSearch in form of array.
  *
  * @method getMapping
  * @private
  * @return {Object} Object containing arrays of mappings.
  * e.g. {'post':[{mapping:'body', type:'string'}]}
  */
  getMapping() {
    if (!this.host || !this.namespace) {
      console.log('[EsMapper][getMapping]: Host or Namespace not defined.');
      return false;
    }

    let host = this.host,
        namespace = this.namespace;

    return new Ember.RSVP.Promise(function(resolve, reject) {
      fetch(host + '/'+ namespace + '/_mappings')
        .then((resp) => {
          let r = resp.json(),
              types = {};

          r.then((result) => {
              for (var i in result) {
                if (result[i]['mappings']) {
                  let _types = result[i]['mappings'];

                  for (var j in _types) {
                    if (_types[j]) {
                      let _properties = _types[j]['properties'];
                      let _propKeys = Object.keys(_properties);

                      types[j] = [];

                      for (var k=0; k < _propKeys.length; k++) {
                        types[j].push({
                          'mapping': _propKeys[k],
                          'type': _properties[_propKeys[k]].type
                        });
                      }

                      resolve(types);
                    }
                    else {
                      reject('[EsMapper][getMapping]: No Types Found.');
                    }
                  }
                }
                else {
                  reject('[EsMapper][getMapping]: No Mappings Found.');
                }
              }
            });

          });

    });
  }

  getTrue() {
    return true;
  }

  getLastId(index,field) {
    let host = this.host,
        namespace = this.namespace,
        queryString = "_search?query=match_all&size=1&fields=" + field + "&sort=" + field + ":desc",
        url = [host, namespace, index, queryString].join('/');

    return new Ember.RSVP.Promise(function(resolve, reject) {
      fetch(url)
        .then((resp) => {
          resp.json()
            .then((json) => {
              if (json && json.hits.hits.length) {
                resolve(parseInt(json.hits.hits[0]._id));
              }
              else {
                reject('[EsMapper][getLastId]: No Response.');
              }
            });
        });
    });
  }

}
export default EsTools;
<|endoftext|>


<commit_before>import Ember from 'ember';

const {
  assert,
  on
  } = Ember;

export default function subscribe(path, method) {
  let [service, event, err] = path.split('.');
  let _listener = null;

  assert(`'subscribe()' expects a path with exactly one leaf, given ${path}`, event && service && !err);

  let computedFn = function() {
    if (!this.get(service) || _listener) {
      return;
    }

    // ensure teardown
    let _super = this.get('willDestroy');
    this.set('willDestroy', function() {
      this.get(service).off(event, _listener);
      _listener = null;
      computedFn = null;
      _super();
    });

    // proxy the event
    _listener = (e) => {
      method.call(this, e);
    };

    // subscribe to the event
    this.get(service).on(event, _listener);
  };

  return on.call(this, 'init', computedFn);
}
<commit_msg>fix context for super in subscribe<commit_after>import Ember from 'ember';

const {
  assert,
  on
  } = Ember;

export default function subscribe(path, method) {
  let [service, event, err] = path.split('.');
  let _listener = null;

  assert(`'subscribe()' expects a path with exactly one leaf, given ${path}`, event && service && !err);

  let computedFn = function() {
    if (!this.get(service) || _listener) {
      return;
    }

    // ensure teardown
    let _super = this.get('willDestroy');
    this.set('willDestroy', function() {
      this.get(service).off(event, _listener);
      _listener = null;
      computedFn = null;
      _super.call(this);
    });

    // proxy the event
    _listener = (e) => {
      method.call(this, e);
    };

    // subscribe to the event
    this.get(service).on(event, _listener);
  };

  return on.call(this, 'init', computedFn);
}
<|endoftext|>


<commit_before>
/* Contains all source code modules assigned to RG. */

const RG = require('./rg.js');

RG.Verify = require('./verify.js');

// No deps to RG.SubElems
RG.Random = require('./random.js');
RG.Mixin = require('./mixin.js');
RG.Geometry = require('./geometry.js');
RG.Object = require('./object.js');
RG.Time = require('./time.js');
RG.Template = require('./template.js');
RG.Animation = require('./animation.js');

// Minor deps to other exports
RG.Component = require('./component.js');
RG.Item = require('./item.js');
RG.Spell = require('./spell.js');
RG.System = require('./system.js');
RG.BTree = require('./aisequence.js');
RG.Brain = require('./brain.js');
RG.Inv = require('./inv.js');
RG.Actor = require('./actor.js');
RG.Element = require('./element.js');

RG.Map = require('./map.js');
RG.World = require('./world.js');

RG.Effects = require('../data/effects.js');
RG.ObjectShell = require('./objectshellparser.js');

RG.Game = require('./game.js');
RG.Factory = require('./factory.js');

RG.Names = require('../data/name-gen.js');
RG.LevelGen = require('../data/level-gen.js');
RG.Overworld = require('./overworld.js');

RG.Game.FromJSON = require('./game.fromjson.js');

RG.Factory.Game = require('./factory.game.js');

module.exports = RG;

<commit_msg>Added OW.<commit_after>
/* Contains all source code modules assigned to RG. */

const RG = require('./rg.js');

RG.Verify = require('./verify.js');

// No deps to RG.SubElems
RG.Random = require('./random.js');
RG.Mixin = require('./mixin.js');
RG.Geometry = require('./geometry.js');
RG.Object = require('./object.js');
RG.Time = require('./time.js');
RG.Template = require('./template.js');
RG.Animation = require('./animation.js');

// Minor deps to other exports
RG.Component = require('./component.js');
RG.Item = require('./item.js');
RG.Spell = require('./spell.js');
RG.System = require('./system.js');
RG.BTree = require('./aisequence.js');
RG.Brain = require('./brain.js');
RG.Inv = require('./inv.js');
RG.Actor = require('./actor.js');
RG.Element = require('./element.js');

RG.Map = require('./map.js');
RG.World = require('./world.js');

RG.Effects = require('../data/effects.js');
RG.ObjectShell = require('./objectshellparser.js');

RG.Game = require('./game.js');
RG.Factory = require('./factory.js');

RG.Names = require('../data/name-gen.js');
RG.LevelGen = require('../data/level-gen.js');

RG.OW = require('./overworld.map');
RG.Overworld = require('./overworld.js');

RG.Game.FromJSON = require('./game.fromjson.js');

RG.Factory.Game = require('./factory.game.js');

module.exports = RG;

<|endoftext|>