<commit_before>"use strict";

var app = angular.module('js120', []);

app.controller('InstrumentController', ['$scope', 'InstrumentService', 'SequencerService', function($scope, InstrumentService, SequencerService) {
  var instrumentID = 1;

  $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    instrumentID = SequencerService.trackByID(args.trackID).instrumentID;
    $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  });

  $scope.$on('InstrumentService.update', function(event) {
    $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  });

  $scope.updateInstrument = function() {
    InstrumentService.updateInstrument();
  };
}]);

app.controller('PatternCollectionController', ['$rootScope', '$scope', 'PatternService', 'SequencerService', function($rootScope, $scope, PatternService, SequencerService) {
  var instrumentID = 1;

  var buildPatternOptions = function() {
    return PatternService.patternsByInstrumentID(instrumentID).map(function(pattern) {
     return { id: pattern.id, name: pattern.name };
    });
  };

  $scope.patternOptions = buildPatternOptions();
  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    instrumentID = SequencerService.trackByID(args.trackID).instrumentID;
    $scope.patternOptions = buildPatternOptions();
    $scope.changeSelectedPattern($scope.patternOptions[0].id);
  });
  $scope.$on('PatternService.update', function(event) {
    $scope.patternOptions = buildPatternOptions();
  });

  $scope.selectedPatternID = 1;

  $scope.addPattern = function() {
    var newPattern = PatternService.addPattern(instrumentID);
    $scope.changeSelectedPattern(newPattern.id);
  };

  $scope.removePattern = function(patternID) {
    var i;
    var newSelectedPatternID;

    if (patternID === $scope.selectedPatternID) {
      i = 0;
      while (i < $scope.patternOptions.length && $scope.patternOptions[i].id !== patternID) {
        i++;
      }

      if (i === ($scope.patternOptions.length - 1)) {
        newSelectedPatternID = $scope.patternOptions[i - 1].id;
      }
      else {
        newSelectedPatternID = $scope.patternOptions[i + 1].id;
      }
    }
    else {
      newSelectedPatternID = $scope.selectedPatternID;
    }

    SequencerService.unsetPattern(patternID);
    PatternService.removePattern(patternID);

    if (newSelectedPatternID !== patternID) {
      $scope.changeSelectedPattern(newSelectedPatternID);
    }
  };

  $scope.changeSelectedPattern = function(patternID) {
    $scope.selectedPatternID = patternID;
    $rootScope.$broadcast('PatternCollectionController.selectedPatternChanged', { patternID: patternID });
  };
}]);

app.controller('PatternController', ['$scope', 'InstrumentService', 'PatternService', function($scope, InstrumentService, PatternService) {
  var instrumentID = 1;
  $scope.pattern = PatternService.patternByID(1);

  var buildInstrumentOptions = function() {
    return InstrumentService.instruments().map(function(instrument) {
     return { id: instrument.id, name: instrument.name };
    });
  };

  $scope.instrumentOptions = buildInstrumentOptions();
  $scope.$on('InstrumentService.update', function(event) {
    $scope.instrumentOptions = buildInstrumentOptions();
  });
  $scope.$on('PatternCollectionController.selectedPatternChanged', function(event, args) {
    $scope.pattern = PatternService.patternByID(args.patternID);
  });

  $scope.$on('PatternService.update', function(event) {
    $scope.pattern = PatternService.patternByID($scope.pattern.id);
  });

  $scope.updateName = function() {
    PatternService.updateName($scope.pattern.id);
  };

  $scope.changeInstrument = function() {
    PatternService.changeInstrument($scope.pattern.id);
  };

  $scope.addTrack = function() {
    PatternService.addTrack($scope.pattern.id);
  };

  $scope.removeTrack = function(trackIndex) {
    PatternService.removeTrack($scope.pattern.id, trackIndex);
  };

  $scope.toggleTrackMute = function(trackIndex) {
    PatternService.toggleTrackMute($scope.pattern.id, trackIndex);
  };

  $scope.updateNotes = function(trackIndex, noteIndex) {
    PatternService.updateNotes($scope.pattern.id, trackIndex, noteIndex);
  };
}]);

app.controller('SequencerController', ['$rootScope', '$scope', '$interval', 'InstrumentService', 'PatternService', 'SequencerService', 'TransportService', function($rootScope, $scope, $interval, InstrumentService, PatternService, SequencerService, TransportService) {
  var timeoutId;

  $scope.tracks = SequencerService.tracks();
  $scope.selectedTrack = SequencerService.trackByID(1);
  $scope.currentStep = null;

  var buildPatternOptions = function() {
    var patternOptions = InstrumentService.instruments().map(function(instrument) {
      var instrumentPatterns = PatternService.patternsByInstrumentID(instrument.id);
      var options = instrumentPatterns.map(function(pattern) {
        return { id: pattern.id, name: pattern.name, }
      });

      options.unshift({ id: -1, name: ''})

      return options;
    });

    return patternOptions;
  };

  $scope.$on('TransportService.start', function(event) {
    var timeoutId = $interval(function() {
      $scope.syncCurrentStep();
    }, 15);
  });

  $scope.$on('TransportService.stop', function(event) {
    $interval.cancel(timeoutId);
  });

  $scope.patternOptions = buildPatternOptions();
  $scope.$on('PatternService.update', function(event) {
    $scope.patternOptions = buildPatternOptions();
  });

  $scope.changeSequencer = function(sequenceIndex) {
    SequencerService.changeSequencer(sequenceIndex);
  };

  $scope.addTrack = function() {
    SequencerService.addTrack();
  };

  $scope.removeTrack = function(trackID) {
    SequencerService.removeTrack(trackID);
  };

  $scope.toggleTrackMute = function(trackID) {
    SequencerService.toggleTrackMute(trackID);
  };

  $scope.changeSelectedTrack = function(trackID) {
    $scope.selectedTrack = SequencerService.trackByID(trackID);
    $rootScope.$broadcast('SequencerController.selectedTrackChanged', { trackID: $scope.selectedTrack.id, });
  };

  $scope.changeTrackName = function(trackID) {
    $rootScope.$broadcast('SequencerController.trackNameChanged', { trackID: trackID, });
  }

  $scope.syncCurrentStep = function() {
    if (TransportService.currentStep()) {
      $scope.currentStep = Math.floor((TransportService.currentStep() / 16) % 8) + 1;
    }
    else
    {
      $scope.currentStep = null;
    }
  };
}]);

app.controller('TrackEditorController', ['$scope', 'SequencerService', function($scope, SequencerService) {
  $scope.trackID = 1;
  $scope.trackName = SequencerService.trackByID($scope.trackID).name;

  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    var track = SequencerService.trackByID(args.trackID);
    $scope.trackID = track.id;
    $scope.trackName = track.name;
  });

  $scope.$on('SequencerController.trackNameChanged', function(event, args) {
    if (args.trackID === $scope.trackID) {
      var track = SequencerService.trackByID(args.trackID);
      $scope.trackName = track.name;
    }
  });
}]);

app.controller('TransportController', ['$scope', 'SerializationService', 'TransportService', function($scope, SerializationService, TransportService) {
  $scope.playing = false;
  $scope.amplitude = 0.25;
  $scope.tempo = 100;
  $scope.loop = true;
  $scope.downloadFileName = "js-120";

  TransportService.setPatterns(SerializationService.serialize());
  $scope.$on('InstrumentService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });
  $scope.$on('PatternService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });
  $scope.$on('SequencerService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });

  $scope.updateTempo = function() {
    TransportService.setTempo(parseInt($scope.tempo, 10));
  };

  $scope.updateAmplitude = function() {
    TransportService.setAmplitude(parseFloat($scope.amplitude));
  };

  $scope.toggle = function() {
    TransportService.toggle();
    $scope.playing = !$scope.playing;
  };

  $scope.updateLoop = function() {
    TransportService.loop = $scope.loop;
  };

  $scope.export = function() {
    var exportCompleteCallback = function(blob) {
      var url  = window.URL.createObjectURL(blob);

      var hiddenDownloadLink = document.getElementById("hidden-download-link");
      if (typeof hiddenDownloadLink.download != "undefined") {
        hiddenDownloadLink.download = $scope.downloadFileName + ".wav";
        hiddenDownloadLink.href = url;
        hiddenDownloadLink.click();
      }
      else {
        alert("Downloading to Wave file is not supported in your browser.");
      }

      window.URL.revokeObjectURL(blob);
    };

    TransportService.export(exportCompleteCallback);
  };
}]);

// Copied from Angular docs, added to allow using an integer value
// with a <select> tag.
app.directive('convertToNumber', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$parsers.push(function(val) {
        return parseInt(val, 10);
      });
      ngModel.$formatters.push(function(val) {
        return '' + val;
      });
    }
  };
});

app.directive('instrumentEditor', function() {
  var controller = ['$scope', function ($scope) { }];

  return {
    restrict: 'A',
    scope: {
      instrument: '=',
      updateInstrument: '&',
    },
    controller: controller,
    templateUrl: 'instrument.html',
  };
});

app.directive('tabList', function() {
  return {
    restrict: 'A',
    transclude: true,
    scope: {
      title: '@',
    },
    controller: ['$scope', function($scope) {
      var panes = $scope.panes = [];

      $scope.select = function(pane) {
        panes.forEach(function(pane) {
          pane.selected = false;
        });
        pane.selected = true;
      };

      this.addPane = function(pane) {
        if (panes.length === 0) {
          $scope.select(pane);
        }
        panes.push(pane);
      };
    }],
    templateUrl: 'tabList.html'
  };
});

app.directive('tabPane', function() {
  return {
    require: '^^tabList',
    restrict: 'A',
    transclude: true,
    scope: {
      title: '@'
    },
    link: function(scope, element, attrs, tabsCtrl) {
      tabsCtrl.addPane(scope);
    },
    templateUrl: 'tabPane.html'
  };
});

app.directive('noteInput', function () {
  return {
    require: 'ngModel',
    link: function (scope, element, attrs, ctrl) {
       if (!ctrl) return;

       var formatNoteValue = function(rawValue) {
         var formattedValue = rawValue;

         // Make first character uppercase (but not subsequent characters, to avoid
         // making a 'b' uppercase, which will mess with ♭ replacement).
         var firstCharacter = formattedValue.substr(0, 1);
         formattedValue = firstCharacter.toUpperCase() + formattedValue.substr(1);

         formattedValue = formattedValue.replace("##", "𝄪");
         formattedValue = formattedValue.replace("#", "♯");
         formattedValue = formattedValue.replace("bb", "𝄫");
         formattedValue = formattedValue.replace("b", "♭");
         formattedValue = formattedValue.replace("-", "—");

         return formattedValue;
       };

       ctrl.$formatters.push(function (a) {
         return formatNoteValue(ctrl.$modelValue);
       });

       ctrl.$parsers.unshift(function (viewValue) {
         var parsedValue = viewValue;

         // Make first character uppercase (but not subsequent characters, to avoid
         // making a 'b' uppercase, which will mess with ♭ replacement).
         var firstCharacter = viewValue.substr(0, 1);
         parsedValue = firstCharacter.toUpperCase() + viewValue.substr(1);
         parsedValue = parsedValue.replace("♯", "#");
         parsedValue = parsedValue.replace("𝄪", "##");
         parsedValue = parsedValue.replace("♭", "b");
         parsedValue = parsedValue.replace("𝄫", "bb");

         if (/^$|^-$|(^[A-G](b|bb|#|##){0,1}[0-7]$)/.test(parsedValue)) {
           ctrl.$setValidity('noteInput', true);
           return parsedValue;
         }
         else {
           ctrl.$setValidity('noteInput', false);
           return '';
         }
       });

       element.bind('blur', function(e) {
         element.val(formatNoteValue(element.val()));
       });

       element.bind('keydown', function(e) {
         var changeCurrentlySelectedNote = function(element, config) {
           var patternID = parseInt(element[0].id.split("-")[1], 10);
           var trackIndex = parseInt(element[0].id.split("-")[3], 10);
           var noteIndex = parseInt(element[0].id.split("-")[5], 10);
           var nextNoteId = 'pattern-' + patternID + '-track-' + (trackIndex + config.trackIndexDelta) + '-note-' + (noteIndex + config.noteIndexDelta);

           document.getElementById(nextNoteId).focus();
         };

         var currentValue = element.val();

         if (e.keyCode === 32) {  // Space bar
           element.val('');
         }
         else if (e.keyCode >= 48 && e.keyCode <= 57) {  // Numbers 0 through 9
           if (/^.*\d$/.test(currentValue)) {
             element.val(currentValue.slice(0, currentValue.length - 1));
           }
         }
         else if (e.keyCode === 189) {  // Dash
           element.val('');
         }
         else if (e.keyCode === 37) {  // Left arrow key
           if (element[0].selectionStart === 0 && !(element.hasClass('firstNote'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 0, noteIndexDelta: -1 });
           }
         }
         else if (e.keyCode === 39) {  // Right arrow key
           if (element[0].selectionEnd === currentValue.length && !(element.hasClass('lastNote'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 0, noteIndexDelta: 1 });
           }
         }
         else if (e.keyCode === 38) {  // Up arrow key
           if (!(element.hasClass('firstTrack'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: -1, noteIndexDelta: 0 });
           }
         }
         else if (e.keyCode === 40) {  // Down arrow key
           if (!(element.hasClass('lastTrack'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 1, noteIndexDelta: 0 });
           }
         }
       });

       element.bind('keyup', function(e) {
         if (e.keyCode === 32) {  // Space bar
           element.val('');
         }
       });
    }
  };
});
<commit_msg>Adding semi-colon<commit_after>"use strict";

var app = angular.module('js120', []);

app.controller('InstrumentController', ['$scope', 'InstrumentService', 'SequencerService', function($scope, InstrumentService, SequencerService) {
  var instrumentID = 1;

  $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    instrumentID = SequencerService.trackByID(args.trackID).instrumentID;
    $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  });

  $scope.$on('InstrumentService.update', function(event) {
    $scope.instrument = InstrumentService.instrumentByID(instrumentID);
  });

  $scope.updateInstrument = function() {
    InstrumentService.updateInstrument();
  };
}]);

app.controller('PatternCollectionController', ['$rootScope', '$scope', 'PatternService', 'SequencerService', function($rootScope, $scope, PatternService, SequencerService) {
  var instrumentID = 1;

  var buildPatternOptions = function() {
    return PatternService.patternsByInstrumentID(instrumentID).map(function(pattern) {
     return { id: pattern.id, name: pattern.name };
    });
  };

  $scope.patternOptions = buildPatternOptions();
  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    instrumentID = SequencerService.trackByID(args.trackID).instrumentID;
    $scope.patternOptions = buildPatternOptions();
    $scope.changeSelectedPattern($scope.patternOptions[0].id);
  });
  $scope.$on('PatternService.update', function(event) {
    $scope.patternOptions = buildPatternOptions();
  });

  $scope.selectedPatternID = 1;

  $scope.addPattern = function() {
    var newPattern = PatternService.addPattern(instrumentID);
    $scope.changeSelectedPattern(newPattern.id);
  };

  $scope.removePattern = function(patternID) {
    var i;
    var newSelectedPatternID;

    if (patternID === $scope.selectedPatternID) {
      i = 0;
      while (i < $scope.patternOptions.length && $scope.patternOptions[i].id !== patternID) {
        i++;
      }

      if (i === ($scope.patternOptions.length - 1)) {
        newSelectedPatternID = $scope.patternOptions[i - 1].id;
      }
      else {
        newSelectedPatternID = $scope.patternOptions[i + 1].id;
      }
    }
    else {
      newSelectedPatternID = $scope.selectedPatternID;
    }

    SequencerService.unsetPattern(patternID);
    PatternService.removePattern(patternID);

    if (newSelectedPatternID !== patternID) {
      $scope.changeSelectedPattern(newSelectedPatternID);
    }
  };

  $scope.changeSelectedPattern = function(patternID) {
    $scope.selectedPatternID = patternID;
    $rootScope.$broadcast('PatternCollectionController.selectedPatternChanged', { patternID: patternID });
  };
}]);

app.controller('PatternController', ['$scope', 'InstrumentService', 'PatternService', function($scope, InstrumentService, PatternService) {
  var instrumentID = 1;
  $scope.pattern = PatternService.patternByID(1);

  var buildInstrumentOptions = function() {
    return InstrumentService.instruments().map(function(instrument) {
     return { id: instrument.id, name: instrument.name };
    });
  };

  $scope.instrumentOptions = buildInstrumentOptions();
  $scope.$on('InstrumentService.update', function(event) {
    $scope.instrumentOptions = buildInstrumentOptions();
  });
  $scope.$on('PatternCollectionController.selectedPatternChanged', function(event, args) {
    $scope.pattern = PatternService.patternByID(args.patternID);
  });

  $scope.$on('PatternService.update', function(event) {
    $scope.pattern = PatternService.patternByID($scope.pattern.id);
  });

  $scope.updateName = function() {
    PatternService.updateName($scope.pattern.id);
  };

  $scope.changeInstrument = function() {
    PatternService.changeInstrument($scope.pattern.id);
  };

  $scope.addTrack = function() {
    PatternService.addTrack($scope.pattern.id);
  };

  $scope.removeTrack = function(trackIndex) {
    PatternService.removeTrack($scope.pattern.id, trackIndex);
  };

  $scope.toggleTrackMute = function(trackIndex) {
    PatternService.toggleTrackMute($scope.pattern.id, trackIndex);
  };

  $scope.updateNotes = function(trackIndex, noteIndex) {
    PatternService.updateNotes($scope.pattern.id, trackIndex, noteIndex);
  };
}]);

app.controller('SequencerController', ['$rootScope', '$scope', '$interval', 'InstrumentService', 'PatternService', 'SequencerService', 'TransportService', function($rootScope, $scope, $interval, InstrumentService, PatternService, SequencerService, TransportService) {
  var timeoutId;

  $scope.tracks = SequencerService.tracks();
  $scope.selectedTrack = SequencerService.trackByID(1);
  $scope.currentStep = null;

  var buildPatternOptions = function() {
    var patternOptions = InstrumentService.instruments().map(function(instrument) {
      var instrumentPatterns = PatternService.patternsByInstrumentID(instrument.id);
      var options = instrumentPatterns.map(function(pattern) {
        return { id: pattern.id, name: pattern.name, }
      });

      options.unshift({ id: -1, name: ''});

      return options;
    });

    return patternOptions;
  };

  $scope.$on('TransportService.start', function(event) {
    var timeoutId = $interval(function() {
      $scope.syncCurrentStep();
    }, 15);
  });

  $scope.$on('TransportService.stop', function(event) {
    $interval.cancel(timeoutId);
  });

  $scope.patternOptions = buildPatternOptions();
  $scope.$on('PatternService.update', function(event) {
    $scope.patternOptions = buildPatternOptions();
  });

  $scope.changeSequencer = function(sequenceIndex) {
    SequencerService.changeSequencer(sequenceIndex);
  };

  $scope.addTrack = function() {
    SequencerService.addTrack();
  };

  $scope.removeTrack = function(trackID) {
    SequencerService.removeTrack(trackID);
  };

  $scope.toggleTrackMute = function(trackID) {
    SequencerService.toggleTrackMute(trackID);
  };

  $scope.changeSelectedTrack = function(trackID) {
    $scope.selectedTrack = SequencerService.trackByID(trackID);
    $rootScope.$broadcast('SequencerController.selectedTrackChanged', { trackID: $scope.selectedTrack.id, });
  };

  $scope.changeTrackName = function(trackID) {
    $rootScope.$broadcast('SequencerController.trackNameChanged', { trackID: trackID, });
  }

  $scope.syncCurrentStep = function() {
    if (TransportService.currentStep()) {
      $scope.currentStep = Math.floor((TransportService.currentStep() / 16) % 8) + 1;
    }
    else
    {
      $scope.currentStep = null;
    }
  };
}]);

app.controller('TrackEditorController', ['$scope', 'SequencerService', function($scope, SequencerService) {
  $scope.trackID = 1;
  $scope.trackName = SequencerService.trackByID($scope.trackID).name;

  $scope.$on('SequencerController.selectedTrackChanged', function(event, args) {
    var track = SequencerService.trackByID(args.trackID);
    $scope.trackID = track.id;
    $scope.trackName = track.name;
  });

  $scope.$on('SequencerController.trackNameChanged', function(event, args) {
    if (args.trackID === $scope.trackID) {
      var track = SequencerService.trackByID(args.trackID);
      $scope.trackName = track.name;
    }
  });
}]);

app.controller('TransportController', ['$scope', 'SerializationService', 'TransportService', function($scope, SerializationService, TransportService) {
  $scope.playing = false;
  $scope.amplitude = 0.25;
  $scope.tempo = 100;
  $scope.loop = true;
  $scope.downloadFileName = "js-120";

  TransportService.setPatterns(SerializationService.serialize());
  $scope.$on('InstrumentService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });
  $scope.$on('PatternService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });
  $scope.$on('SequencerService.update', function(event) {
    TransportService.setPatterns(SerializationService.serialize());
  });

  $scope.updateTempo = function() {
    TransportService.setTempo(parseInt($scope.tempo, 10));
  };

  $scope.updateAmplitude = function() {
    TransportService.setAmplitude(parseFloat($scope.amplitude));
  };

  $scope.toggle = function() {
    TransportService.toggle();
    $scope.playing = !$scope.playing;
  };

  $scope.updateLoop = function() {
    TransportService.loop = $scope.loop;
  };

  $scope.export = function() {
    var exportCompleteCallback = function(blob) {
      var url  = window.URL.createObjectURL(blob);

      var hiddenDownloadLink = document.getElementById("hidden-download-link");
      if (typeof hiddenDownloadLink.download != "undefined") {
        hiddenDownloadLink.download = $scope.downloadFileName + ".wav";
        hiddenDownloadLink.href = url;
        hiddenDownloadLink.click();
      }
      else {
        alert("Downloading to Wave file is not supported in your browser.");
      }

      window.URL.revokeObjectURL(blob);
    };

    TransportService.export(exportCompleteCallback);
  };
}]);

// Copied from Angular docs, added to allow using an integer value
// with a <select> tag.
app.directive('convertToNumber', function() {
  return {
    require: 'ngModel',
    link: function(scope, element, attrs, ngModel) {
      ngModel.$parsers.push(function(val) {
        return parseInt(val, 10);
      });
      ngModel.$formatters.push(function(val) {
        return '' + val;
      });
    }
  };
});

app.directive('instrumentEditor', function() {
  var controller = ['$scope', function ($scope) { }];

  return {
    restrict: 'A',
    scope: {
      instrument: '=',
      updateInstrument: '&',
    },
    controller: controller,
    templateUrl: 'instrument.html',
  };
});

app.directive('tabList', function() {
  return {
    restrict: 'A',
    transclude: true,
    scope: {
      title: '@',
    },
    controller: ['$scope', function($scope) {
      var panes = $scope.panes = [];

      $scope.select = function(pane) {
        panes.forEach(function(pane) {
          pane.selected = false;
        });
        pane.selected = true;
      };

      this.addPane = function(pane) {
        if (panes.length === 0) {
          $scope.select(pane);
        }
        panes.push(pane);
      };
    }],
    templateUrl: 'tabList.html'
  };
});

app.directive('tabPane', function() {
  return {
    require: '^^tabList',
    restrict: 'A',
    transclude: true,
    scope: {
      title: '@'
    },
    link: function(scope, element, attrs, tabsCtrl) {
      tabsCtrl.addPane(scope);
    },
    templateUrl: 'tabPane.html'
  };
});

app.directive('noteInput', function () {
  return {
    require: 'ngModel',
    link: function (scope, element, attrs, ctrl) {
       if (!ctrl) return;

       var formatNoteValue = function(rawValue) {
         var formattedValue = rawValue;

         // Make first character uppercase (but not subsequent characters, to avoid
         // making a 'b' uppercase, which will mess with ♭ replacement).
         var firstCharacter = formattedValue.substr(0, 1);
         formattedValue = firstCharacter.toUpperCase() + formattedValue.substr(1);

         formattedValue = formattedValue.replace("##", "𝄪");
         formattedValue = formattedValue.replace("#", "♯");
         formattedValue = formattedValue.replace("bb", "𝄫");
         formattedValue = formattedValue.replace("b", "♭");
         formattedValue = formattedValue.replace("-", "—");

         return formattedValue;
       };

       ctrl.$formatters.push(function (a) {
         return formatNoteValue(ctrl.$modelValue);
       });

       ctrl.$parsers.unshift(function (viewValue) {
         var parsedValue = viewValue;

         // Make first character uppercase (but not subsequent characters, to avoid
         // making a 'b' uppercase, which will mess with ♭ replacement).
         var firstCharacter = viewValue.substr(0, 1);
         parsedValue = firstCharacter.toUpperCase() + viewValue.substr(1);
         parsedValue = parsedValue.replace("♯", "#");
         parsedValue = parsedValue.replace("𝄪", "##");
         parsedValue = parsedValue.replace("♭", "b");
         parsedValue = parsedValue.replace("𝄫", "bb");

         if (/^$|^-$|(^[A-G](b|bb|#|##){0,1}[0-7]$)/.test(parsedValue)) {
           ctrl.$setValidity('noteInput', true);
           return parsedValue;
         }
         else {
           ctrl.$setValidity('noteInput', false);
           return '';
         }
       });

       element.bind('blur', function(e) {
         element.val(formatNoteValue(element.val()));
       });

       element.bind('keydown', function(e) {
         var changeCurrentlySelectedNote = function(element, config) {
           var patternID = parseInt(element[0].id.split("-")[1], 10);
           var trackIndex = parseInt(element[0].id.split("-")[3], 10);
           var noteIndex = parseInt(element[0].id.split("-")[5], 10);
           var nextNoteId = 'pattern-' + patternID + '-track-' + (trackIndex + config.trackIndexDelta) + '-note-' + (noteIndex + config.noteIndexDelta);

           document.getElementById(nextNoteId).focus();
         };

         var currentValue = element.val();

         if (e.keyCode === 32) {  // Space bar
           element.val('');
         }
         else if (e.keyCode >= 48 && e.keyCode <= 57) {  // Numbers 0 through 9
           if (/^.*\d$/.test(currentValue)) {
             element.val(currentValue.slice(0, currentValue.length - 1));
           }
         }
         else if (e.keyCode === 189) {  // Dash
           element.val('');
         }
         else if (e.keyCode === 37) {  // Left arrow key
           if (element[0].selectionStart === 0 && !(element.hasClass('firstNote'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 0, noteIndexDelta: -1 });
           }
         }
         else if (e.keyCode === 39) {  // Right arrow key
           if (element[0].selectionEnd === currentValue.length && !(element.hasClass('lastNote'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 0, noteIndexDelta: 1 });
           }
         }
         else if (e.keyCode === 38) {  // Up arrow key
           if (!(element.hasClass('firstTrack'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: -1, noteIndexDelta: 0 });
           }
         }
         else if (e.keyCode === 40) {  // Down arrow key
           if (!(element.hasClass('lastTrack'))) {
             changeCurrentlySelectedNote(element, { trackIndexDelta: 1, noteIndexDelta: 0 });
           }
         }
       });

       element.bind('keyup', function(e) {
         if (e.keyCode === 32) {  // Space bar
           element.val('');
         }
       });
    }
  };
});
<|endoftext|>


<commit_before>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FB_TEXT_LIMIT = 640;
var barbrad = 'true';

class FacebookBot {
    constructor() {
        this.apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
        this.sessionIds = new Map();
        this.messagesDelay = 200;
    }

    doDataResponse(sender, facebookResponseData) {
        if (!Array.isArray(facebookResponseData)) {
            console.log('Response as formatted message');
            this.sendFBMessage(sender, facebookResponseData)
                .catch(err => console.error(err));
        } else {
            async.eachSeries(facebookResponseData, (facebookMessage, callback) => {
                if (facebookMessage.sender_action) {
                    console.log('Response as sender action');
                    this.sendFBSenderAction(sender, facebookMessage.sender_action)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
                else {
                    console.log('Response as formatted message');
                    this.sendFBMessage(sender, facebookMessage)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
            }, (err) => {
                if (err) {
                    console.error(err);
                } else {
                    console.log('Data response completed');
                }
            });
        }
    }

    doRichContentResponse(sender, messages) {
        let facebookMessages = []; // array with result messages

        for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
            let message = messages[messageIndex];

            switch (message.type) {
                //message.type 0 means text message
                case 0:
                    // speech: ["hi"]
                    // we have to get value from fulfillment.speech, because of here is raw speech
                    if (message.speech) {

                        let splittedText = this.splitResponse(message.speech);

                        splittedText.forEach(s => {
                            facebookMessages.push({text: s});
                        });
                    }

                    break;
                //message.type 1 means card message
                case 1: {
                    let carousel = [message];

                    for (messageIndex++; messageIndex < messages.length; messageIndex++) {
                        if (messages[messageIndex].type == 1) {
                            carousel.push(messages[messageIndex]);
                        } else {
                            messageIndex--;
                            break;
                        }
                    }

                    let facebookMessage = {};
                    carousel.forEach((c) => {
                        // buttons: [ {text: "hi", postback: "postback"} ], imageUrl: "", title: "", subtitle: ""

                        let card = {};

                        card.title = c.title;
                        card.image_url = c.imageUrl;
                        if (this.isDefined(c.subtitle)) {
                            card.subtitle = c.subtitle;
                        }
                        //If button is involved in.
                        if (c.buttons.length > 0) {
                            let buttons = [];
                            for (let buttonIndex = 0; buttonIndex < c.buttons.length; buttonIndex++) {
                                let button = c.buttons[buttonIndex];

                                if (button.text) {
                                    let postback = button.postback;
                                    if (!postback) {
                                        postback = button.text;
                                    }

                                    let buttonDescription = {
                                        title: button.text
                                    };

                                    if (postback.startsWith("http")) {
                                        buttonDescription.type = "web_url";
                                        buttonDescription.url = postback;
                                    } else {
                                        buttonDescription.type = "postback";
                                        buttonDescription.payload = postback;
                                    }

                                    buttons.push(buttonDescription);
                                }
                            }

                            if (buttons.length > 0) {
                                card.buttons = buttons;
                            }
                        }

                        if (!facebookMessage.attachment) {
                            facebookMessage.attachment = {type: "template"};
                        }

                        if (!facebookMessage.attachment.payload) {
                            facebookMessage.attachment.payload = {template_type: "generic", elements: []};
                        }

                        facebookMessage.attachment.payload.elements.push(card);
                    });

                    facebookMessages.push(facebookMessage);
                }

                    break;
                //message.type 2 means quick replies message
                case 2: {
                    if (message.replies && message.replies.length > 0) {
                        let facebookMessage = {};

                        facebookMessage.text = message.title ? message.title : 'Choose an item';
                        facebookMessage.quick_replies = [];

                        message.replies.forEach((r) => {
                            facebookMessage.quick_replies.push({
                                content_type: "text",
                                title: r,
                                payload: r
                            });
                        });

                        facebookMessages.push(facebookMessage);
                    }
                }

                    break;
                //message.type 3 means image message
                case 3:

                    if (message.imageUrl) {
                        let facebookMessage = {};

                        // "imageUrl": "http://example.com/image.jpg"
                        facebookMessage.attachment = {type: "image"};
                        facebookMessage.attachment.payload = {url: message.imageUrl};

                        facebookMessages.push(facebookMessage);
                    }

                    break;
                //message.type 4 means custom payload message
                case 4:
                    if (message.payload && message.payload.facebook) {
                        facebookMessages.push(message.payload.facebook);
                    }
                    break;

                default:
                    break;
            }
        }

        return new Promise((resolve, reject) => {
            async.eachSeries(facebookMessages, (msg, callback) => {
                    this.sendFBSenderAction(sender, "typing_on")
                        .then(() => this.sleep(this.messagesDelay))
                        .then(() => this.sendFBMessage(sender, msg))
                        .then(() => callback())
                        .catch(callback);
                },
                (err) => {
                    if (err) {
                        console.error(err);
                        reject(err);
                    } else {
                        console.log('Messages sent');
                        resolve();
                    }
                });
        });

    }

    doTextResponse(sender, responseText) {
        console.log('Response as text message');
        // facebook API limit for text length is 640,
        // so we must split message if needed
        let splittedText = this.splitResponse(responseText);

        async.eachSeries(splittedText, (textPart, callback) => {
            this.sendFBMessage(sender, {text: textPart})
                .then(() => callback())
                .catch(err => callback(err));
        });
    }
 //which webhook event
    getEventText(event) {
        if (event.message) {
            if (event.message.quick_reply && event.message.quick_reply.payload) {
                return event.message.quick_reply.payload;
            }

            if (event.message.text) {
                return event.message.text;
            }
        }

        if (event.postback && event.postback.payload) {
                if (event.postback.referral.ref) {
                    console.log(event.postback.referral.ref);
                } else {
                    console.log(event.postback.payload);
                    //console.log(event.postback.referral.ref);
                    console.log('testing');
                    return event.postback.payload;
                }
            }

        return null;

    }

    processEvent(event) {
        const sender = event.sender.id.toString();
        const text = this.getEventText(event);

        if (text) {

            // Handle a text message from the sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
            }

            console.log("Text", text);
            //send user's text to api.ai service
            let apiaiRequest = this.apiAiService.textRequest(text,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });

            //get response from api.ai
            apiaiRequest.on('response', (response) => {
                if (this.isDefined(response.result) && this.isDefined(response.result.fulfillment)) {
                    let responseText = response.result.fulfillment.speech;
                    let responseData = response.result.fulfillment.data;
                    let responseMessages = response.result.fulfillment.messages;

                    let action = response.result.action;

                    if (this.isDefined(responseData) && this.isDefined(responseData.facebook)) {
                        let facebookResponseData = responseData.facebook;
                        this.doDataResponse(sender, facebookResponseData);
                    } else if (this.isDefined(responseMessages) && responseMessages.length > 0) {
                        this.doRichContentResponse(sender, responseMessages);
                    }
                    else if (this.isDefined(responseText)) {
                        this.doTextResponse(sender, responseText);
                    }

                }
            });

            apiaiRequest.on('error', (error) => console.error(error));
            apiaiRequest.end();
        }
    }

    splitResponse(str) {
        if (str.length <= FB_TEXT_LIMIT) {
            return [str];
        }

        return this.chunkString(str, FB_TEXT_LIMIT);
    }

    chunkString(s, len) {
        let curr = len, prev = 0;

        let output = [];

        while (s[curr]) {
            if (s[curr++] == ' ') {
                output.push(s.substring(prev, curr));
                prev = curr;
                curr += len;
            }
            else {
                let currReverse = curr;
                do {
                    if (s.substring(currReverse - 1, currReverse) == ' ') {
                        output.push(s.substring(prev, currReverse));
                        prev = currReverse;
                        curr = currReverse + len;
                        break;
                    }
                    currReverse--;
                } while (currReverse > prev)
            }
        }
        output.push(s.substr(prev));
        return output;
    }

    sendFBMessage(sender, messageData) {
        return new Promise((resolve, reject) => {
            request({
                url: 'https://graph.facebook.com/v2.6/me/messages',
                qs: {access_token: FB_PAGE_ACCESS_TOKEN},
                method: 'POST',
                json: {
                    recipient: {id: sender},
                    message: messageData
                }
            }, (error, response) => {
                if (error) {
                    console.log('Error sending message: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.log('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    sendFBSenderAction(sender, action) {
        return new Promise((resolve, reject) => {
            request({
                url: 'https://graph.facebook.com/v2.6/me/messages',
                qs: {access_token: FB_PAGE_ACCESS_TOKEN},
                method: 'POST',
                json: {
                    recipient: {id: sender},
                    sender_action: action
                }
            }, (error, response) => {
                if (error) {
                    console.error('Error sending action: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.error('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    doSubscribeRequest() {
        request({
                method: 'POST',
                uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
            },
            (error, response, body) => {
                if (error) {
                    console.error('Error while subscription: ', error);
                } else {
                    console.log('Subscription result: ', response.body);
                }
            });
    }

    isDefined(obj) {
        if (typeof obj == 'undefined') {
            return false;
        }

        if (!obj) {
            return false;
        }

        return obj != null;
    }

    sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), delay);
        });
    }

}

let facebookBot = new FacebookBot();

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            facebookBot.doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        const data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                    if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            facebookBot.processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

facebookBot.doSubscribeRequest();
<commit_msg>Update app.js<commit_after>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FB_TEXT_LIMIT = 640;
var barbrad = 'true';

class FacebookBot {
    constructor() {
        this.apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
        this.sessionIds = new Map();
        this.messagesDelay = 200;
    }

    doDataResponse(sender, facebookResponseData) {
        if (!Array.isArray(facebookResponseData)) {
            console.log('Response as formatted message');
            this.sendFBMessage(sender, facebookResponseData)
                .catch(err => console.error(err));
        } else {
            async.eachSeries(facebookResponseData, (facebookMessage, callback) => {
                if (facebookMessage.sender_action) {
                    console.log('Response as sender action');
                    this.sendFBSenderAction(sender, facebookMessage.sender_action)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
                else {
                    console.log('Response as formatted message');
                    this.sendFBMessage(sender, facebookMessage)
                        .then(() => callback())
                        .catch(err => callback(err));
                }
            }, (err) => {
                if (err) {
                    console.error(err);
                } else {
                    console.log('Data response completed');
                }
            });
        }
    }

    doRichContentResponse(sender, messages) {
        let facebookMessages = []; // array with result messages

        for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
            let message = messages[messageIndex];

            switch (message.type) {
                //message.type 0 means text message
                case 0:
                    // speech: ["hi"]
                    // we have to get value from fulfillment.speech, because of here is raw speech
                    if (message.speech) {

                        let splittedText = this.splitResponse(message.speech);

                        splittedText.forEach(s => {
                            facebookMessages.push({text: s});
                        });
                    }

                    break;
                //message.type 1 means card message
                case 1: {
                    let carousel = [message];

                    for (messageIndex++; messageIndex < messages.length; messageIndex++) {
                        if (messages[messageIndex].type == 1) {
                            carousel.push(messages[messageIndex]);
                        } else {
                            messageIndex--;
                            break;
                        }
                    }

                    let facebookMessage = {};
                    carousel.forEach((c) => {
                        // buttons: [ {text: "hi", postback: "postback"} ], imageUrl: "", title: "", subtitle: ""

                        let card = {};

                        card.title = c.title;
                        card.image_url = c.imageUrl;
                        if (this.isDefined(c.subtitle)) {
                            card.subtitle = c.subtitle;
                        }
                        //If button is involved in.
                        if (c.buttons.length > 0) {
                            let buttons = [];
                            for (let buttonIndex = 0; buttonIndex < c.buttons.length; buttonIndex++) {
                                let button = c.buttons[buttonIndex];

                                if (button.text) {
                                    let postback = button.postback;
                                    if (!postback) {
                                        postback = button.text;
                                    }

                                    let buttonDescription = {
                                        title: button.text
                                    };

                                    if (postback.startsWith("http")) {
                                        buttonDescription.type = "web_url";
                                        buttonDescription.url = postback;
                                    } else {
                                        buttonDescription.type = "postback";
                                        buttonDescription.payload = postback;
                                    }

                                    buttons.push(buttonDescription);
                                }
                            }

                            if (buttons.length > 0) {
                                card.buttons = buttons;
                            }
                        }

                        if (!facebookMessage.attachment) {
                            facebookMessage.attachment = {type: "template"};
                        }

                        if (!facebookMessage.attachment.payload) {
                            facebookMessage.attachment.payload = {template_type: "generic", elements: []};
                        }

                        facebookMessage.attachment.payload.elements.push(card);
                    });

                    facebookMessages.push(facebookMessage);
                }

                    break;
                //message.type 2 means quick replies message
                case 2: {
                    if (message.replies && message.replies.length > 0) {
                        let facebookMessage = {};

                        facebookMessage.text = message.title ? message.title : 'Choose an item';
                        facebookMessage.quick_replies = [];

                        message.replies.forEach((r) => {
                            facebookMessage.quick_replies.push({
                                content_type: "text",
                                title: r,
                                payload: r
                            });
                        });

                        facebookMessages.push(facebookMessage);
                    }
                }

                    break;
                //message.type 3 means image message
                case 3:

                    if (message.imageUrl) {
                        let facebookMessage = {};

                        // "imageUrl": "http://example.com/image.jpg"
                        facebookMessage.attachment = {type: "image"};
                        facebookMessage.attachment.payload = {url: message.imageUrl};

                        facebookMessages.push(facebookMessage);
                    }

                    break;
                //message.type 4 means custom payload message
                case 4:
                    if (message.payload && message.payload.facebook) {
                        facebookMessages.push(message.payload.facebook);
                    }
                    break;

                default:
                    break;
            }
        }

        return new Promise((resolve, reject) => {
            async.eachSeries(facebookMessages, (msg, callback) => {
                    this.sendFBSenderAction(sender, "typing_on")
                        .then(() => this.sleep(this.messagesDelay))
                        .then(() => this.sendFBMessage(sender, msg))
                        .then(() => callback())
                        .catch(callback);
                },
                (err) => {
                    if (err) {
                        console.error(err);
                        reject(err);
                    } else {
                        console.log('Messages sent');
                        resolve();
                    }
                });
        });

    }

    doTextResponse(sender, responseText) {
        console.log('Response as text message');
        // facebook API limit for text length is 640,
        // so we must split message if needed
        let splittedText = this.splitResponse(responseText);

        async.eachSeries(splittedText, (textPart, callback) => {
            this.sendFBMessage(sender, {text: textPart})
                .then(() => callback())
                .catch(err => callback(err));
        });
    }
 //which webhook event
    getEventText(event) {
        if (event.message) {
            if (event.message.quick_reply && event.message.quick_reply.payload) {
                return event.message.quick_reply.payload;
            }

            if (event.message.text) {
                return event.message.text;
            }
        }

        if (event.postback && event.postback.payload) {
                if (event.postback.referral.ref) {
                    console.log(event.postback.referral.ref);
                    return event.postback.referral.ref;
                } else {
                    console.log(event.postback.payload);
                    //console.log(event.postback.referral.ref);
                    console.log('testing');
                    return event.postback.payload;
                }
            }

        return null;

    }

    processEvent(event) {
        const sender = event.sender.id.toString();
        const text = this.getEventText(event);

        if (text) {

            // Handle a text message from the sender
            if (!this.sessionIds.has(sender)) {
                this.sessionIds.set(sender, uuid.v4());
            }

            console.log("Text", text);
            //send user's text to api.ai service
            let apiaiRequest = this.apiAiService.textRequest(text,
                {
                    sessionId: this.sessionIds.get(sender),
                    originalRequest: {
                        data: event,
                        source: "facebook"
                    }
                });

            //get response from api.ai
            apiaiRequest.on('response', (response) => {
                if (this.isDefined(response.result) && this.isDefined(response.result.fulfillment)) {
                    let responseText = response.result.fulfillment.speech;
                    let responseData = response.result.fulfillment.data;
                    let responseMessages = response.result.fulfillment.messages;

                    let action = response.result.action;

                    if (this.isDefined(responseData) && this.isDefined(responseData.facebook)) {
                        let facebookResponseData = responseData.facebook;
                        this.doDataResponse(sender, facebookResponseData);
                    } else if (this.isDefined(responseMessages) && responseMessages.length > 0) {
                        this.doRichContentResponse(sender, responseMessages);
                    }
                    else if (this.isDefined(responseText)) {
                        this.doTextResponse(sender, responseText);
                    }

                }
            });

            apiaiRequest.on('error', (error) => console.error(error));
            apiaiRequest.end();
        }
    }

    splitResponse(str) {
        if (str.length <= FB_TEXT_LIMIT) {
            return [str];
        }

        return this.chunkString(str, FB_TEXT_LIMIT);
    }

    chunkString(s, len) {
        let curr = len, prev = 0;

        let output = [];

        while (s[curr]) {
            if (s[curr++] == ' ') {
                output.push(s.substring(prev, curr));
                prev = curr;
                curr += len;
            }
            else {
                let currReverse = curr;
                do {
                    if (s.substring(currReverse - 1, currReverse) == ' ') {
                        output.push(s.substring(prev, currReverse));
                        prev = currReverse;
                        curr = currReverse + len;
                        break;
                    }
                    currReverse--;
                } while (currReverse > prev)
            }
        }
        output.push(s.substr(prev));
        return output;
    }

    sendFBMessage(sender, messageData) {
        return new Promise((resolve, reject) => {
            request({
                url: 'https://graph.facebook.com/v2.6/me/messages',
                qs: {access_token: FB_PAGE_ACCESS_TOKEN},
                method: 'POST',
                json: {
                    recipient: {id: sender},
                    message: messageData
                }
            }, (error, response) => {
                if (error) {
                    console.log('Error sending message: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.log('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    sendFBSenderAction(sender, action) {
        return new Promise((resolve, reject) => {
            request({
                url: 'https://graph.facebook.com/v2.6/me/messages',
                qs: {access_token: FB_PAGE_ACCESS_TOKEN},
                method: 'POST',
                json: {
                    recipient: {id: sender},
                    sender_action: action
                }
            }, (error, response) => {
                if (error) {
                    console.error('Error sending action: ', error);
                    reject(error);
                } else if (response.body.error) {
                    console.error('Error: ', response.body.error);
                    reject(new Error(response.body.error));
                }

                resolve();
            });
        });
    }

    doSubscribeRequest() {
        request({
                method: 'POST',
                uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
            },
            (error, response, body) => {
                if (error) {
                    console.error('Error while subscription: ', error);
                } else {
                    console.log('Subscription result: ', response.body);
                }
            });
    }

    isDefined(obj) {
        if (typeof obj == 'undefined') {
            return false;
        }

        if (!obj) {
            return false;
        }

        return obj != null;
    }

    sleep(delay) {
        return new Promise((resolve, reject) => {
            setTimeout(() => resolve(), delay);
        });
    }

}

let facebookBot = new FacebookBot();

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            facebookBot.doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        const data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                    if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            facebookBot.processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

facebookBot.doSubscribeRequest();
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */
var express = require('express')
    , app = express()
    , server = require('http').createServer(app)
    , path = require('path')
    , io = require('socket.io').listen(server)
    , DashboardConfig = require(__dirname + '/dashboard-config.js');

/**
 * Config
 */
var config = require(__dirname + '/../config/' + app.get('env') + '.json');

// CORS middleware
var allowCrossDomain = function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
}

// all environments
app.set('port', process.env.TEST_PORT || 8080);
app.use(express.favicon(path.join(__dirname, '../public/images/favicon.ico')));
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(allowCrossDomain);
app.use(express.static(path.join(__dirname, '../public')));

// development only
if ('development' == app.get('env')) {
    app.use(express.errorHandler());
}

//Routes
app.get('/', function (req, res) {
    res.sendfile(path.join(__dirname, '/../public/index.html'));
});
app.get('/admin', function (req, res) {
    res.sendfile(path.join(__dirname, '/../public/admin.html'));
});

//Socket.io Config
io.set('log level', 1);

server.listen(app.get('port'), function () {
    console.log('Express server listening on port ' + app.get('port'));
});

// Loading modules
var modules = [];
var modulesConf = DashboardConfig.getModulesConf();
modulesConf.forEach(function(moduleConfig) {
    var OfficeModule = require('../public/js/modules/' + moduleConfig['type'] + '/backend');
    modules.push(new OfficeModule(moduleConfig, io.sockets));
});

// Socket.io Server
io.sockets.on('connection', function (socket) {

    socket.on('get-modules', function () {
        console.log("Client requested modules");
        socket.emit('modules', modules);
    });

    socket.on('get-modules-instances', function () {
        console.log("Admin requested modules instances");
        socket.emit('modules-instances', modules);
    });

    socket.on('get-modules-list', function () {
        console.log("Admin requested modules list");
        socket.emit('modules-list', DashboardConfig.listAvailableModules());
    });

    socket.on('add-module-instance', function (moduleConfig) {
        console.log("Admin added a module instance");
        modules = DashboardConfig.addModule(moduleConfig);
    });
});

<commit_msg>pass module conf to frontend app<commit_after>/**
 * Module dependencies.
 */
var express = require('express')
    , app = express()
    , server = require('http').createServer(app)
    , path = require('path')
    , io = require('socket.io').listen(server)
    , DashboardConfig = require(__dirname + '/dashboard-config.js');

/**
 * Config
 */
var config = require(__dirname + '/../config/' + app.get('env') + '.json');

// CORS middleware
var allowCrossDomain = function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
}

// all environments
app.set('port', process.env.TEST_PORT || 8080);
app.use(express.favicon(path.join(__dirname, '../public/images/favicon.ico')));
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(allowCrossDomain);
app.use(express.static(path.join(__dirname, '../public')));

// development only
if ('development' == app.get('env')) {
    app.use(express.errorHandler());
}

//Routes
app.get('/', function (req, res) {
    res.sendfile(path.join(__dirname, '/../public/index.html'));
});
app.get('/admin', function (req, res) {
    res.sendfile(path.join(__dirname, '/../public/admin.html'));
});

//Socket.io Config
io.set('log level', 1);

server.listen(app.get('port'), function () {
    console.log('Express server listening on port ' + app.get('port'));
});

// Loading modules
var modules = [];
var modulesConf = DashboardConfig.getModulesConf();
modulesConf.forEach(function(moduleConfig) {
    var OfficeModule = require('../public/js/modules/' + moduleConfig['type'] + '/backend');
    modules.push(new OfficeModule(moduleConfig, io.sockets));
});

// Socket.io Server
io.sockets.on('connection', function (socket) {

    socket.on('get-modules', function () {
        console.log("Client requested modules");
        socket.emit('modules', modulesConf);
    });

    socket.on('get-modules-instances', function () {
        console.log("Admin requested modules instances");
        socket.emit('modules-instances', modules);
    });

    socket.on('get-modules-list', function () {
        console.log("Admin requested modules list");
        socket.emit('modules-list', DashboardConfig.listAvailableModules());
    });

    socket.on('add-module-instance', function (moduleConfig) {
        console.log("Admin added a module instance");
        modules = DashboardConfig.addModule(moduleConfig);
        modulesConf = DashboardConfig.getModulesConf();
    });

});

<|endoftext|>


<commit_before>'use strict';

import React, { Component, PropTypes } from 'react/addons';
import update from 'react/lib/update';
import classNames from 'classnames'
import SplitPane from 'react-split-pane'
import babel from 'babel-core/lib/babel/api/browser.js'
import falafel from 'falafel';

global.babel = babel
var CodeMirror = require('codemirror')
require("codemirror/lib/codemirror.css");
require("./codemirror.less");
require("codemirror/mode/javascript/javascript")
require("codemirror/keymap/sublime")
// require("codemirror/addon/edit/closebrackets")
require("./closebrackets");

require("codemirror/addon/edit/matchbrackets")
require("codemirror/addon/comment/comment")
require("codemirror/addon/comment/continuecomment")
// require("codemirror/addon/fold/foldcode")
// require("codemirror/addon/fold/foldgutter")
require("codemirror/addon/fold/foldgutter.css")
require("codemirror/addon/search/match-highlighter")
require("codemirror/addon/hint/show-hint")
require("codemirror/addon/hint/show-hint.css")
require("codemirror/addon/dialog/dialog")
require("codemirror/addon/dialog/dialog.css")
require("codemirror/addon/search/searchcursor")
require("codemirror/addon/search/search")

global.tern = require('tern')
require("codemirror/addon/tern/tern.css")
require("codemirror/addon/tern/tern")

// Tern Server Eliot
var eliot = new CodeMirror.TernServer({defs: [
    require('tern/defs/ecma5.json')
]});

// var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
// import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup'

import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd/modules/backends/HTML5';
import { DragSource, DropTarget } from 'react-dnd';

class DocumentModel { // a document is a collection of cells
    constructor() {
        this.cells = []
        this._ids = 0
    }
    find(id) { return this.cells.filter(x => x.id === id)[0] }
    item(index) { return this.cells[index] }
    get length() { return this.length }
    update(){}
}

class CellModel {
    constructor(doc, index) {
        this.doc = doc
        this.id = --doc._ids;
        this.value = ""
        this.cm = null
        if(typeof index === 'undefined'){
            doc.cells.push(this)
        }else{
            doc.cells.splice(index, 0, this)
        }
        this._mounted =  []
        this.update()
    }
    mount = (x) => {
        if(typeof x == 'function'){
            this.cm ? x() : this._mounted.push(x);
        }else if(x instanceof CodeMirror){
            this.cm = x;
            while(this._mounted.length)
                this._mounted.shift()();
        }
    }
    remove() {
        this.doc.cells.splice(this.index, 1)
        this.update()
    }
    moveTo(after) {
        var cardIndex = this.index,
            afterIndex = after.index;
        this.doc.cells.splice(cardIndex, 1)
        this.doc.cells.splice(afterIndex, 0, this);
        console.log('moveto', after)
        this.update()
    }
    update(){ if(this.doc) this.doc.update() }
    get value(){ return this._value; }
    set value(val){
        this._value = val;
        this.update()
    }
    get has_focus(){ return this._has_focus; }
    set has_focus(val){
        this._has_focus = val;
        if(val){
            for(var i = 0; i < this.doc.cells.length; i++){
                var other = this.doc.cells[i];
                if(other !== this) other.has_focus = false;
            }
            this.doc.update()
        }
    }
    get index(){ return this.doc.cells.indexOf(this) }
    get prev(){ return this.doc.item(this.index - 1) }
    get next(){ return this.doc.item(this.index + 1) }
}

class Editor extends Component {
    constructor(props) {
        super(props)
        this.componentDidMount = this.componentDidMount.bind(this)
    }
    componentDidMount() {
        var {doc, cell} = this.props;

        var cm = CodeMirror(React.findDOMNode(this), {
            value: cell.value,
            mode: "javascript",
            lineNumbers: false,
            indentUnit: 4,
            continueComments: true,
            keyMap: 'sublime',
            autoCloseBrackets: true,
            matchBrackets: true,
            lineWrapping: true,
            // highlightSelectionMatches: true,
            viewportMargin: Infinity
        })

        cell.mount(cm)

        cm.setOption("extraKeys", {
            "Ctrl-Space": function(cm) { eliot.complete(cm); },
            "Ctrl-I": function(cm) { eliot.showType(cm); },
            "Ctrl-O": function(cm) { eliot.showDocs(cm); },
            "Alt-.": function(cm) { eliot.jumpToDef(cm); },
            "Alt-,": function(cm) { eliot.jumpBack(cm); },
            "Ctrl-Q": function(cm) { eliot.rename(cm); },
            "Ctrl-.": function(cm) { eliot.selectName(cm); },
            "Shift-Enter": (cm) => {

                if(cell.next) cell.next.cm.focus()
            },
            "Ctrl-N": (cm) => {
            },
            "Cmd-Up": (cm) => {
                if(cell.prev) cell.prev.cm.focus();
            },
            "Cmd-Down": (cm) => {
                if(cell.next) cell.next.cm.focus();
            },
            "Cmd-J": (cm) => {
                var newCell = new CellModel(doc, cell.index + 1)
                newCell.cm.focus()
            },
            "Cmd-K": (cm) => {
                var newCell = new CellModel(doc, cell.index)
                newCell.cm.focus()
            }
        })

        cm.on('changes', (cm) => {
            cell.value = cm.getValue()

            if(!cell.next && cell.value){
                new CellModel(doc);
            }
        })
        cm.on("cursorActivity", function(cm) {
            eliot.updateArgHints(cm);
            // eliot.complete(cm)
        });
        cm.on('blur', (cm, evt) => {
            // cell.has_focus = false
            // doc.update()
        })
        cm.on('focus', (cm, evt) => {
            cell.has_focus = true

            if(!cell.next && cell.value){
                new CellModel(doc)
                doc.update()
            }
        })
        cm.on('keyup', function(cm, evt){
            // console.log(cm.type, cm, evt.keyCode)
            if(evt.keyCode == 190){ // i don't know why this is "."
                eliot.complete(cm)
            }
        })
        cm.on('keydown', (cm, evt) => {
            // console.log(evt, cm, evt.keyCode)
            if(evt.keyCode == 40){ // down
                var cursor = cm.getCursor()
                if (cursor.line === (cm.lineCount()-1)) {
                    // cursor.ch === cm.getLine(cursor.line).length
                    // select the next thing
                    if(cell.next) cell.next.cm.focus()
                }
            }else if(evt.keyCode == 38){ // up
                var cursor = cm.getCursor()
                if (cursor.line === 0) {
                    if(cell.prev) cell.prev.cm.focus()
                }
            }else if(evt.keyCode == 8) { // backspace
                if(cm.getValue() == ""){ // if the cell is empty
                    if(cell.prev){
                        cell.prev.cm.focus()
                    }else if(cell.next){
                        cell.next.cm.focus()
                    }
                    if(doc.cells.length > 1){
                        cell.remove()
                    }
                }
            }

        })
    }
    render() {
        return

    }
}

const cardSource = {
    beginDrag(props) {
        return { id: props.cell.id };
    }
};

const cardTarget = {
    hover(props, monitor) {
        const draggedId = monitor.getItem().id;
        if (draggedId !== props.cell.id) {
            props.doc.find(draggedId).moveTo(props.cell)
        }
    }
};

@DropTarget('Cell', cardTarget, connect => ({
    connectDropTarget: connect.dropTarget(),
}))
@DragSource('Cell', cardSource, (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))
class Cell extends Component {
    handleClick = (e) => {
        console.log(this, e)
        this.props.cell.has_focus = true;
    }
    render() {
        const { doc, cell } = this.props;

        const { isDragging, connectDragSource, connectDropTarget } = this.props;
        const opacity = isDragging ? 0 : 1;

        return connectDropTarget(connectDragSource(

                    <Editor {...this.props}></Editor>

));
    }
}

@DragDropContext(HTML5Backend)
class EditPane extends Component {
    constructor(props) {
        super(props);

    }
    handleClick = (e) => {
        var {doc} = this.props;
        if(e.target === React.findDOMNode(this)){
            doc.cells[doc.cells.length - 1].cm.focus()
        }
    }
    render() {
        var {doc} = this.props;

        return (

                {doc.cells.map(
                    (cell) => <Cell {...this.props} key={cell.id} cell={cell}></Cell>
                )}

        )
    }
}

class CellResult extends Component {
    render() {
        var {doc, cell} = this.props;
        // if(cell.has_focus){
        //     return
omg has focus

        // }
        try {
            var middle = babel.transform(cell.value).code;

            var progressTracker = '__track$loop__'
            function removeMerp(src){
                return falafel(src, function(node){
                    if(node.type == 'CallExpression' &&
                        node.callee.type == 'Identifier' &&
                        node.callee.name == progressTracker){
                        node.update('0')
                    }
                })
            }
            var result = falafel(middle, function (node) {
                if(node.type === 'ForStatement'){
                    if(node.test.type == 'BinaryExpression' &&
                        node.test.right.type == 'Literal' &&
                        node.test.left.type == 'Identifier'){
                        node.update.update(node.update.source() + ','+progressTracker+'(' + node.test.left.name + ', ' + node.test.right.value + ')')
                        node.body.update(removeMerp(node.body.source()))
                    }
                }else if(node.type === 'CallExpression'){
                    if(node.callee.type == 'MemberExpression' &&
                        ['forEach', 'map'].indexOf(node.callee.property.name) != -1) {
                        var thing = node.arguments[0].body;
                        thing.update(removeMerp('{'+progressTracker+'(arguments[1], arguments[2].length);' + thing.source().slice(1)))
                    }
                }
            });

        } catch (err) {
            var result = err.toString()
        }

        return (

                {result}

        )
    }
}

class OutPane extends Component {
    render() {
        var {doc} = this.props;
        return (

                {doc.cells.map(
                    (cell) => <CellResult {...this.props} cell={cell} key={cell.id}></CellResult>
                )}

        )
    }
}

export default class App extends Component {
    constructor(props) {
        super(props);
        var doc = new DocumentModel()
        new CellModel(doc)
        this.state = { doc }
        doc.update = this.forceUpdate.bind(this)
    }
    render() {
        return  (

                <SplitPane orientation="horizontal" minSize={250}>
                    <EditPane doc={this.state.doc}></EditPane>
                    <OutPane  doc={this.state.doc}></OutPane>
                </SplitPane>

        );
    }
}<commit_msg>state restore from localstorage<commit_after>'use strict';

import React, { Component, PropTypes } from 'react/addons';
import update from 'react/lib/update';
import classNames from 'classnames'
import SplitPane from 'react-split-pane'
import babel from 'babel-core/lib/babel/api/browser.js'
import falafel from 'falafel';

global.babel = babel
var CodeMirror = require('codemirror')
require("codemirror/lib/codemirror.css");
require("./codemirror.less");
require("codemirror/mode/javascript/javascript")
require("codemirror/keymap/sublime")
// require("codemirror/addon/edit/closebrackets")
require("./closebrackets");

require("codemirror/addon/edit/matchbrackets")
require("codemirror/addon/comment/comment")
require("codemirror/addon/comment/continuecomment")
// require("codemirror/addon/fold/foldcode")
// require("codemirror/addon/fold/foldgutter")
require("codemirror/addon/fold/foldgutter.css")
require("codemirror/addon/search/match-highlighter")
require("codemirror/addon/hint/show-hint")
require("codemirror/addon/hint/show-hint.css")
require("codemirror/addon/dialog/dialog")
require("codemirror/addon/dialog/dialog.css")
require("codemirror/addon/search/searchcursor")
require("codemirror/addon/search/search")

global.tern = require('tern')
require("codemirror/addon/tern/tern.css")
require("codemirror/addon/tern/tern")

// Tern Server Eliot
var eliot = new CodeMirror.TernServer({defs: [
    require('tern/defs/ecma5.json')
]});

// var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
// import ReactCSSTransitionGroup from 'react/lib/ReactCSSTransitionGroup'

import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd/modules/backends/HTML5';
import { DragSource, DropTarget } from 'react-dnd';

class DocumentModel { // a document is a collection of cells
    constructor() {
        this.cells = []
        this._ids = 0
    }
    find(id) { return this.cells.filter(x => x.id === id)[0] }
    item(index) { return this.cells[index] }
    get length() { return this.length }
    update(){}
    serialize() {
        return {
            cells: this.cells.map( x => x.serialize() )
        }
    }
    restore(state){
        for(let c of state.cells){
            var cell = new CellModel(this, c.index)
            cell.value = c.value
        }
    }
}

class CellModel {
    constructor(doc, index) {
        this.doc = doc
        this.id = --doc._ids;
        this.value = ""
        this.cm = null
        if(typeof index === 'undefined'){
            doc.cells.push(this)
        }else{
            doc.cells.splice(index, 0, this)
        }
        this._mounted =  []
        this.update()
    }
    serialize() {
        return {
            value: this.value,
            index: this.index
        }
    }
    mount = (x) => {
        if(typeof x == 'function'){
            this.cm ? x() : this._mounted.push(x);
        }else if(x instanceof CodeMirror){
            this.cm = x;
            while(this._mounted.length)
                this._mounted.shift()();
        }
    }
    remove() {
        this.doc.cells.splice(this.index, 1)
        this.update()
    }
    moveTo(after) {
        var cardIndex = this.index,
            afterIndex = after.index;
        this.doc.cells.splice(cardIndex, 1)
        this.doc.cells.splice(afterIndex, 0, this);
        console.log('moveto', after)
        this.update()
    }
    update(){ if(this.doc) this.doc.update() }
    get value(){ return this._value; }
    set value(val){
        this._value = val;
        this.update()
    }
    get has_focus(){ return this._has_focus; }
    set has_focus(val){
        this._has_focus = val;
        if(val){
            for(var i = 0; i < this.doc.cells.length; i++){
                var other = this.doc.cells[i];
                if(other !== this) other.has_focus = false;
            }
            this.doc.update()
        }
    }
    get index(){ return this.doc.cells.indexOf(this) }
    get prev(){ return this.doc.item(this.index - 1) }
    get next(){ return this.doc.item(this.index + 1) }
}

class Editor extends Component {
    constructor(props) {
        super(props)
        this.componentDidMount = this.componentDidMount.bind(this)
    }
    componentDidMount() {
        var {doc, cell} = this.props;

        var cm = CodeMirror(React.findDOMNode(this), {
            value: cell.value,
            mode: "javascript",
            lineNumbers: false,
            indentUnit: 4,
            continueComments: true,
            keyMap: 'sublime',
            autoCloseBrackets: true,
            matchBrackets: true,
            lineWrapping: true,
            // highlightSelectionMatches: true,
            viewportMargin: Infinity
        })

        cell.mount(cm)

        cm.setOption("extraKeys", {
            "Ctrl-Space": function(cm) { eliot.complete(cm); },
            "Ctrl-I": function(cm) { eliot.showType(cm); },
            "Ctrl-O": function(cm) { eliot.showDocs(cm); },
            "Alt-.": function(cm) { eliot.jumpToDef(cm); },
            "Alt-,": function(cm) { eliot.jumpBack(cm); },
            "Ctrl-Q": function(cm) { eliot.rename(cm); },
            "Ctrl-.": function(cm) { eliot.selectName(cm); },
            "Shift-Enter": (cm) => {

                if(cell.next) cell.next.cm.focus()
            },
            "Ctrl-N": (cm) => {
            },
            "Cmd-Up": (cm) => {
                if(cell.prev) cell.prev.cm.focus();
            },
            "Cmd-Down": (cm) => {
                if(cell.next) cell.next.cm.focus();
            },
            "Cmd-J": (cm) => {
                var newCell = new CellModel(doc, cell.index + 1)
                newCell.cm.focus()
            },
            "Cmd-K": (cm) => {
                var newCell = new CellModel(doc, cell.index)
                newCell.cm.focus()
            }
        })

        cm.on('changes', (cm) => {
            cell.value = cm.getValue()

            if(!cell.next && cell.value){
                new CellModel(doc);
            }
        })
        cm.on("cursorActivity", function(cm) {
            eliot.updateArgHints(cm);
            // eliot.complete(cm)
        });
        cm.on('blur', (cm, evt) => {
            // cell.has_focus = false
            // doc.update()
        })
        cm.on('focus', (cm, evt) => {
            cell.has_focus = true

            if(!cell.next && cell.value){
                new CellModel(doc)
                doc.update()
            }
        })
        cm.on('keyup', function(cm, evt){
            // console.log(cm.type, cm, evt.keyCode)
            if(evt.keyCode == 190){ // i don't know why this is "."
                eliot.complete(cm)
            }
        })
        cm.on('keydown', (cm, evt) => {
            // console.log(evt, cm, evt.keyCode)
            if(evt.keyCode == 40){ // down
                var cursor = cm.getCursor()
                if (cursor.line === (cm.lineCount()-1)) {
                    // cursor.ch === cm.getLine(cursor.line).length
                    // select the next thing
                    if(cell.next) cell.next.cm.focus()
                }
            }else if(evt.keyCode == 38){ // up
                var cursor = cm.getCursor()
                if (cursor.line === 0) {
                    if(cell.prev) cell.prev.cm.focus()
                }
            }else if(evt.keyCode == 8) { // backspace
                if(cm.getValue() == ""){ // if the cell is empty
                    if(cell.prev){
                        cell.prev.cm.focus()
                    }else if(cell.next){
                        cell.next.cm.focus()
                    }
                    if(doc.cells.length > 1){
                        cell.remove()
                    }
                }
            }

        })
    }
    render() {
        return

    }
}

const cardSource = {
    beginDrag(props) {
        return { id: props.cell.id };
    }
};

const cardTarget = {
    hover(props, monitor) {
        const draggedId = monitor.getItem().id;
        if (draggedId !== props.cell.id) {
            props.doc.find(draggedId).moveTo(props.cell)
        }
    }
};

@DropTarget('Cell', cardTarget, connect => ({
    connectDropTarget: connect.dropTarget(),
}))
@DragSource('Cell', cardSource, (connect, monitor) => ({
    connectDragSource: connect.dragSource(),
    isDragging: monitor.isDragging()
}))
class Cell extends Component {
    handleClick = (e) => {
        console.log(this, e)
        this.props.cell.has_focus = true;
    }
    render() {
        const { doc, cell } = this.props;

        const { isDragging, connectDragSource, connectDropTarget } = this.props;
        const opacity = isDragging ? 0 : 1;

        return connectDropTarget(connectDragSource(

                    <Editor {...this.props}></Editor>

));
    }
}

@DragDropContext(HTML5Backend)
class EditPane extends Component {
    constructor(props) {
        super(props);

    }
    handleClick = (e) => {
        var {doc} = this.props;
        if(e.target === React.findDOMNode(this)){
            doc.cells[doc.cells.length - 1].cm.focus()
        }
    }
    render() {
        var {doc} = this.props;

        return (

                {doc.cells.map(
                    (cell) => <Cell {...this.props} key={cell.id} cell={cell}></Cell>
                )}

        )
    }
}

class CellResult extends Component {
    render() {
        var {doc, cell} = this.props;
        // if(cell.has_focus){
        //     return
omg has focus

        // }
        try {
            var middle = babel.transform(cell.value).code;

            var progressTracker = '__track$loop__'
            function removeMerp(src){
                return falafel(src, function(node){
                    if(node.type == 'CallExpression' &&
                        node.callee.type == 'Identifier' &&
                        node.callee.name == progressTracker){
                        node.update('0')
                    }
                })
            }
            var result = falafel(middle, function (node) {
                if(node.type === 'ForStatement'){
                    if(node.test.type == 'BinaryExpression' &&
                        node.test.right.type == 'Literal' &&
                        node.test.left.type == 'Identifier'){
                        node.update.update(node.update.source() + ','+progressTracker+'(' + node.test.left.name + ', ' + node.test.right.value + ')')
                        node.body.update(removeMerp(node.body.source()))
                    }
                }else if(node.type === 'CallExpression'){
                    if(node.callee.type == 'MemberExpression' &&
                        ['forEach', 'map'].indexOf(node.callee.property.name) != -1) {
                        var thing = node.arguments[0].body;
                        thing.update(removeMerp('{'+progressTracker+'(arguments[1], arguments[2].length);' + thing.source().slice(1)))
                    }
                }
            });

        } catch (err) {
            var result = err.toString()
        }

        return (

                {result}

        )
    }
}

class OutPane extends Component {
    render() {
        var {doc} = this.props;
        return (

                {doc.cells.map(
                    (cell) => <CellResult {...this.props} cell={cell} key={cell.id}></CellResult>
                )}

        )
    }
}

export default class App extends Component {
    constructor(props) {
        super(props);
        var doc = new DocumentModel()
        new CellModel(doc)
        this.state = { doc }
        try {
            var last_state = JSON.parse(localStorage.last_state);
            doc.restore(last_state)
        } catch (err) {
            console.error(err)
        }

        doc.update = () => {
            localStorage.last_state = JSON.stringify(doc.serialize())
            this.forceUpdate()
        }
    }
    handleKey = (e) => {
        if(e.keyCode == 80 && e.metaKey){
            console.log('Cmd+P')
            e.preventDefault();
        }
    }
    render() {
        return  (

                <SplitPane orientation="horizontal" minSize={250}>
                    <EditPane doc={this.state.doc}></EditPane>
                    <OutPane  doc={this.state.doc}></OutPane>
                </SplitPane>

        );
    }
}<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var GeneratorBase = require('../generator-base.js');

var _ = require('lodash');

var Generator = module.exports = function Generator() {
  GeneratorBase.apply(this, arguments);

};

util.inherits(Generator, GeneratorBase);

Generator.prototype.askFor = function askFor() {

	var done = this.async();
	var currentModule = this.config.get('currentModule');
	currentModule = currentModule ? currentModule : "app";

	var prompts = [{
		type: 'list',
		name: 'type',
		message: "What kind of module would you like to make?",
		choices: ['module', 'controller', 'directive'],
		default: 'module'
	}, {
		when: function (r) {return r.type!=='module';},
		type: 'input',
		name: 'module',
		message: "Module: ",
		default: currentModule
	}, {
		type: 'input',
		name: 'name',
		message: "Name: "
	}, {
		type: 'input',
		name: 'dependencies',
		message: "Dependencies (separate with a space): "
	}, {
		when: function (r) {return r.type==='controller';},
		type: 'confirm',
		name: 'hasRoute',
		message: "Does the module have a route?",
		default: true
	}];

	this.prompt(prompts, function (results) {

		results.dependencies = _.compact(results.dependencies.split(" "));
		this.createModule(results);

		done();
	}.bind(this));
};<commit_msg>Updated module generator to include more dialog.<commit_after>'use strict';
var util = require('util');
var GeneratorBase = require('../generator-base.js');

var _ = require('lodash');

var Generator = module.exports = function Generator() {
  GeneratorBase.apply(this, arguments);

};

util.inherits(Generator, GeneratorBase);

Generator.prototype.listModules = function listModules() {
	var modules = this.config.get('modules');

	var msg = "\nMODULE GENERATOR " +
	"\nUse this generator to scaffold your application." +
	"\nWhen you are done making modules, use \'yo build\'" +
	"\nto compile modules, resolve dependencies, and add" +
	"\nscripts to index.html file." +
	"\n";

	console.log(msg);

	console.log("Current modules:\n");

	_.each(modules, function (module, key) {
		console.log(key);
		_.each(module, function (j, key) {
			console.log("    " + key);
		});
	});

	console.log("\n");
};

Generator.prototype.askFor = function askFor() {

	var done = this.async();
	var currentModule = this.config.get('currentModule');

	currentModule = currentModule ? currentModule : "app";

	var prompts = [{
		type: 'list',
		name: 'type',
		message: "What kind of module would you like to make?",
		choices: ['module', 'controller', 'directive'],
		default: 'module'
	}, {
		when: function (r) {return r.type!=='module';},
		type: 'input',
		name: 'module',
		message: "Module: ",
		default: currentModule
	}, {
		type: 'input',
		name: 'name',
		message: "Name: "
	}, {
		type: 'input',
		name: 'dependencies',
		message: "Dependencies (separate with a space): "
	}, {
		when: function (r) {return r.type==='controller';},
		type: 'confirm',
		name: 'hasRoute',
		message: "Does the module have a route?",
		default: true
	}];

	this.prompt(prompts, function (results) {

		results.dependencies = _.compact(results.dependencies.split(" "));
		this.createModule(results);

		done();
	}.bind(this));
};<|endoftext|>


<commit_before>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { sync } from 'vuex-router-sync'
import router from './components/router'
import store from './store'
import ensurePolyfills from './utils/ensurePolyfills'

Vue.config.devtools = false
const { state } = store
sync(store, router)

/*Vue.http.interceptors.push(function(request, next) {
  next();
})*/

ensurePolyfills(() => {
  require.ensure(['./theme/Index.vue','vue-resource'], function(){
    let App  = require('./theme/Index.vue')
    Vue.use(require('vue-resource'));
    let vue = new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })
  })
});<commit_msg>require Index before router<commit_after>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import { sync } from 'vuex-router-sync'
import router from './components/router'
import store from './store'
import ensurePolyfills from './utils/ensurePolyfills'

Vue.config.devtools = false
const { state } = store
sync(store, router)

/*Vue.http.interceptors.push(function(request, next) {
  next();
})*/

ensurePolyfills(() => {
  require.ensure(['./theme/Index.vue','vue-resource'], function(){
    Vue.use(require('vue-resource'));
    let App  = require('./theme/Index.vue')
    let vue = new Vue({
      el: '#app',
      router,
      store,
      template: '<App/>',
      components: { App }
    })
  })
});<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import { location as locationType } from './PropTypes'

class Miss extends React.Component {
  static contextTypes = {
    match: PropTypes.object,
    location: PropTypes.object,
    serverRouter: PropTypes.object
  }

  constructor(props, context) {
    super(props, context)

    // ignore if rendered out of context (probably for unit tests)
    if (context.match && !context.serverRouter) {
      this.unsubscribe = this.context.match.subscribe((matchesFound) => {
        this.setState({
          noMatchesInContext: !matchesFound
        })
      })
    }

    if (context.serverRouter) {
      context.serverRouter.registerMissPresence(
        context.match.serverRouterIndex
      )
    }

    this.state = {
      noMatchesInContext: false
    }
  }

  componentWillUnmount() {
    if (this.unsubscribe) {
      this.unsubscribe()
    }
  }

  render() {
    const { render, component:Component } = this.props
    const { noMatchesInContext } = this.state
    const { location:locationProp } = this.props
    const location = locationProp || this.context.location
    const { serverRouter, match } = this.context
    const noMatchesOnServerContext = serverRouter &&
      serverRouter.missedAtIndex(match.serverRouterIndex)
    if (noMatchesInContext || noMatchesOnServerContext) {
      return (
        render ? (
          render({ location })
        ) : (
          <Component location={location}/>
        )
      )
    } else {
      return null
    }
  }
}

if (__DEV__) {
  Miss.propTypes = {
    children: PropTypes.node,
    location: locationType,
    render: PropTypes.func,
    component: PropTypes.func
  }
}

export default Miss
<commit_msg>fix Miss<commit_after>import React, { PropTypes } from 'react'
import { location as locationType } from './PropTypes'
import { LocationSubscriber } from './Broadcasts'

class Miss extends React.Component {
  static contextTypes = {
    match: PropTypes.object,
    location: PropTypes.object,
    serverRouter: PropTypes.object
  }

  constructor(props, context) {
    super(props, context)

    // ignore if rendered out of context (probably for unit tests)
    if (context.match && !context.serverRouter) {
      this.unsubscribe = this.context.match.subscribe((matchesFound) => {
        this.setState({
          noMatchesInContext: !matchesFound
        })
      })
    }

    if (context.serverRouter) {
      context.serverRouter.registerMissPresence(
        context.match.serverRouterIndex
      )
    }

    this.state = {
      noMatchesInContext: false
    }
  }

  componentWillUnmount() {
    if (this.unsubscribe) {
      this.unsubscribe()
    }
  }

  render() {
    return (
      <LocationSubscriber>
        {(locationContext) => {
          const { render, component:Component } = this.props
          const { noMatchesInContext } = this.state
          const { location:locationProp } = this.props
          const location = locationProp || locationContext
          const { serverRouter, match } = this.context
          const noMatchesOnServerContext = serverRouter &&
            serverRouter.missedAtIndex(match.serverRouterIndex)
          if (noMatchesInContext || noMatchesOnServerContext) {
            return (
              render ? (
                render({ location })
              ) : (
                <Component location={location}/>
              )
            )
          } else {
            return null
          }
        }}
      </LocationSubscriber>
    )
  }
}

if (__DEV__) {
  Miss.propTypes = {
    children: PropTypes.node,
    location: locationType,
    render: PropTypes.func,
    component: PropTypes.func
  }
}

export default Miss
<|endoftext|>