<commit_before>/*

Copyright 2015 Stefano Terna

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

'use strict';

/**
 * @ngdoc overview
 * @name consoleApp
 * @description
 * # consoleApp
 *
 * Main module of the application.
 */
angular
  .module('consoleApp', [
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch',
    'ui.codemirror',
  ]).value('API_URL', 'http://127.0.0.1:8550/')
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl',
        controllerAs: 'main'
      })
      .when('/about', {
        templateUrl: 'views/about.html',
        controller: 'AboutCtrl',
        controllerAs: 'about'
      })
      .when('/messages', {
        templateUrl: 'views/messages.html',
        controller: 'MessagesCtrl',
        controllerAs: 'messagesCtrl'
      })
      .when('/devicecode', {
        templateUrl: 'views/devicecode.html',
        controller: 'DevicecodeCtrl',
        controllerAs: 'codeCtrl'
      })
      .when('/deviceconsole', {
        templateUrl: 'views/deviceconsole.html',
        controller: 'DeviceconsoleCtrl',
        controllerAs: 'deviceCtrl'
      })
      .when('/project', {
        templateUrl: 'views/project.html',
        controller: 'ProjectCtrl',
        controllerAs: 'projectCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });

angular
  .module('consoleApp').controller('AppCtrl', function ($scope, $rootScope, $location, httpRequestService, websocketService, projectService) {
        //this.tab = 0;
    websocketService.init();

    // $rootScope.project = {
    //   'name': 'LEGO motor remote control',
    //   'board':'Raspberry PI',
    //   'fwlanguage':'Python',
    //   'boards': [
    //     {
    //       'name':'My Board 1',
    //       'ID': 'jn9wn6x0f6r'
    //     },
    //     {
    //       'name':'My Board 2',
    //       'ID': 't57wh281tt9'
    //     }
    //   ],
    //   'messages': [
    //     {
    //       'type': 'operate motor',
    //       'description': 'operates the motor clockwise',
    //       'keys':[
    //         {
    //           'key':'motor_number',
    //           'value': 1
    //         },
    //         {
    //           'key':'direction',
    //           'value': 'right'
    //         }
    //       ]
    //     },
    //     {
    //       'type': 'operate motor',
    //       'description': 'operates the motor counter clockwise',
    //       'keys':[
    //         {
    //           'key':'motor_number',
    //           'value': 1
    //         },
    //         {
    //           'key':'direction',
    //           'value': 'left'
    //         }
    //       ]
    //     }
    //   ]
    // }

    $scope.project = projectService.project;

    this.getSectionFromId = function(secId){
      switch(secId){
        case 0:
          return 'project';
        case 1:
          return 'messages';
        case 2:
          return 'devicecode';
        case 3:
          return 'deviceconsole';
      }
    };
    this.setTab = function(value){
      this.tab = value;
      $location.path(this.getSectionFromId(value) )
    };

    this.editCode = function(codeCtrl){
      this.setTab(2);
      $rootScope.projectSourceCode = this.projecttoCode();
      //$rootScope.editor.getDoc().setValue();

      //codeCtrl.code = this.projecttoCode();
      //TODO inserire il timeout nel controller
      setTimeout(function(){
        $rootScope.editor.refresh();
      }, 100);

    }

    function uniqueBy(arr, fn) {
      var unique = {};
      var distinct = [];
      arr.forEach(function (x) {
        var key = fn(x);
        if (!unique[key]) {
          distinct.push(x);
          unique[key] = true;
        }
      });
      return distinct;
    }

    this.projecttoCode = function() {
      var code = '#  Kindly written by IOTTLY for you on ' + (new Date).toISOString();
      code = code + '\n';
      code = code + '\n' + '#  Project: ' + $scope.project.data.name;
      code = code + '\n' + '#  Board: ' + $scope.project.data.board;
      code = code + '\n';

      code = code + '\n' + 'import iottly             #The IOTTLY module';
      code = code + '\n' + 'import RPi.GPIO as GPIO   #The GPIO module'

      code = code + '\n';

      var uniquemsgs = uniqueBy($scope.project.data.messages, function(x){ return x.typetag; });

      uniquemsgs.forEach(function(element, index, array){
        code = code + '\n' + '#  This function will be called every time';
        code = code + '\n' + '#  the board receives the following command: '+ element.type;
        code = code + '\n' + 'def ' +  element.typetag + '(command):';
        code = code + '\n';
        code = code + '\n' + '  ' + '#  command is a dictionary with the following example structure:'
        code = code + '\n' + '  ' + '#    ' + messagetoJSON(element);
        code = code + '\n' + '  ' + 'payload = command["' + element.typetag + '"]';

        code = code + '\n';

        code = code + '\n' + '  ' + '#  this event is sent back to IOTTLY'
        code = code + '\n' + '  ' + '#  if you see it on Device Admin Console, all is going right with communication!'
        var echo = JSON.parse(messagetoJSON(element));
        echo.ECHO = 1;
        code = code + '\n' + '  ' + '#    ' + JSON.stringify(echo);

        code = code + '\n' + '  ' + 'iottly.sendECHOevent(command)';

        code = code + '\n';
        code = code + '\n' + '  ' + '#  Now it is time to write your useful functions, here:';
        code = code + '\n' + '  ' + '#  for example:';
        code = code + '\n' + '  ' + '#  GPIO.output(<set here desired GPIO>,False)';

        code = code + '\n';

      });

      return code;
    };

    this.isSet = function(value){
      return this.tab === value;
    };

    this.checkMessages = function(){
      return $scope.project.data.messages.length === 0;
    };

  }); <commit_msg>fixed lost tab 0 at startup<commit_after>/*

Copyright 2015 Stefano Terna

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

*/

'use strict';

/**
 * @ngdoc overview
 * @name consoleApp
 * @description
 * # consoleApp
 *
 * Main module of the application.
 */
angular
  .module('consoleApp', [
    'ngAnimate',
    'ngCookies',
    'ngResource',
    'ngRoute',
    'ngSanitize',
    'ngTouch',
    'ui.codemirror',
  ]).value('API_URL', 'http://127.0.0.1:8550/')
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl',
        controllerAs: 'main'
      })
      .when('/about', {
        templateUrl: 'views/about.html',
        controller: 'AboutCtrl',
        controllerAs: 'about'
      })
      .when('/messages', {
        templateUrl: 'views/messages.html',
        controller: 'MessagesCtrl',
        controllerAs: 'messagesCtrl'
      })
      .when('/devicecode', {
        templateUrl: 'views/devicecode.html',
        controller: 'DevicecodeCtrl',
        controllerAs: 'codeCtrl'
      })
      .when('/deviceconsole', {
        templateUrl: 'views/deviceconsole.html',
        controller: 'DeviceconsoleCtrl',
        controllerAs: 'deviceCtrl'
      })
      .when('/project', {
        templateUrl: 'views/project.html',
        controller: 'ProjectCtrl',
        controllerAs: 'projectCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });

angular
  .module('consoleApp').controller('AppCtrl', function ($scope, $rootScope, $location, httpRequestService, websocketService, projectService) {
        //this.tab = 0;
    websocketService.init();

    // $rootScope.project = {
    //   'name': 'LEGO motor remote control',
    //   'board':'Raspberry PI',
    //   'fwlanguage':'Python',
    //   'boards': [
    //     {
    //       'name':'My Board 1',
    //       'ID': 'jn9wn6x0f6r'
    //     },
    //     {
    //       'name':'My Board 2',
    //       'ID': 't57wh281tt9'
    //     }
    //   ],
    //   'messages': [
    //     {
    //       'type': 'operate motor',
    //       'description': 'operates the motor clockwise',
    //       'keys':[
    //         {
    //           'key':'motor_number',
    //           'value': 1
    //         },
    //         {
    //           'key':'direction',
    //           'value': 'right'
    //         }
    //       ]
    //     },
    //     {
    //       'type': 'operate motor',
    //       'description': 'operates the motor counter clockwise',
    //       'keys':[
    //         {
    //           'key':'motor_number',
    //           'value': 1
    //         },
    //         {
    //           'key':'direction',
    //           'value': 'left'
    //         }
    //       ]
    //     }
    //   ]
    // }

    $scope.project = projectService.project;

    this.getSectionFromId = function(secId){
      switch(secId){
        case 0:
          return 'project';
        case 1:
          return 'messages';
        case 2:
          return 'devicecode';
        case 3:
          return 'deviceconsole';
      }
    };
    this.setTab = function(value){
      this.tab = value;
      $location.path(this.getSectionFromId(value) )
    };

    this.editCode = function(codeCtrl){
      this.setTab(2);
      $rootScope.projectSourceCode = this.projecttoCode();
      //$rootScope.editor.getDoc().setValue();

      //codeCtrl.code = this.projecttoCode();
      //TODO inserire il timeout nel controller
      setTimeout(function(){
        $rootScope.editor.refresh();
      }, 100);

    }

    function uniqueBy(arr, fn) {
      var unique = {};
      var distinct = [];
      arr.forEach(function (x) {
        var key = fn(x);
        if (!unique[key]) {
          distinct.push(x);
          unique[key] = true;
        }
      });
      return distinct;
    }

    this.projecttoCode = function() {
      var code = '#  Kindly written by IOTTLY for you on ' + (new Date).toISOString();
      code = code + '\n';
      code = code + '\n' + '#  Project: ' + $scope.project.data.name;
      code = code + '\n' + '#  Board: ' + $scope.project.data.board;
      code = code + '\n';

      code = code + '\n' + 'import iottly             #The IOTTLY module';
      code = code + '\n' + 'import RPi.GPIO as GPIO   #The GPIO module'

      code = code + '\n';

      var uniquemsgs = uniqueBy($scope.project.data.messages, function(x){ return x.typetag; });

      uniquemsgs.forEach(function(element, index, array){
        code = code + '\n' + '#  This function will be called every time';
        code = code + '\n' + '#  the board receives the following command: '+ element.type;
        code = code + '\n' + 'def ' +  element.typetag + '(command):';
        code = code + '\n';
        code = code + '\n' + '  ' + '#  command is a dictionary with the following example structure:'
        code = code + '\n' + '  ' + '#    ' + messagetoJSON(element);
        code = code + '\n' + '  ' + 'payload = command["' + element.typetag + '"]';

        code = code + '\n';

        code = code + '\n' + '  ' + '#  this event is sent back to IOTTLY'
        code = code + '\n' + '  ' + '#  if you see it on Device Admin Console, all is going right with communication!'
        var echo = JSON.parse(messagetoJSON(element));
        echo.ECHO = 1;
        code = code + '\n' + '  ' + '#    ' + JSON.stringify(echo);

        code = code + '\n' + '  ' + 'iottly.sendECHOevent(command)';

        code = code + '\n';
        code = code + '\n' + '  ' + '#  Now it is time to write your useful functions, here:';
        code = code + '\n' + '  ' + '#  for example:';
        code = code + '\n' + '  ' + '#  GPIO.output(<set here desired GPIO>,False)';

        code = code + '\n';

      });

      return code;
    };

    this.isSet = function(value){
      return this.tab === value;
    };

    this.checkMessages = function(){
      return $scope.project.data.messages.length === 0;
    };

    this.setTab(0);
  }); <|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commi<commit_msg>What do you mean you can't pack up the commit?<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds", length, commit_count, commit<|endoftext|>


<commit_before>"use strict";

var d3 = require('d3'),
    nv = require('../../shim/nv.d3.js'),
    $ = require('jquery'),
    _ = require('lodash');

var widthCutoff = 400;

// Make jQuery handle destroyed event.
// http://stackoverflow.com/questions/2200494/
// jquery-trigger-event-when-an-element-is-removed-from-the-dom
(function($) {
    $.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };
})($);

// When we replace a chart with a new one, the tooltip for the old chart
// persists because it resides under the body tag instead of under
// chartEl (the container div for the chart) like the other chart components.
// Therefore, we manually remove the tooltip when elements under chartEl are
// destroyed.
function removeTooltipOnDestroy(chartEl, tooltip) {
    $(chartEl).children().bind('destroyed', function() {
        $('#' + tooltip.id()).remove();
    });
}

function makeSvg(el) {
    // For some reason, the chart will only render if the style is
    // defined inline, even if it is blank.
    var svg = $('<svg style=""></svg>').get(0);
    $(el).empty();
    $(svg).appendTo(el);
    return svg;
}

function handleCommonOptions(chart, options) {
    if (options.yAxisLabel) {
        chart.yAxis.axisLabel(options.yAxisLabel);
    }
    if (options.isPercentage) {
        chart.yAxis.tickFormat(d3.format('.0%'));
    }
    if (options.abbreviateTicks) {
        chart.yAxis.tickFormat(d3.format('.2s'));
    }
}

function getNumBars(data) {
    return data[0].values.length;
}

/*
    Renders a horizontal bar chart for a single series of data without a legend.

    data is of the form
    [
        {
            x: ...,
            y: ...
        },
        ...
    ]
    note that x corresponds to the vertical axis since this is a horizontal bar
    chart.

    options includes: barClasses, margin, yAxisLabel, isPercentage, maxBarHeight
    and abbreviateTicks
*/
function renderHorizontalBarChart(chartEl, data, options) {
    var chart = nv.models.multiBarHorizontalChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    // Augment data so that it is part of a single series to match
    // the format expected by NVD3.
    data = [
        {
          key: 'Series 1',
          values: data
        }
    ];

    // The colors and barColors methods on chart do not
    // support the behavior we want.
    function addBarClasses() {
        var bars = $(chartEl).find('.nv-bar'),
            oldClass,
            newClass;

        _.each(bars, function(bar, i) {
            // Can't use addClass on SVG elements.
            oldClass = $(bar).attr('class');
            newClass = oldClass + ' ' + options.barClasses[i];
            $(bar).attr('class', newClass);
        });
    }

    function setChartHeight() {
        // Set chart height to ensure that bars (and their padding)
        // are no taller than maxBarHeight.
        var numBars = getNumBars(data),
            maxHeight = options.margin.top + options.margin.bottom +
                        numBars * options.maxBarHeight,
            actualHeight = $svg.height();

        if (actualHeight > maxHeight) {
            chart.height(maxHeight);
        } else {
            chart.height(actualHeight);
        }
    }

    function updateChart() {
        var width = $svg.width(),
            availableWidth = width - (options.margin.left + options.margin.right),
            minTickWidth = 60,
            yticks = Math.floor(availableWidth / minTickWidth);

        if($svg.is(':visible')) {
            setChartHeight();
            chart.yAxis.ticks(Math.min(yticks, 5));
            chart.update(); // Throws error if updating a hidden svg.
            if (options.barClasses) {
                addBarClasses();
            }
        }
    }

    options = options || {};
    _.defaults(options, {
        margin: {top: 30, right: 30, bottom: 40, left: 210},
        maxBarHeight: 150
    });

    nv.addGraph(function() {
        chart.showLegend(false)
             .showControls(false)
             .duration(0)
             .margin(options.margin);

        setChartHeight();
        chart.tooltip.enabled(false);
        chart.yAxis.ticks(5);
        handleCommonOptions(chart, options);

        d3.select(svg)
            .datum(data)
            .call(chart);

        if (options.barClasses) {
            addBarClasses();
        }

        nv.utils.windowResize(updateChart);
        // The bar-chart:refresh event occurs when switching tabs which requires
        // redrawing the chart.
        $(chartEl).on('bar-chart:refresh', updateChart);

        // This isn't strictly necessary since tooltips aren't enabled in the
        // horizontal bar chart, but it's here defensively in case we start
        // using them.
        removeTooltipOnDestroy(chartEl, chart.tooltip);

        return chart;
    });
}

/*
    Renders a stacked vertical bar chart for multiple series of data
    with a legend.

    data is of the form
    [
        {
            key: series-name,
            values: [
                {
                    x: ...,
                    y: ...
                },
                ...
            ]
        },
        ...
   ]
   where a series corresponds to a group of data that will be
   displayed with the same color/legend item. Eg. Runoff

   options includes: margin, yAxisLabel, yAxisUnit, seriesColors,
   isPercentage, maxBarWidth, abbreviateTicks, reverseLegend, and
   disableToggle
*/
function renderVerticalBarChart(chartEl, data, options) {
    var chart = nv.models.multiBarChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    function setChartWidth() {
        // Set chart width to ensure that bars (and their padding)
        // are no wider than maxBarWidth.
        var numBars = getNumBars(data),
            maxWidth = options.margin.left + options.margin.right +
                       numBars * options.maxBarWidth,
            actualWidth = $svg.width();

        if (actualWidth > maxWidth) {
           chart.width(maxWidth);
        } else {
           chart.width(actualWidth);
        }
    }

    function updateChart() {
        if($svg.is(':visible')) {
            setChartWidth();
            chart
                .staggerLabels($svg.width() < widthCutoff)
                .update(); // Throws error if updating a hidden svg.
        }
    }

    options = options || {};
    _.defaults(options, {
        margin: {top: 20, right: 30, bottom: 40, left: 60},
        maxBarWidth: 150
    });

    nv.addGraph(function() {
        chart.showLegend(true)
             .showControls(false)
             .stacked(true)
             .reduceXTicks(false)
             .staggerLabels($svg.width() < widthCutoff)
             .duration(0)
             .margin(options.margin);

        setChartWidth();
        // Throws error if this is not set to false for unknown reasons.
        chart.legend
            .disableToggle(options.disableToggle)
            .reverse(options.reverseLegend)
            .rightAlign(false);
        chart.tooltip.enabled(true);
        chart.yAxis.ticks(5);
        handleCommonOptions(chart, options);

        if (options.yAxisUnit) {
            chart.tooltip.valueFormatter(function(d) {
                return chart.yAxis.tickFormat()(d) + ' ' + options.yAxisUnit;
            });
        }
        if (options.seriesColors) {
            chart.color(options.seriesColors);
        }

        d3.select(svg)
            .datum(data)
            .call(chart);

        nv.utils.windowResize(updateChart);
        // The bar-chart:refresh event occurs when switching tabs which requires
        // redrawing the chart.
        $(chartEl).on('bar-chart:refresh', updateChart);

        removeTooltipOnDestroy(chartEl, chart.tooltip);

        return chart;
    });
}

// data is same format as for renderVerticalBarChart
function renderLineChart(chartEl, data, options) {
    var chart = nv.models.lineChart(),
        svg = makeSvg(chartEl);

    options = options || {};
    _.defaults(options, {
        margin: {top: 20, right: 30, bottom: 40, left: 60}
    });

    nv.addGraph(function() {
        chart.showLegend(false)
            .margin(options.margin);

        chart.xAxis
            .tickValues(options.xTickValues)
            .tickFormat(function(month) {
                return options.xAxisLabel(month);
            });

        chart.yAxis
            .axisLabel(options.yAxisLabel)
            .tickFormat(d3.format('.02f'));

        chart.tooltip.valueFormatter(function(d) {
            return chart.yAxis.tickFormat()(d) + ' ' + options.yAxisUnit;
        });

        handleCommonOptions(chart, options);

        d3.select(svg)
            .datum(data)
            .call(chart);

        return chart;
    });
}

function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax, data, columnWidth, xAxisWidth, callback) {
    var options = {
            margin: {
                top: 20,
                bottom: 20,
                left: 60,
            },
        },
        onRenderComplete = (callback) ? callback : _.noop,
        yTickFormat = stacked ? '0.1f' : yFormat(),
        chart = nv.models.multiBarChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    function setChartWidth() {
        var scenarioCount = _.size(_.head(data).values),
            scenariosWidth = scenarioCount * columnWidth + xAxisWidth;

        chartEl.style.width = scenariosWidth + "px";
        chart.width(chartEl.offsetWidth);
    }

    function yFormat() {
        var getYs = function(d) { return _.map(d.values, 'y'); },
            nonZero = function(x) { return x > 0; },
            ys = _(data).map(getYs).flatten().filter(nonZero).value(),
            minY = Math.min.apply(null, ys);

        if (minY > 1) {
            return '0.1f';
        }

        // Count decimal places to most significant digit, up to 4
        for (var i = 0; minY < 1 && i < 4; i++) {
            minY *= 10;
        }

        return '0.0' + i + 'f';
    }

    nv.addGraph(function() {
        chart.showLegend(false)
             .showControls(false)
             .stacked(stacked)
             .reduceXTicks(false)
             .staggerLabels($svg.width() < widthCutoff)
             .duration(0)
             .margin(options.margin)
             .color(colors)
             .showXAxis(false)
             .id(name);

        chart.yAxis
             .axisLabel(label)
             .tickFormat(d3.format(yTickFormat))
             .showMaxMin(false);

        chart.tooltip.enabled(true);

        setChartWidth();

        if (yMax !== null) {
            chart.yDomain([0, yMax]);
        }

        d3.select(svg)
          .datum(data)
          .call(chart);

        return chart;
    }, onRenderComplete);
}

module.exports = {
    renderHorizontalBarChart: renderHorizontalBarChart,
    renderVerticalBarChart: renderVerticalBarChart,
    renderLineChart: renderLineChart,
    renderCompareMultibarChart: renderCompareMultibarChart
};
<commit_msg>Show all values in a series on mouse hover<commit_after>"use strict";

var d3 = require('d3'),
    nv = require('../../shim/nv.d3.js'),
    $ = require('jquery'),
    _ = require('lodash');

var widthCutoff = 400;

// Make jQuery handle destroyed event.
// http://stackoverflow.com/questions/2200494/
// jquery-trigger-event-when-an-element-is-removed-from-the-dom
(function($) {
    $.event.special.destroyed = {
        remove: function(o) {
            if (o.handler) {
                o.handler();
            }
        }
    };
})($);

// When we replace a chart with a new one, the tooltip for the old chart
// persists because it resides under the body tag instead of under
// chartEl (the container div for the chart) like the other chart components.
// Therefore, we manually remove the tooltip when elements under chartEl are
// destroyed.
function removeTooltipOnDestroy(chartEl, tooltip) {
    $(chartEl).children().bind('destroyed', function() {
        $('#' + tooltip.id()).remove();
    });
}

function makeSvg(el) {
    // For some reason, the chart will only render if the style is
    // defined inline, even if it is blank.
    var svg = $('<svg style=""></svg>').get(0);
    $(el).empty();
    $(svg).appendTo(el);
    return svg;
}

function handleCommonOptions(chart, options) {
    if (options.yAxisLabel) {
        chart.yAxis.axisLabel(options.yAxisLabel);
    }
    if (options.isPercentage) {
        chart.yAxis.tickFormat(d3.format('.0%'));
    }
    if (options.abbreviateTicks) {
        chart.yAxis.tickFormat(d3.format('.2s'));
    }
}

function getNumBars(data) {
    return data[0].values.length;
}

/*
    Renders a horizontal bar chart for a single series of data without a legend.

    data is of the form
    [
        {
            x: ...,
            y: ...
        },
        ...
    ]
    note that x corresponds to the vertical axis since this is a horizontal bar
    chart.

    options includes: barClasses, margin, yAxisLabel, isPercentage, maxBarHeight
    and abbreviateTicks
*/
function renderHorizontalBarChart(chartEl, data, options) {
    var chart = nv.models.multiBarHorizontalChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    // Augment data so that it is part of a single series to match
    // the format expected by NVD3.
    data = [
        {
          key: 'Series 1',
          values: data
        }
    ];

    // The colors and barColors methods on chart do not
    // support the behavior we want.
    function addBarClasses() {
        var bars = $(chartEl).find('.nv-bar'),
            oldClass,
            newClass;

        _.each(bars, function(bar, i) {
            // Can't use addClass on SVG elements.
            oldClass = $(bar).attr('class');
            newClass = oldClass + ' ' + options.barClasses[i];
            $(bar).attr('class', newClass);
        });
    }

    function setChartHeight() {
        // Set chart height to ensure that bars (and their padding)
        // are no taller than maxBarHeight.
        var numBars = getNumBars(data),
            maxHeight = options.margin.top + options.margin.bottom +
                        numBars * options.maxBarHeight,
            actualHeight = $svg.height();

        if (actualHeight > maxHeight) {
            chart.height(maxHeight);
        } else {
            chart.height(actualHeight);
        }
    }

    function updateChart() {
        var width = $svg.width(),
            availableWidth = width - (options.margin.left + options.margin.right),
            minTickWidth = 60,
            yticks = Math.floor(availableWidth / minTickWidth);

        if($svg.is(':visible')) {
            setChartHeight();
            chart.yAxis.ticks(Math.min(yticks, 5));
            chart.update(); // Throws error if updating a hidden svg.
            if (options.barClasses) {
                addBarClasses();
            }
        }
    }

    options = options || {};
    _.defaults(options, {
        margin: {top: 30, right: 30, bottom: 40, left: 210},
        maxBarHeight: 150
    });

    nv.addGraph(function() {
        chart.showLegend(false)
             .showControls(false)
             .duration(0)
             .margin(options.margin);

        setChartHeight();
        chart.tooltip.enabled(false);
        chart.yAxis.ticks(5);
        handleCommonOptions(chart, options);

        d3.select(svg)
            .datum(data)
            .call(chart);

        if (options.barClasses) {
            addBarClasses();
        }

        nv.utils.windowResize(updateChart);
        // The bar-chart:refresh event occurs when switching tabs which requires
        // redrawing the chart.
        $(chartEl).on('bar-chart:refresh', updateChart);

        // This isn't strictly necessary since tooltips aren't enabled in the
        // horizontal bar chart, but it's here defensively in case we start
        // using them.
        removeTooltipOnDestroy(chartEl, chart.tooltip);

        return chart;
    });
}

/*
    Renders a stacked vertical bar chart for multiple series of data
    with a legend.

    data is of the form
    [
        {
            key: series-name,
            values: [
                {
                    x: ...,
                    y: ...
                },
                ...
            ]
        },
        ...
   ]
   where a series corresponds to a group of data that will be
   displayed with the same color/legend item. Eg. Runoff

   options includes: margin, yAxisLabel, yAxisUnit, seriesColors,
   isPercentage, maxBarWidth, abbreviateTicks, reverseLegend, and
   disableToggle
*/
function renderVerticalBarChart(chartEl, data, options) {
    var chart = nv.models.multiBarChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    function setChartWidth() {
        // Set chart width to ensure that bars (and their padding)
        // are no wider than maxBarWidth.
        var numBars = getNumBars(data),
            maxWidth = options.margin.left + options.margin.right +
                       numBars * options.maxBarWidth,
            actualWidth = $svg.width();

        if (actualWidth > maxWidth) {
           chart.width(maxWidth);
        } else {
           chart.width(actualWidth);
        }
    }

    function updateChart() {
        if($svg.is(':visible')) {
            setChartWidth();
            chart
                .staggerLabels($svg.width() < widthCutoff)
                .update(); // Throws error if updating a hidden svg.
        }
    }

    options = options || {};
    _.defaults(options, {
        margin: {top: 20, right: 30, bottom: 40, left: 60},
        maxBarWidth: 150
    });

    nv.addGraph(function() {
        chart.showLegend(true)
             .showControls(false)
             .stacked(true)
             .reduceXTicks(false)
             .staggerLabels($svg.width() < widthCutoff)
             .duration(0)
             .margin(options.margin);

        setChartWidth();
        // Throws error if this is not set to false for unknown reasons.
        chart.legend
            .disableToggle(options.disableToggle)
            .reverse(options.reverseLegend)
            .rightAlign(false);
        chart.tooltip.enabled(true);
        chart.yAxis.ticks(5);
        handleCommonOptions(chart, options);

        if (options.yAxisUnit) {
            chart.tooltip.valueFormatter(function(d) {
                return chart.yAxis.tickFormat()(d) + ' ' + options.yAxisUnit;
            });
        }
        if (options.seriesColors) {
            chart.color(options.seriesColors);
        }

        d3.select(svg)
            .datum(data)
            .call(chart);

        nv.utils.windowResize(updateChart);
        // The bar-chart:refresh event occurs when switching tabs which requires
        // redrawing the chart.
        $(chartEl).on('bar-chart:refresh', updateChart);

        removeTooltipOnDestroy(chartEl, chart.tooltip);

        return chart;
    });
}

// data is same format as for renderVerticalBarChart
function renderLineChart(chartEl, data, options) {
    var chart = nv.models.lineChart(),
        svg = makeSvg(chartEl);

    options = options || {};
    _.defaults(options, {
        margin: {top: 20, right: 30, bottom: 40, left: 60}
    });

    nv.addGraph(function() {
        chart.showLegend(false)
            .margin(options.margin);

        chart.xAxis
            .tickValues(options.xTickValues)
            .tickFormat(function(month) {
                return options.xAxisLabel(month);
            });

        chart.yAxis
            .axisLabel(options.yAxisLabel)
            .tickFormat(d3.format('.02f'));

        chart.tooltip.valueFormatter(function(d) {
            return chart.yAxis.tickFormat()(d) + ' ' + options.yAxisUnit;
        });

        handleCommonOptions(chart, options);

        d3.select(svg)
            .datum(data)
            .call(chart);

        return chart;
    });
}

function renderCompareMultibarChart(chartEl, name, label, colors, stacked, yMax, data, columnWidth, xAxisWidth, callback) {
    var options = {
            margin: {
                top: 20,
                bottom: 20,
                left: 60,
            },
        },
        onRenderComplete = (callback) ? callback : _.noop,
        yTickFormat = stacked ? '0.1f' : yFormat(),
        chart = nv.models.multiBarChart(),
        svg = makeSvg(chartEl),
        $svg = $(svg);

    function setChartWidth() {
        var scenarioCount = _.size(_.head(data).values),
            scenariosWidth = scenarioCount * columnWidth + xAxisWidth;

        chartEl.style.width = scenariosWidth + "px";
        chart.width(chartEl.offsetWidth);
    }

    function yFormat() {
        var getYs = function(d) { return _.map(d.values, 'y'); },
            nonZero = function(x) { return x > 0; },
            ys = _(data).map(getYs).flatten().filter(nonZero).value(),
            minY = Math.min.apply(null, ys);

        if (minY > 1) {
            return '0.1f';
        }

        // Count decimal places to most significant digit, up to 4
        for (var i = 0; minY < 1 && i < 4; i++) {
            minY *= 10;
        }

        return '0.0' + i + 'f';
    }

    nv.addGraph(function() {
        chart.showLegend(false)
             .showControls(false)
             .stacked(stacked)
             .reduceXTicks(false)
             .staggerLabels($svg.width() < widthCutoff)
             .duration(0)
             .margin(options.margin)
             .color(colors)
             .showXAxis(false)
             .id(name);

        chart.yAxis
             .axisLabel(label)
             .tickFormat(d3.format(yTickFormat))
             .showMaxMin(false);

        chart.tooltip.enabled(true);

        setChartWidth();

        if (yMax !== null) {
            chart.yDomain([0, yMax]);
        }

        d3.select(svg)
            .datum(data)
            .call(chart);

        // filter definition for drop shadows
        var filter =
            d3.select(svg)
                .selectAll("defs")
                .append("filter")
                .attr("id","drop-shadow")
                .attr("height", "120%");

        filter.append("feGaussianBlur")
            .attr("in", "SourceAlpha")
            .attr("stdDeviation", 2)
            .attr("result", "blur");

        filter.append("feOffset")
            .attr("in", "blur")
            .attr("dx", 2)
            .attr("dy", 2)
            .attr("result", "offsetBlur");

        var feMerge = filter.append("feMerge");
        feMerge.append("feMergeNode")
            .attr("in", "offsetBlur");
        feMerge.append("feMergeNode")
            .attr("in", "SourceGraphic");

        var chartContainerId = chartEl.id + "-chart-container";

        // iterate over the series names to add the svg elements to the chart
        _.forEach(["nv-series-0", "nv-series-1", "nv-series-2"],
            function(el) {
                d3.select("#" + chartEl.id).select("svg").selectAll("g." + el).each(function() {
                    var g = d3.select(this);
                    var parentG = d3.select(this.parentNode);
                    parentG.attr("id", chartContainerId);

                    g.selectAll(".nv-bar").each(function(bar) {
                        var b = d3.select(this);
                        var barWidth = b.attr("width");
                        var bgWidth = 40.0;
                        var bgHeight = 20.0;
                        var bgVertOffset = -20.0;

                        var dialogTickSize = 5.0;

                        // add drop-shadow
                        parentG.append("rect")
                            .each(function () {
                                d3.select(this).attr({
                                    "transform": b.attr("transform"),
                                    "y": (parseFloat(b.attr("y")) + bgVertOffset - 1),
                                    "x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (bgWidth / 2) - 1),
                                    "width": bgWidth + 2,
                                    "height": bgHeight + 2,
                                    "stroke": 1,
                                    "class": ("bar-value-text-bg-shadow_" + el)
                                });

                                d3.select(this).style({
                                    "fill": "#aaaaaa",
                                    "opacity": 0,
                                    "fill-opacity": 0,
                                    "filter": "url(#drop-shadow)"
                                });
                            });

                        // add background shape
                        parentG.append("rect")
                            .each(function() {
                                d3.select(this).attr({
                                    "transform": b.attr("transform"),
                                    "y": (parseFloat(b.attr("y")) + bgVertOffset),
                                    "x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (bgWidth / 2)),
                                    "width": bgWidth,
                                    "height": bgHeight,
                                    "stroke": "#aaaaaa",
                                    "stroke-width": 0.5,
                                    "class": ("bar-value-text-bg_" + el)
                                });

                                d3.select(this).style({
                                    "fill": "white",
                                    "opacity": "0",
                                    "fill-opacity": "0",
                                    "stroke-opacity": 0.75
                                });
                            });

                        // add tick mark under shape
                        parentG.append("rect")
                            .each(function() {
                                var rotatedX = parseFloat(b.attr("x")) + parseFloat(barWidth) / 2;
                                var rotatedY = parseFloat(b.attr("y")) + bgHeight + bgVertOffset - (dialogTickSize / 2);
                                d3.select(this).attr({
                                    "transform": (b.attr("transform") + " rotate(45," + rotatedX +"," + rotatedY + ")"),
                                    "y": (parseFloat(b.attr("y")) + bgHeight + bgVertOffset - dialogTickSize),
                                    "x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2)),
                                    "width": dialogTickSize,
                                    "height": dialogTickSize,
                                    "class": ("bar-value-text-bg-tick_" + el)
                                });

                                d3.select(this).style({
                                    "fill": "white",
                                    "fill-opacity": "0",
                                    "opacity": "0"
                                });
                            });

                        // add the text value
                        parentG.append("text")
                            .each(function() {
                                d3.select(this).text(function() {
                                    if (bar.y === 0) {
                                        return;
                                    }
                                    return parseFloat(bar.y).toFixed(2);
                                });

                                var width = this.getBBox().width;

                                d3.select(this).attr({
                                    "transform": b.attr("transform"),
                                    "y": (parseFloat(b.attr("y")) + 15 + bgVertOffset),
                                    "x": (parseFloat(b.attr("x")) + (parseFloat(barWidth) / 2) - (width / 2)),
                                    "font-size": "0.8rem",
                                    "font-weight": "normal",
                                    "font-family": "helvetica",
                                    "class": ("bar-value-text_" + el)
                                });

                                d3.select(this).style({
                                    "stroke": "black",
                                    "stroke-width": 0.2,
                                    "opacity": 0,
                                    "font-weight": "normal",
                                    "font-family": "arial"
                                });

                            });
                    });
                });

                d3.select(svg)
                    .select("#" + chartContainerId)
                    .selectAll("g." + el)
                    .selectAll("rect.nv-bar")
                    .on("mouseover", function () {
                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text_" + el)
                            .style("opacity", "1");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg-shadow_" + el)
                            .style("opacity", "0.5")
                            .style("fill-opacity", "0.5");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg_" + el)
                            .style("opacity", "1")
                            .style("fill-opacity", "1");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg-tick_" + el)
                            .style("opacity", "1")
                            .style("fill-opacity", "1");
                    });

                d3.select(svg)
                    .select("#" + chartContainerId)
                    .selectAll("g." + el)
                    .selectAll("rect.nv-bar")
                    .on("mouseout", function () {
                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text_" + el)
                            .style("opacity", "0");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg-shadow_" + el)
                            .style("opacity", "0")
                            .style("fill-opacity", "0");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg_" + el)
                            .style("opacity", "0")
                            .style("fill-opacity", "0");

                        d3.select("#" + chartContainerId)
                            .selectAll(".bar-value-text-bg-tick_" + el)
                            .style("opacity", "0")
                            .style("fill-opacity", "0");
                    });
            });

        return chart;
    }, onRenderComplete);
}

module.exports = {
    renderHorizontalBarChart: renderHorizontalBarChart,
    renderVerticalBarChart: renderVerticalBarChart,
    renderLineChart: renderLineChart,
    renderCompareMultibarChart: renderCompareMultibarChart
};
<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress<commit_msg>AAAAAAAAAAARGH!<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress"<|endoftext|>


<commit_before>'use strict';

const Playlist = require('./Playlist.js');
const Tag = require('./Tag.js');
const PlaylistEntry = require('./PlaylistEntry.js');
const logger = require('../util/Logger.js');
const tagConfig = require('../util/TagConfig.js');
const EventEmitter = require('events');

class PlaylistManager extends EventEmitter{

  constructor(pm, allTracks){
    super();
    this._pm = pm;
    this._gmusicPlaylists;
    this._gmusicPlaylistsEntries;
    this._playlists = [];
    this._tagPlaylists = [];
    this._playlistsById = new Object();
    this._allTracks = allTracks;
  }

  /**
   * emits 'updated' or 'error'
   */
  fetchPlaylists(){
    this._playlists = [];
    this._playlistsById = new Object();

    this._pm.getPlayLists((err, data) => {
      if (err){
        this.emit('error', new Error('failed to fetch playlists from gmusic', err));
        return;
      }

      this._gmusicPlaylists = data.data.items;

      this._pm.getPlayListEntries((err, library) => {
        if (err){
          this.emit('error', new Error('failed to fetch playlists entries from gmusic', err));
          return;
        }

        this._gmusicPlaylistsEntries = library.data.items;
        this._populatePlaylists();
        this.emit('updated');
      });
    });
  }

  newPlaylist(name){
    this._pm.addPlayList(name, (err, mutation)=>{
      if (err){
        logger.error('failed to create Playlist ' + name, err);
        return;
      }

      //update our model
      this.fetchPlaylists();
    });
  }

  deletePlaylist(playlist){
    this._pm.deletePlayList(playlist.getId(), (err, mutation)=>{
      if (err){
        logger.error('failed to delete Playlist ' + playlist.getName(), err);
        return;
      }

      logger.debug('Deleted playlist ' + playlist.getName());

      //update our model
      this.fetchPlaylists();
    });
  }

  getPlaylists(){
    return this._playlists;
  }

  getPlaylist(idx){
    return this._playlists[idx];
  }

  findByName(name){
    return this._playlists.find((element) => {
      return element.getName() === name;
    });
  }

  findTagByName(name){
    return this._tagPlaylists.find((element) => {
      return element.getName() === name;
    });
  }

  getPlaylistsEntries(){
    return this._gmusicPlaylistsEntries;
  }

  entriesAsStringArray(){
    return this._playlists.map( (element) => {
      return element.getName();
    } ) || [];
  }

  moveEntry(entry, beforeEntry, afterEntry){
    let beforeEntryClientId = beforeEntry ? beforeEntry.getClientId() : null;
    let afterEntryClientId = afterEntry ? afterEntry.getClientId() : null;

    this._pm.movePlayListEntry(entry._gmusicPlaylistEntry, beforeEntryClientId,
      afterEntryClientId, (err, entries)=>{
        if (err){
          logger.error('failed to move ' + entry.getName(), err);
          return;
        }

        logger.info('moved ' + entry.getName());

        this.fetchPlaylists(); //re-fetch grom gmusic
      });
  }

  addTrackToPlaylist(playlist, track, entryBefore, entryAfter){
    let entryBeforeClientId = entryBefore ? entryBefore.getClientId() : null;
    let entryAfterClientId = entryAfter ? entryAfter.getClientId() : null;

    let trackId = track.getId();
    let playlistId = playlist.getId();

    this._pm.addTrackToPlayList(trackId, playlistId, (err, mutationStatus)=>{
      if (err){
        logger.error('failed to add ' + track.getName() + ' playlist', err);
        return;
      }

      logger.info('added ' + track.getName() + ' to ' + playlist.getName());

      this.fetchPlaylists(); //re-fetch from gmusic
    }, entryBeforeClientId, entryAfterClientId);
  }

  removeEntryFromPlaylist(playlist, entry){
    let entryId = entry.getId();
    this._pm.removePlayListEntry(entryId, (err)=>{
      if (err){
        logger.error('failed to remove ' + entry.getTrack().getName() + ' from playlist', err);
        return;
      }

      logger.info('removed ' + entry.getTrack().getName() + ' from ' + playlist.getName());

      this.fetchPlaylists(); //re-fetch from gmusic
    });
  }

  addTrackToTag(track, tagName){
    let tag = this.findTagByName(tagName);
    if (tag){
      if (!tag.containsTrack(track)){
        this.addTrackToPlaylist(tag, track);
      }
      return;
    }

    //Tag does not exists yet -> create it first
    this.once('updated', ()=>{
      this.addTrackToTag(track, tagName);
    });
    let playlistName = tagConfig.getPlaylistPrefix() + tagName;
    logger.debug('creating playlist ' + playlistName + ' to store tagged tracks');
    this.newPlaylist(playlistName);
  }

  removeTrackFromTag(track, tagName){
    let tag = this.findTagByName(tagName);
    if (!tag){
      logger.warning('Playlist for ' + tagName + ' not found');
      return;
    }

    let entry = tag.getEntryByTrack(track);
    if (!entry){
      logger.warning('Track ' + track.getName() + ' not tagged as ' + tag.getName());
      return;
    }

    this.removeEntryFromPlaylist(tag, entry);
  }

  _populatePlaylists(){
    let self = this;

    this._allTracks.clearPlaylistAndTagsLocally();

    //enumerate all playlists
    this._gmusicPlaylists.forEach((element) => {
      let pl = null;
      if (tagConfig.isTagPlaylist(element.name)){
        pl = new Tag(self, element);
        self._tagPlaylists.push(pl);
      } else {
        pl = new Playlist(self, element);
        self._playlists.push(pl);
      }
      self._playlistsById[element.id] = pl;
    });

    self._playlists.sort((a,b)=> {
      return a.getName() > b.getName() ? 1 : -1;
    });

    //enumerate all playlist-entries
    this._gmusicPlaylistsEntries.forEach((element)=>{
      let pl = self._playlistsById[element.playlistId];
      if (!pl){
        throw new Error('Unable to find playlist with id ' + element.playlistId);
      }

      let track = self._allTracks.getTrackById(element.trackId);
      if (!track){
        throw new Error('Playlist ' + pl.getName() + ': track with id ' + element.trackId + ' not found');
      }

      let plEntry = new PlaylistEntry(this, element, track);

      pl.add(plEntry);
      if (pl.isTag){
        track.addTagLocally(pl);
      } else {
        track.addPlaylistLocally(pl);
      }
    });

  }
}

module.exports = PlaylistManager;<commit_msg>Do not throw on missing playlist for entry anymore (might indeed happen during playlist deletion with entries)<commit_after>'use strict';

const Playlist = require('./Playlist.js');
const Tag = require('./Tag.js');
const PlaylistEntry = require('./PlaylistEntry.js');
const logger = require('../util/Logger.js');
const tagConfig = require('../util/TagConfig.js');
const EventEmitter = require('events');

class PlaylistManager extends EventEmitter{

  constructor(pm, allTracks){
    super();
    this._pm = pm;
    this._gmusicPlaylists;
    this._gmusicPlaylistsEntries;
    this._playlists = [];
    this._tagPlaylists = [];
    this._playlistsById = new Object();
    this._allTracks = allTracks;
  }

  /**
   * emits 'updated' or 'error'
   */
  fetchPlaylists(){
    this._playlists = [];
    this._playlistsById = new Object();

    this._pm.getPlayLists((err, data) => {
      if (err){
        this.emit('error', new Error('failed to fetch playlists from gmusic', err));
        return;
      }

      this._gmusicPlaylists = data.data.items;

      this._pm.getPlayListEntries((err, library) => {
        if (err){
          this.emit('error', new Error('failed to fetch playlists entries from gmusic', err));
          return;
        }

        this._gmusicPlaylistsEntries = library.data.items;
        this._populatePlaylists();
        this.emit('updated');
      });
    });
  }

  newPlaylist(name){
    this._pm.addPlayList(name, (err, mutation)=>{
      if (err){
        logger.error('failed to create Playlist ' + name, err);
        return;
      }

      //update our model
      this.fetchPlaylists();
    });
  }

  deletePlaylist(playlist){
    this._pm.deletePlayList(playlist.getId(), (err, mutation)=>{
      if (err){
        logger.error('failed to delete Playlist ' + playlist.getName(), err);
        return;
      }

      logger.debug('Deleted playlist ' + playlist.getName());

      //update our model
      this.fetchPlaylists();
    });
  }

  getPlaylists(){
    return this._playlists;
  }

  getPlaylist(idx){
    return this._playlists[idx];
  }

  findByName(name){
    return this._playlists.find((element) => {
      return element.getName() === name;
    });
  }

  findTagByName(name){
    return this._tagPlaylists.find((element) => {
      return element.getName() === name;
    });
  }

  getPlaylistsEntries(){
    return this._gmusicPlaylistsEntries;
  }

  entriesAsStringArray(){
    return this._playlists.map( (element) => {
      return element.getName();
    } ) || [];
  }

  moveEntry(entry, beforeEntry, afterEntry){
    let beforeEntryClientId = beforeEntry ? beforeEntry.getClientId() : null;
    let afterEntryClientId = afterEntry ? afterEntry.getClientId() : null;

    this._pm.movePlayListEntry(entry._gmusicPlaylistEntry, beforeEntryClientId,
      afterEntryClientId, (err, entries)=>{
        if (err){
          logger.error('failed to move ' + entry.getName(), err);
          return;
        }

        logger.info('moved ' + entry.getName());

        this.fetchPlaylists(); //re-fetch grom gmusic
      });
  }

  addTrackToPlaylist(playlist, track, entryBefore, entryAfter){
    let entryBeforeClientId = entryBefore ? entryBefore.getClientId() : null;
    let entryAfterClientId = entryAfter ? entryAfter.getClientId() : null;

    let trackId = track.getId();
    let playlistId = playlist.getId();

    this._pm.addTrackToPlayList(trackId, playlistId, (err, mutationStatus)=>{
      if (err){
        logger.error('failed to add ' + track.getName() + ' playlist', err);
        return;
      }

      logger.info('added ' + track.getName() + ' to ' + playlist.getName());

      this.fetchPlaylists(); //re-fetch from gmusic
    }, entryBeforeClientId, entryAfterClientId);
  }

  removeEntryFromPlaylist(playlist, entry){
    let entryId = entry.getId();
    this._pm.removePlayListEntry(entryId, (err)=>{
      if (err){
        logger.error('failed to remove ' + entry.getTrack().getName() + ' from playlist', err);
        return;
      }

      logger.info('removed ' + entry.getTrack().getName() + ' from ' + playlist.getName());

      this.fetchPlaylists(); //re-fetch from gmusic
    });
  }

  addTrackToTag(track, tagName){
    let tag = this.findTagByName(tagName);
    if (tag){
      if (!tag.containsTrack(track)){
        this.addTrackToPlaylist(tag, track);
      }
      return;
    }

    //Tag does not exists yet -> create it first
    this.once('updated', ()=>{
      this.addTrackToTag(track, tagName);
    });
    let playlistName = tagConfig.getPlaylistPrefix() + tagName;
    logger.debug('creating playlist ' + playlistName + ' to store tagged tracks');
    this.newPlaylist(playlistName);
  }

  removeTrackFromTag(track, tagName){
    let tag = this.findTagByName(tagName);
    if (!tag){
      logger.warning('Playlist for ' + tagName + ' not found');
      return;
    }

    let entry = tag.getEntryByTrack(track);
    if (!entry){
      logger.warning('Track ' + track.getName() + ' not tagged as ' + tag.getName());
      return;
    }

    this.removeEntryFromPlaylist(tag, entry);
  }

  _populatePlaylists(){
    let self = this;

    this._allTracks.clearPlaylistAndTagsLocally();

    //enumerate all playlists
    this._gmusicPlaylists.forEach((element) => {
      let pl = null;
      if (tagConfig.isTagPlaylist(element.name)){
        pl = new Tag(self, element);
        self._tagPlaylists.push(pl);
      } else {
        pl = new Playlist(self, element);
        self._playlists.push(pl);
      }
      self._playlistsById[element.id] = pl;
    });

    self._playlists.sort((a,b)=> {
      return a.getName() > b.getName() ? 1 : -1;
    });

    //enumerate all playlist-entries
    this._gmusicPlaylistsEntries.forEach((element)=>{
      let pl = self._playlistsById[element.playlistId];
      if (!pl){
        logger.warning('Unable to find playlist with id ' + element.playlistId);
        return;
      }

      let track = self._allTracks.getTrackById(element.trackId);
      if (!track){
        throw new Error('Playlist ' + pl.getName() + ': track with id ' + element.trackId + ' not found');
      }

      let plEntry = new PlaylistEntry(this, element, track);

      pl.add(plEntry);
      if (pl.isTag){
        track.addTagLocally(pl);
      } else {
        track.addPlaylistLocally(pl);
      }
    });

  }
}

module.exports = PlaylistManager;<|endoftext|>


<commit_before>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Wri<commit_msg>Logic error<commit_after>var fs = require('fs');
var child_process = require('child_process')
var max_sleep = 300
//Added a comment that achieves no real goal.

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  console.info("Writ<|endoftext|>


<commit_before>import React from 'react'
import utils from 'utils'
import {FormGroup, Col, ControlLabel, FormControl, InputGroup, HelpBlock, ButtonGroup, Button} from 'react-bootstrap'

const getFieldId = field => (
	field.id || field.name  // name property is required
)

const getHumanValue = (field, humanValue) => {
	if (typeof humanValue === 'function') {
		return humanValue(field.value)
	} else if (typeof humanValue !== 'undefined') {
		return humanValue
	} else {
		return field.value
	}
}

const getFormGroupValidationState = (field, form) => {
	const {touched, errors} = form
	const fieldTouched = touched[field.name]
	const fieldError = errors[field.name]
	const fieldWarning = null
	return (fieldTouched && (fieldError ? 'error' : fieldWarning ? 'warning': null)) || null
}

const getHelpBlock = (field, form) => {
	const {touched, errors} = form
	const fieldTouched = touched[field.name]
	const fieldError = errors[field.name]
	const fieldWarning = null
	return (fieldTouched && (fieldError || fieldWarning) &&
		<HelpBlock>
			{fieldTouched && (fieldError ? fieldError: fieldWarning)}
		</HelpBlock>
	)
}

const renderFieldNoLabel = (field, form, widgetElem, children) => {
	const id = getFieldId(field)
	const validationState = getFormGroupValidationState(field, form)
	return (
		<FormGroup controlId={id} validationState={validationState}>
			{widgetElem}
			{getHelpBlock(field, form)}
			{children}
		</FormGroup>
	)
}

const renderField = (field, label, form, widgetElem, children, extraColElem, addon) => {
	if (typeof label === 'undefined') {
		label = utils.sentenceCase(field.name) // name is a required prop of field
	}
	let widget
	if (!addon) {
		widget = widgetElem
	} else {
		// allows passing a url for an image
		if (addon.indexOf('.') !== -1) {
			addon =
		}
		const focusElement = () => {
			const element = document.getElementById(id)
			if (element && element.focus) {
				element.focus()
			}
		}
		widget = <InputGroup>
			{widgetElem}
			<InputGroup.Addon onClick={focusElement}>{addon}</InputGroup.Addon>
		</InputGroup>
	}
	const id = getFieldId(field)
	const validationState = getFormGroupValidationState(field, form)

	// setting label or extraColElem explicitly to null will completely remove these columns!
	const widgetWidth = 12 - (label === null ? 0 : 2) - (extraColElem === null ? 0 : 3)
	// controlId prop of the FormGroup sets the id of the control element
	return (
		<FormGroup controlId={id} validationState={validationState}>
			{label !== null && <Col sm={2} componentClass={ControlLabel}>{label}</Col>}
			<Col sm={widgetWidth}>

					{widget}
					{getHelpBlock(field, form)}
					{children}

			</Col>
			{extraColElem && <Col sm={3} {...extraColElem.props} />}
		</FormGroup>
	)
}

export const renderInputField = ({
  field, // { name, value, onChange, onBlur }
  form, // contains, touched, errors, values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, ...otherProps} = props
	const widgetElem = <FormControl {...field} {...otherProps} />
	return renderField(field, label, form, widgetElem, children, extraColElem)
}

export const renderInputFieldNoLabel = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {children, ...otherProps} = props
	const widgetElem = <FormControl {...field} {...otherProps} />
	return renderFieldNoLabel(field, form, widgetElem, children)
}

export const renderReadonlyField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, humanValue, ...otherProps} = props
	const widgetElem = <FormControl.Static componentClass={'div'} {...field} {...otherProps}>{getHumanValue(field, humanValue)}</FormControl.Static>
	return renderField(field, label, form, widgetElem, children, extraColElem)
}

export const renderValue = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const id = field.id || field.name
	return <FormControl.Static componentClass={'span'} id={id}>{getHumanValue(field)}</FormControl.Static>
}

export const renderSpecialField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, widget, children, addon, extraColElem, ...otherProps} = props
	const widgetElem = React.cloneElement(widget, {...field, ...otherProps})
	return renderField(field, label, form, widgetElem, children, extraColElem, addon)
}

export const renderButtonToggleGroup = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, buttons, children, extraColElem, ...otherProps} = props
	const widgetElem = (
		<ButtonGroup {...otherProps}>
			{buttons.map((button, index) => {
				if (!button) { return null }
				const {label, ...props} = button
				return (
					<Button
						{...props}
						name={field.name}
						key={button.value}
						active={field.value === button.value}
						onBlur={field.onBlur}
						onClick={field.onChange}
					>
							{label}
					</Button>
				)
			})}
		</ButtonGroup>
	)
	return renderField(field, label, form, widgetElem, children, extraColElem)
}

export const renderToggleButton = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {labels, children, ...otherProps} = props
	delete field.onBlur // the onBlur would change the value from boolean to string
	const widgetElem = <Button
		{...field} {...otherProps}>{labels[field.value]}</Button>
	return renderFieldNoLabel(field, form, widgetElem, children)
}

export default renderField
<commit_msg>NCI-Agency/anet#917: Add possibility to display field label/widget vertically<commit_after>import React from 'react'
import utils from 'utils'
import {FormGroup, Col, ControlLabel, FormControl, InputGroup, HelpBlock, ButtonGroup, Button} from 'react-bootstrap'

const getFieldId = field => (
	field.id || field.name  // name property is required
)

const getHumanValue = (field, humanValue) => {
	if (typeof humanValue === 'function') {
		return humanValue(field.value)
	} else if (typeof humanValue !== 'undefined') {
		return humanValue
	} else {
		return field.value
	}
}

const getFormGroupValidationState = (field, form) => {
	const {touched, errors} = form
	const fieldTouched = touched[field.name]
	const fieldError = errors[field.name]
	const fieldWarning = null
	return (fieldTouched && (fieldError ? 'error' : fieldWarning ? 'warning': null)) || null
}

const getHelpBlock = (field, form) => {
	const {touched, errors} = form
	const fieldTouched = touched[field.name]
	const fieldError = errors[field.name]
	const fieldWarning = null
	return (fieldTouched && (fieldError || fieldWarning) &&
		<HelpBlock>
			{fieldTouched && (fieldError ? fieldError: fieldWarning)}
		</HelpBlock>
	)
}

const renderFieldNoLabel = (field, form, widgetElem, children) => {
	const id = getFieldId(field)
	const validationState = getFormGroupValidationState(field, form)
	return (
		<FormGroup controlId={id} validationState={validationState}>
			{widgetElem}
			{getHelpBlock(field, form)}
			{children}
		</FormGroup>
	)
}

const renderField = (field, label, form, widgetElem, children, extraColElem, addon, horizontal) => {
	if (typeof label === 'undefined') {
		label = utils.sentenceCase(field.name) // name is a required prop of field
	}
	if (typeof horizontal === 'undefined') {
		horizontal = true  // default direction of label and input
	}
	let widget
	if (!addon) {
		widget = widgetElem
	} else {
		// allows passing a url for an image
		if (addon.indexOf('.') !== -1) {
			addon =
		}
		const focusElement = () => {
			const element = document.getElementById(id)
			if (element && element.focus) {
				element.focus()
			}
		}
		widget = <InputGroup>
			{widgetElem}
			<InputGroup.Addon onClick={focusElement}>{addon}</InputGroup.Addon>
		</InputGroup>
	}
	const id = getFieldId(field)
	const validationState = getFormGroupValidationState(field, form)

	// setting label or extraColElem explicitly to null will completely remove these columns!
	const widgetWidth = 12 - (label === null ? 0 : 2) - (extraColElem === null ? 0 : 3)
	// controlId prop of the FormGroup sets the id of the control element
	return (
		<FormGroup controlId={id} validationState={validationState}>
			{horizontal ?
				<React.Fragment>
					{label !== null && <Col sm={2} componentClass={ControlLabel}>{label}</Col>}
					<Col sm={widgetWidth}>

							{widget}
							{getHelpBlock(field, form)}
							{children}

					</Col>
				</React.Fragment>
			:
				<React.Fragment>
					{label !== null && <ControlLabel>{label}</ControlLabel>}
					{widget}
					{getHelpBlock(field, form)}
					{children}
				</React.Fragment>
			}
			{extraColElem && <Col sm={3} {...extraColElem.props} />}
		</FormGroup>
	)
}

export const renderInputField = ({
  field, // { name, value, onChange, onBlur }
  form, // contains, touched, errors, values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, addon, horizontal, ...otherProps} = props
	const widgetElem = <FormControl {...field} {...otherProps} />
	return renderField(field, label, form, widgetElem, children, extraColElem, addon, horizontal)
}

export const renderInputFieldNoLabel = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {children, ...otherProps} = props
	const widgetElem = <FormControl {...field} {...otherProps} />
	return renderFieldNoLabel(field, form, widgetElem, children)
}

export const renderReadonlyField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, addon, horizontal, humanValue, ...otherProps} = props
	const widgetElem = <FormControl.Static componentClass={'div'} {...field} {...otherProps}>{getHumanValue(field, humanValue)}</FormControl.Static>
	return renderField(field, label, form, widgetElem, children, extraColElem, addon, horizontal)
}

export const renderValue = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const id = field.id || field.name
	return <FormControl.Static componentClass={'span'} id={id}>{getHumanValue(field)}</FormControl.Static>
}

export const renderSpecialField = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, addon, horizontal, widget, ...otherProps} = props
	const widgetElem = React.cloneElement(widget, {...field, ...otherProps})
	return renderField(field, label, form, widgetElem, children, extraColElem, addon, horizontal)
}

export const renderButtonToggleGroup = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {label, children, extraColElem, addon, horizontal, buttons, ...otherProps} = props
	const widgetElem = (
		<ButtonGroup {...otherProps}>
			{buttons.map((button, index) => {
				if (!button) { return null }
				const {label, ...props} = button
				return (
					<Button
						{...props}
						name={field.name}
						key={button.value}
						active={field.value === button.value}
						onBlur={field.onBlur}
						onClick={field.onChange}
					>
							{label}
					</Button>
				)
			})}
		</ButtonGroup>
	)
	return renderField(field, label, form, widgetElem, children, extraColElem, addon, horizontal)
}

export const renderToggleButton = ({
  field, // { name, value, onChange, onBlur }
  form, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
  ...props
}) => {
	const {labels, children, ...otherProps} = props
	delete field.onBlur // the onBlur would change the value from boolean to string
	const widgetElem = <Button
		{...field} {...otherProps}>{labels[field.value]}</Button>
	return renderFieldNoLabel(field, form, widgetElem, children)
}

export default renderField
<|endoftext|>


<commit_before>var Path = require('path');
var ugly = require("uglify-js");
var clean = require("clean-css");
var fs = require('fs');
var jsp = ugly.parser;
var pro = ugly.uglify;

module.exports = function(view, opts) {
	var $ = view.window.$;
	var settings = {
		minify: opts.settings.minify == null || opts.settings.minify,
		statics: opts.settings.statics || process.cwd() + '/public',
		viewmtime: view.mtime
	}
	processTags('script', 'src', minifyJS, $, settings);
	processTags('link', 'href', minifyCSS, $, settings);
};

function processTags(tag, att, minfun, $, settings) {
	var files = {};
	$(tag+'[notemplate\\:minify]').each(function() {
		if (settings.minify) {
			var src = $(this).attr(att);
			var dst = $(this).attr("notemplate:minify");
			var list = files[dst] || [];
			list.push(src);
			if (!files[dst]) files[dst] = list;
		} else {
			$(this).get(0).attributes.removeNamedItem('notemplate:minify');
		}
	});
	if (settings.minify) {
		for (var dst in files) {
			if (!minified(settings.viewmtime, settings.statics, dst, files[dst])) minfun(settings.statics, dst, files[dst]);
			var nodes = $(tag+'[notemplate\\:minify="'+dst+'"]');
			var last = nodes.last().get(0);
			nodes.slice(0, -1).remove();
			last.attributes.removeNamedItem('notemplate:minify');
			last.attributes[att].value = dst;
		}
	}
}

function minified(viewmtime, public, dst, sources) {
	// return true if dst exists has been modified after all files
	var dst = Path.join(public, dst);
	if (!Path.existsSync(dst)) return false;
	var dstTime = fs.statSync(dst).mtime.getTime();
	var tmplTime = viewmtime.getTime();
	if (tmplTime > dstTime) return false;
	return sources.every(function(src) {
		var src = Path.join(public, src);
		if (!Path.existsSync(src)) {
			console.error("Cannot check if missing file is minified :", src);
			return false;
		}
		if (fs.statSync(src).mtime.getTime() > dstTime) return false;
		return true;
	});
}

function minifyJS(public, dst, sources) {
	var dst = Path.join(public, dst);
	var fd = fs.openSync(dst, 'w');
	sources.forEach(function(src) {
		var src = Path.join(public, src);
		var buf = fs.readFileSync(src);
		if (buf == null) return console.error("Cannot minify empty file :", src);
		var ast = jsp.parse(buf.toString());
		ast = pro.ast_mangle(ast);
		ast = pro.ast_squeeze(ast);
		fs.writeSync(fd, pro.gen_code(ast) + ";\n");
	});
	fs.closeSync(fd);
}

function minifyCSS(public, dst, sources) {
	var dst = Path.join(public, dst);
	var fd = fs.openSync(dst, 'w');
	sources.forEach(function(src) {
		var src = Path.join(public, src);
		fs.writeSync(fd, clean.process(cssImportRule(src, dst), {keepSpecialComments:0}) + "\n");
	});
	fs.closeSync(fd);
}

function fixRelativePaths(src, dst, text) {
	var srcDir = Path.dirname(src);
	var dstDir = Path.dirname(dst);
	var relativePath = Path.relative(Path.dirname(dst), Path.dirname(src));
	if (!relativePath) return text;
	var text = text.replace(/(url\(["'])([^"']*)(["']\))/gm, function(match, p1, p2, p3, offset, s) {
		return p1 + Path.join(relativePath, p2) + p3;
	});
	return text;
}

function cssImportRule(src, dst) {
	var text = fs.readFileSync(src, 'utf8');
	if (text == null) return "";
	else return fixRelativePaths(src, dst, text);

	return text.replace(/^\s*@import\s+(?:url\()?["']([^"']*)["'](?:\))?;?/gm, function(match, p1, offset, s) {
		var newsrc = Path.join(Path.dirname(src), p1);
		return cssImportRule(newsrc, dst);
	});
}
<commit_msg>Add minify = "cat" parameter to just concatenate files, not minify them.<commit_after>var Path = require('path');
var ugly = require("uglify-js");
var clean = require("clean-css");
var fs = require('fs');
var jsp = ugly.parser;
var pro = ugly.uglify;

module.exports = function(view, opts) {
	var $ = view.window.$;
	var settings = {
		minify: opts.settings.minify == null || opts.settings.minify,
		statics: opts.settings.statics || process.cwd() + '/public',
		viewmtime: view.mtime
	}
	processTags('script', 'src', concatenateJS, $, settings);
	processTags('link', 'href', concatenateCSS, $, settings);
};

function processTags(tag, att, minfun, $, settings) {
	var files = {};
	$(tag+'[notemplate\\:minify]').each(function() {
		if (settings.minify) {
			var src = $(this).attr(att);
			var dst = $(this).attr("notemplate:minify");
			var list = files[dst] || [];
			list.push(src);
			if (!files[dst]) files[dst] = list;
		} else {
			$(this).get(0).attributes.removeNamedItem('notemplate:minify');
		}
	});
	if (settings.minify) {
		for (var dst in files) {
			if (!minified(settings.viewmtime, settings.statics, dst, files[dst])) minfun(settings.statics, dst, files[dst], settings.minify);
			var nodes = $(tag+'[notemplate\\:minify="'+dst+'"]');
			var last = nodes.last().get(0);
			nodes.slice(0, -1).remove();
			last.attributes.removeNamedItem('notemplate:minify');
			last.attributes[att].value = dst;
		}
	}
}

function minified(viewmtime, public, dst, sources) {
	// return true if dst exists has been modified after all files
	var dst = Path.join(public, dst);
	if (!Path.existsSync(dst)) return false;
	var dstTime = fs.statSync(dst).mtime.getTime();
	var tmplTime = viewmtime.getTime();
	if (tmplTime > dstTime) return false;
	return sources.every(function(src) {
		var src = Path.join(public, src);
		if (!Path.existsSync(src)) {
			console.error("Cannot check if missing file is minified :", src);
			return false;
		}
		if (fs.statSync(src).mtime.getTime() > dstTime) return false;
		return true;
	});
}

function concatenateJS(public, dst, sources, minify) {
	var dst = Path.join(public, dst);
	var fd = fs.openSync(dst, 'w');
	sources.forEach(function(src) {
		var src = Path.join(public, src);
		var buf = fs.readFileSync(src);
		if (buf == null) return console.error("Cannot minify empty file :", src);
		if (minify != "cat") {
			var ast = jsp.parse(buf.toString());
			ast = pro.ast_mangle(ast);
			ast = pro.ast_squeeze(ast);
			buf = pro.gen_code(ast);
		}
		fs.writeSync(fd, buf + ";\n");
	});
	fs.closeSync(fd);
}

function concatenateCSS(public, dst, sources, minify) {
	var dst = Path.join(public, dst);
	var fd = fs.openSync(dst, 'w');
	sources.forEach(function(src) {
		var src = Path.join(public, src);
		var buf = cssImportRule(src, dst);
		if (minify != "cat") {
			buf = clean.process(buf, {keepSpecialComments:0});
		}
		fs.writeSync(fd, buf + "\n");
	});
	fs.closeSync(fd);
}

function fixRelativePaths(src, dst, text) {
	var srcDir = Path.dirname(src);
	var dstDir = Path.dirname(dst);
	var relativePath = Path.relative(Path.dirname(dst), Path.dirname(src));
	if (!relativePath) return text;
	var text = text.replace(/(url\(["'])([^"']*)(["']\))/gm, function(match, p1, p2, p3, offset, s) {
		return p1 + Path.join(relativePath, p2) + p3;
	});
	return text;
}

function cssImportRule(src, dst) {
	var text = fs.readFileSync(src, 'utf8');
	if (text == null) return "";
	else return fixRelativePaths(src, dst, text);

	return text.replace(/^\s*@import\s+(?:url\()?["']([^"']*)["'](?:\))?;?/gm, function(match, p1, offset, s) {
		var newsrc = Path.join(Path.dirname(src), p1);
		return cssImportRule(newsrc, dst);
	});
}
<|endoftext|>


<commit_before>// c++ Template for the preamble
var preamble = (basis,classname)=>
`// 3D Projective Geometric Algebra
// Written by a generator written by enki.
#include <stdio.h>
#include <cmath>
#include <array>

#define PI 3.14159265358979323846

static const char* basis[] = { ${basis.map(x=>'"'+x+'"').join(',')} };

class ${classname} {
  public:
    ${classname} ()  { std::fill( mvec, mvec + sizeof( mvec )/4, 0.0f ); }
    ${classname} (float f, int idx=0) { std::fill( mvec, mvec + sizeof( mvec )/4, 0.0f ); mvec[idx] = f; }
    float& operator [] (size_t idx) { return mvec[idx]; }
    const float& operator [] (size_t idx) const { return mvec[idx]; }
    ${classname} log () { int n=0; for (int i=0,j=0;i<${basis.length};i++) if (mvec[i]!=0.0f) { n++; printf("%s%0.7g%s",(j>0)?" + ":"",mvec[i],(i==0)?"":basis[i]); j++; };if (n==0) printf("0");  printf("\\n"); return *this; }
    ${classname} Conjugate();
    ${classname} Involute();
    float norm();
    float inorm();
    ${classname} normalized();
  private:
    float mvec[${basis.length}];
};
`

// C++ Template for our binary operators

var binary = (classname, symbol, name, name_a, name_b, name_ret, code, classname_a=classname, classname_b=classname, desc)=>
`//***********************
// ${classname}.${name} : ${name_ret} = ${symbol?name_a+" "+symbol+" "+name_b:name_a+"."+name+"("+name_b+")"}
// ${desc}
//***********************
inline ${classname} ${symbol?"operator "+symbol:name} (const ${classname_a} &${name_a}, const ${classname_b} &${name_b}) {
  ${classname} ${name_ret};
  ${code}
  return ${name_ret};
};`

// c++ Template for our unary operators

var unary = (classname, symbol, name, name_a, name_ret, code, classname_a=classname, desc)=>
`//***********************
// ${classname}.${name} : ${name_ret} = ${symbol?symbol+name_a:name_a+"."+name+"()"}
// ${desc}
//***********************
inline ${classname} ${symbol?"operator "+symbol:classname+'::'+name} (${symbol?`const ${classname_a} &${name_a}`:``}) {
  ${classname} ${name_ret};
  ${symbol?code:code.replace(/a\[/g,'this->mvec[')}
  return ${name_ret};
};`

// c++ example for CGA
var CGA=(basis,classname)=>({
preamble:`
// PGA is point based. Vectors are points.
static ${classname} e1(1.0f,1), e2(1.0f,2), e3(1.0f,3), e4(1.0f,4), e5(1.0f,5);

// We seldomly work in the natural basis, but instead in a null basis
// for this we create two null vectors 'origin' and 'infinity'
static ${classname} eo = e4+e5, ei = 0.5f*(e5-e4);

// create a point from x,y,z coordinates
static ${classname} up(float x, float y, float z) {
  float d = x*x + y*y + z*z;
  return x*e1 + y*e2 + z*e3 + 0.5f*d*ei + eo;
}

`,
amble:`
  ${classname} px = up(1.0,2.0,3.0);
  ${classname} line = px ^ eo ^ ei;
  ${classname} sphere = !(eo-ei);
  printf("a point       : "); px.log();
  printf("a line        : "); line.log();
  printf("a sphere      : "); sphere.log();
`
})

// c++ example for algebras without example
var GENERIC = (basis,classname)=>({
preamble:`
${basis.slice(1).map((x,i)=>`static ${classname} ${x}(1.0f,${i+1});`).join('\n')}
`,
amble:`
  printf("${basis[1]}*${basis[1]}         : "); (${basis[1]}*${basis[1]}).log();
  printf("pss           : "); ${basis[basis.length-1]}.log();
  printf("pss*pss       : "); (${basis[basis.length-1]}*${basis[basis.length-1]}).log();
`
})

// c++ example for PGA
var PGA3D = (basis,classname)=>({
preamble:`
// A rotor (Euclidean line) and translator (Ideal line)
static ${classname} rotor(float angle, ${classname} line) { return cos(angle/2.0f) + sin(angle/2.0f)*line.normalized(); }
static ${classname} translator(float dist, ${classname} line) { return 1.0f + dist/2.0f*line; }

// PGA is plane based. Vectors are planes. (think linear functionals)
static ${classname} e0(1.0f,1), e1(1.0f,2), e2(1.0f,3), e3(1.0f,4);

// A plane is defined using its homogenous equation ax + by + cz + d = 0
static ${classname} plane(float a,float b,float c,float d) { return a*e1 + b*e2 + c*e3 + d*e0; }

// PGA points are trivectors.
static ${classname} e123 = e1^e2^e3, e032 = e0^e3^e2, e013 = e0^e1^e3, e021 = e0^e2^e1;

// A point is just a homogeneous point, euclidean coordinates plus the origin
static ${classname} point(float x, float y, float z) { return e123 + x*e032 + y*e013 + z*e021; }

// for our toy problem (generate points on the surface of a torus)
// we start with a function that generates motors.
// circle(t) with t going from 0 to 1.
static ${classname} circle(float t, float radius, ${classname} line) {
  return rotor(t*2.0f*PI,line) * translator(radius,e1*e0);
}

// a torus is now the product of two circles.
static ${classname} torus(float s, float t, float r1, ${classname} l1, float r2, ${classname} l2) {
  return circle(s,r2,l2)*circle(t,r1,l1);
}

// and to sample its points we simply sandwich the origin ..
static ${classname} point_on_torus(float s, float t) {
  ${classname} to = torus(s,t,0.25f,e1*e2,0.6f,e1*e3);
  return to * e123 * ~to;
}
`,
amble:`
  // Elements of the even subalgebra (scalar + bivector + pss) of unit length are motors
  ${classname} rot = rotor( PI/2.0f, e1 * e2 );

  // The outer product ^ is the MEET. Here we intersect the yz (x=0) and xz (y=0) planes.
  ${classname} ax_z = e1 ^ e2;

  // line and plane meet in point. We intersect the line along the z-axis (x=0,y=0) with the xy (z=0) plane.
  ${classname} orig = ax_z ^ e3;

  // We can also easily create points and join them into a line using the regressive (vee, &) product.
  ${classname} px = point(1.0,0.0,0.0);
  ${classname} line = orig & px;

  // Lets also create the plane with equation 2x + z - 3 = 0
  ${classname} p = plane(2,0,1,-3);

  // rotations work on all elements
  ${classname} rotated_plane = rot * p * ~rot;
  ${classname} rotated_line  = rot * line * ~rot;
  ${classname} rotated_point = rot * px * ~rot;

  // See the 3D PGA Cheat sheet for a huge collection of useful formulas
  ${classname} point_on_plane = (p | px) * p;

  // Some output.
  printf("a point       : "); px.log();
  printf("a line        : "); line.log();
  printf("a plane       : "); p.log();
  printf("a rotor       : "); rot.log();
  printf("rotated line  : "); rotated_line.log();
  printf("rotated point : "); rotated_point.log();
  printf("rotated plane : "); rotated_plane.log();
  printf("point on plane: "); point_on_plane.normalized().log();
  printf("point on torus: "); point_on_torus(0.0f,0.0f).log();
  (e0-1.0f).log();
  (1.0f-e0).log();
`})

// c++ Template for the postamble
var postamble = (basis, classname, example)=>
`
inline float ${classname}::norm() { return sqrt(abs(((*this)*Conjugate()).mvec[0])); }
inline float ${classname}::inorm() { return (!(*this)).norm(); }
inline ${classname} ${classname}::normalized() { return (*this) * (1/norm()); }

${example.preamble}

int main (int argc, char **argv) {
  ${example.amble}
  return 0;
}`;

Object.assign(exports,{preamble,postamble,unary,binary,desc:"c++",PGA3D,CGA,GENERIC});
<commit_msg>switched to std::abs<commit_after>// c++ Template for the preamble
var preamble = (basis,classname)=>
`// 3D Projective Geometric Algebra
// Written by a generator written by enki.
#include <stdio.h>
#include <cmath>
#include <array>

#define PI 3.14159265358979323846

static const char* basis[] = { ${basis.map(x=>'"'+x+'"').join(',')} };

class ${classname} {
  public:
    ${classname} ()  { std::fill( mvec, mvec + sizeof( mvec )/4, 0.0f ); }
    ${classname} (float f, int idx=0) { std::fill( mvec, mvec + sizeof( mvec )/4, 0.0f ); mvec[idx] = f; }
    float& operator [] (size_t idx) { return mvec[idx]; }
    const float& operator [] (size_t idx) const { return mvec[idx]; }
    ${classname} log () { int n=0; for (int i=0,j=0;i<${basis.length};i++) if (mvec[i]!=0.0f) { n++; printf("%s%0.7g%s",(j>0)?" + ":"",mvec[i],(i==0)?"":basis[i]); j++; };if (n==0) printf("0");  printf("\\n"); return *this; }
    ${classname} Conjugate();
    ${classname} Involute();
    float norm();
    float inorm();
    ${classname} normalized();
  private:
    float mvec[${basis.length}];
};
`

// C++ Template for our binary operators

var binary = (classname, symbol, name, name_a, name_b, name_ret, code, classname_a=classname, classname_b=classname, desc)=>
`//***********************
// ${classname}.${name} : ${name_ret} = ${symbol?name_a+" "+symbol+" "+name_b:name_a+"."+name+"("+name_b+")"}
// ${desc}
//***********************
inline ${classname} ${symbol?"operator "+symbol:name} (const ${classname_a} &${name_a}, const ${classname_b} &${name_b}) {
  ${classname} ${name_ret};
  ${code}
  return ${name_ret};
};`

// c++ Template for our unary operators

var unary = (classname, symbol, name, name_a, name_ret, code, classname_a=classname, desc)=>
`//***********************
// ${classname}.${name} : ${name_ret} = ${symbol?symbol+name_a:name_a+"."+name+"()"}
// ${desc}
//***********************
inline ${classname} ${symbol?"operator "+symbol:classname+'::'+name} (${symbol?`const ${classname_a} &${name_a}`:``}) {
  ${classname} ${name_ret};
  ${symbol?code:code.replace(/a\[/g,'this->mvec[')}
  return ${name_ret};
};`

// c++ example for CGA
var CGA=(basis,classname)=>({
preamble:`
// PGA is point based. Vectors are points.
static ${classname} e1(1.0f,1), e2(1.0f,2), e3(1.0f,3), e4(1.0f,4), e5(1.0f,5);

// We seldomly work in the natural basis, but instead in a null basis
// for this we create two null vectors 'origin' and 'infinity'
static ${classname} eo = e4+e5, ei = 0.5f*(e5-e4);

// create a point from x,y,z coordinates
static ${classname} up(float x, float y, float z) {
  float d = x*x + y*y + z*z;
  return x*e1 + y*e2 + z*e3 + 0.5f*d*ei + eo;
}

`,
amble:`
  ${classname} px = up(1.0,2.0,3.0);
  ${classname} line = px ^ eo ^ ei;
  ${classname} sphere = !(eo-ei);
  printf("a point       : "); px.log();
  printf("a line        : "); line.log();
  printf("a sphere      : "); sphere.log();
`
})

// c++ example for algebras without example
var GENERIC = (basis,classname)=>({
preamble:`
${basis.slice(1).map((x,i)=>`static ${classname} ${x}(1.0f,${i+1});`).join('\n')}
`,
amble:`
  printf("${basis[1]}*${basis[1]}         : "); (${basis[1]}*${basis[1]}).log();
  printf("pss           : "); ${basis[basis.length-1]}.log();
  printf("pss*pss       : "); (${basis[basis.length-1]}*${basis[basis.length-1]}).log();
`
})

// c++ example for PGA
var PGA3D = (basis,classname)=>({
preamble:`
// A rotor (Euclidean line) and translator (Ideal line)
static ${classname} rotor(float angle, ${classname} line) { return cos(angle/2.0f) + sin(angle/2.0f)*line.normalized(); }
static ${classname} translator(float dist, ${classname} line) { return 1.0f + dist/2.0f*line; }

// PGA is plane based. Vectors are planes. (think linear functionals)
static ${classname} e0(1.0f,1), e1(1.0f,2), e2(1.0f,3), e3(1.0f,4);

// A plane is defined using its homogenous equation ax + by + cz + d = 0
static ${classname} plane(float a,float b,float c,float d) { return a*e1 + b*e2 + c*e3 + d*e0; }

// PGA points are trivectors.
static ${classname} e123 = e1^e2^e3, e032 = e0^e3^e2, e013 = e0^e1^e3, e021 = e0^e2^e1;

// A point is just a homogeneous point, euclidean coordinates plus the origin
static ${classname} point(float x, float y, float z) { return e123 + x*e032 + y*e013 + z*e021; }

// for our toy problem (generate points on the surface of a torus)
// we start with a function that generates motors.
// circle(t) with t going from 0 to 1.
static ${classname} circle(float t, float radius, ${classname} line) {
  return rotor(t*2.0f*PI,line) * translator(radius,e1*e0);
}

// a torus is now the product of two circles.
static ${classname} torus(float s, float t, float r1, ${classname} l1, float r2, ${classname} l2) {
  return circle(s,r2,l2)*circle(t,r1,l1);
}

// and to sample its points we simply sandwich the origin ..
static ${classname} point_on_torus(float s, float t) {
  ${classname} to = torus(s,t,0.25f,e1*e2,0.6f,e1*e3);
  return to * e123 * ~to;
}
`,
amble:`
  // Elements of the even subalgebra (scalar + bivector + pss) of unit length are motors
  ${classname} rot = rotor( PI/2.0f, e1 * e2 );

  // The outer product ^ is the MEET. Here we intersect the yz (x=0) and xz (y=0) planes.
  ${classname} ax_z = e1 ^ e2;

  // line and plane meet in point. We intersect the line along the z-axis (x=0,y=0) with the xy (z=0) plane.
  ${classname} orig = ax_z ^ e3;

  // We can also easily create points and join them into a line using the regressive (vee, &) product.
  ${classname} px = point(1.0,0.0,0.0);
  ${classname} line = orig & px;

  // Lets also create the plane with equation 2x + z - 3 = 0
  ${classname} p = plane(2,0,1,-3);

  // rotations work on all elements
  ${classname} rotated_plane = rot * p * ~rot;
  ${classname} rotated_line  = rot * line * ~rot;
  ${classname} rotated_point = rot * px * ~rot;

  // See the 3D PGA Cheat sheet for a huge collection of useful formulas
  ${classname} point_on_plane = (p | px) * p;

  // Some output.
  printf("a point       : "); px.log();
  printf("a line        : "); line.log();
  printf("a plane       : "); p.log();
  printf("a rotor       : "); rot.log();
  printf("rotated line  : "); rotated_line.log();
  printf("rotated point : "); rotated_point.log();
  printf("rotated plane : "); rotated_plane.log();
  printf("point on plane: "); point_on_plane.normalized().log();
  printf("point on torus: "); point_on_torus(0.0f,0.0f).log();
  (e0-1.0f).log();
  (1.0f-e0).log();
`})

// c++ Template for the postamble
var postamble = (basis, classname, example)=>
`
inline float ${classname}::norm() { return sqrt(std::abs(((*this)*Conjugate()).mvec[0])); }
inline float ${classname}::inorm() { return (!(*this)).norm(); }
inline ${classname} ${classname}::normalized() { return (*this) * (1/norm()); }

${example.preamble}

int main (int argc, char **argv) {
  ${example.amble}
  return 0;
}`;

Object.assign(exports,{preamble,postamble,unary,binary,desc:"c++",PGA3D,CGA,GENERIC});
<|endoftext|>