<commit_before>;(function(){//IFEE

angular.module('brewKeeper')
      .controller('recipeDetail', function($scope, $http, $location, $routeParams, $rootScope){
          var id = $routeParams.id;
          var username = $routeParams.username;
          $scope.username = $routeParams.username;
          $scope.id = $routeParams.id;
          $(document).scrollTop(0);

          $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
            .then(function(response){
              $rootScope.detail = response.data;
              $rootScope.steps = response.data.steps;
              $rootScope.notes = response.data.brewnotes;
              var currentRating = $rootScope.detail.rating;
              $scope.rating = 0;
              $scope.ratings = [{
                  current: currentRating,
                  max: 5
              }];

          }) //end http.get

          $scope.rateRecipe = function (rating) {
            var newRating = {"rating": rating}
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", newRating)
          }

          $scope.EliminateRecipe = function() {
            if (window.confirm("Are you sure you want to delete " + $scope.detail.title + "?")){
              $http.delete('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/').then(function(){
                $location.path('/'+ username);
              })
            };
          }; //end Eliminate function

          $scope.deleteStep = function(stepNumber, stepId){
            if (window.confirm("Are you sure you want to delete step " + stepNumber + "?")){
              $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ stepId + "/").then(function(){
                $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id +'/')
                  .then(function(response){
                    $scope.steps = response.data.steps;
                    if(response.data.steps.length == 0){
                      $(".brew-it-button").addClass("hidden");
                      $(".no-steps").removeClass("hidden");
                    }
                  })
              })
            }
          } //end deleteStep function

          // $scope.showSteps = function(stepId){
          //   stepId= "div." + stepId.toString()
          //   $(stepId).removeClass("hidden")
          // };

          $scope.hideEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden")
          }

          $scope.editStep = function(step){
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step)
              .then(function() {
                $scope.hideEditStep(step.id)
                // $scope.showEditSteps(step.id)
              });
          } //end editStep function

          $scope.showEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden");
          }

          $scope.increaseStep = function(step){
            if(step.step_number >= $rootScope.steps.length){
              return
            }
            step.step_number++
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $rootScope.steps = response.data.steps;
                })
            })
          } //end increaseStep function

          $scope.decreaseStep = function(step){
            if(step.step_number <= 1){
              return
            }
            step.step_number--
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $rootScope.steps = response.data.steps;
                })
            })
          } //end decreaseStep function

          $scope.step = { }//Might need to prepopulate this with empty strings for each key... Maybe...
          $scope.addStep=function(){ //add step function
            $scope.step.step_number = $scope.steps.length + 1;
            $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/", $scope.step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $(".brew-it-button").removeClass("hidden");
                  $(".no-steps").addClass("hidden");
                  $rootScope.steps = response.data.steps;
                })
            })
            $scope.step= { };
          } //end new step submit function

        $scope.showAddSteps = function(){
          $("form.create-new-step").toggleClass("hidden");
        };//Reveal "Add Step" when new recipe form is submitted.
        // $scope.hideAddSteps = function(){
        //   $("form.create-new-step").addClass("hidden");
        //   $("button.done-adding").addClass("hidden");
        // }; //hides the add step form

        $('.edit-button').on('click', function(){
          $('.edit-recipe').removeClass("hidden");
          $('.recipe-view').addClass("hidden");
        });

        $(".no-steps").click(function(){
          $scope.showAddSteps()
        })

      $scope.editRecipe = function(recipe){
        $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", recipe)
        .then( function () {
          $('.edit-recipe').addClass("hidden");
          $('.recipe-view').removeClass("hidden");
        })
      } //end editRecipe function
        $('.cancel-recipe-edit').on('click', function(){
          $('.recipe-view').removeClass("hidden");
          $('.edit-recipe').addClass("hidden");
        });

        // Function for cloning recipes
        $scope.cloneRecipe = function(){
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };
          var cloneData = {}
          cloneData.title = "Clone of: " + $scope.detail.title;
          cloneData.bean_name = $scope.detail.bean_name;
          cloneData.roast = $scope.detail.roast;
          cloneData.orientation = $scope.detail.orientation;
          cloneData.general_recipe_comment = $scope.detail.general_recipe_comment;
          cloneData.grind = $scope.detail.grind;
          cloneData.total_bean_amount = $scope.detail.total_bean_amount;
          cloneData.bean_units = $scope.detail.bean_units;
          cloneData.water_type = $scope.detail.water_type;
          cloneData.total_water_amount = $scope.detail.total_water_amount;
          cloneData.water_units = $scope.detail.water_units;
          cloneData.temp = $scope.detail.temp;
          cloneData.steps = [];

          $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/", cloneData).success(function(response){

            newRecipeId = response.id;
            steps = [];
            for(step in $scope.detail.steps){
              steps[step] = {};
              steps[step].step_number = $scope.detail.steps[step].step_number;
              steps[step].step_title = $scope.detail.steps[step].step_title;
              steps[step].step_body = $scope.detail.steps[step].step_body;
              steps[step].duration = $scope.detail.steps[step].duration;
              steps[step].water_amount = $scope.detail.steps[step].water_amount;

              $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ newRecipeId +"/steps/", steps[step])//end step post
            };//end loop to clone steps
          })
          .then(function(){
            $location.path("/"+ username +"/clone/"+ newRecipeId);
          })//end post new recipe
        }; //end recipe clone function

        $scope.showEditNote = function(noteId) {
          noteView = "div.note-view" + noteId.toString();
          editNote = "article.edit-note" + noteId.toString();
          $(noteView).addClass("hidden")
          $(editNote).removeClass("hidden")
        }
          // $scope.showEditStep = function(stepId){
          //   stepId = "div." + stepId.toString();
          //   $(stepId).removeClass("hidden")
          // }

        $scope.editNote = function(note){
          var note_id = note.id
          $http.put("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + note_id + "/", note)
          .then( function () {
            $(editNote).addClass("hidden");
            $(noteView).removeClass("hidden");
          })
        } //end editNote function

        $scope.deleteNote = function(noteId) {
          var noteId = noteId
          if (window.confirm("Are you sure you want to delete this note?")){
            $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + noteId + "/")
            .then(function(){var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
            })
          };
        }; //end deleteNote function

        $scope.showAddBrewNote = function(){
          $(".brew-form").toggleClass("hidden");
        };

        $scope.addBrewNote=function(){
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/brewnotes/', $scope.brewnote)
          .success(function (data) {
            $(".brew-form").toggleClass("hidden");
            var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
          })
        $scope.brewnote = { };
        $scope.addNote = false;
        }//Add Brew Note Form

        $scope.showNoteIcons = function(noteId){
          $(".note-icons").filter($("."+ noteId)).toggleClass("hidden");
        }

        $scope.makePublic = function(){ //makes recipes public
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };
          console.log("making it public!!!")
          var publicData = {}  //build the recipe
          publicData.title = "Public Copy of: " + $scope.detail.title;
          publicData.bean_name = $scope.detail.bean_name;
          publicData.roast = $scope.detail.roast;
          publicData.orientation = $scope.detail.orientation;
          publicData.general_recipe_comment = $scope.detail.general_recipe_comment;
          publicData.grind = $scope.detail.grind;
          publicData.total_bean_amount = $scope.detail.total_bean_amount;
          publicData.bean_units = $scope.detail.bean_units;
          publicData.water_type = $scope.detail.water_type;
          publicData.total_water_amount = $scope.detail.total_water_amount;
          publicData.water_units = $scope.detail.water_units;
          publicData.temp = $scope.detail.temp;
          publicData.steps = [];
          console.log(publicDatas)

          steps = []; //build the steps
          for(step in $scope.detail.steps){
            steps[step] = {};
            steps[step].step_number = $scope.detail.steps[step].step_number;
            steps[step].step_title = $scope.detail.steps[step].step_title;
            steps[step].step_body = $scope.detail.steps[step].step_body;
            steps[step].duration = $scope.detail.steps[step].duration;
            steps[step].water_amount = $scope.detail.steps[step].water_amount;
          }

          console.log(steps);
      }; // end makePublic function

      }) //end recipDetail controller

})();//END Angular IFEE
<commit_msg>user can make a recipe public<commit_after>;(function(){//IFEE

angular.module('brewKeeper')
      .controller('recipeDetail', function($scope, $http, $location, $routeParams, $rootScope){
          var id = $routeParams.id;
          var username = $routeParams.username;
          $scope.username = $routeParams.username;
          $scope.id = $routeParams.id;
          $(document).scrollTop(0);

          $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
            .then(function(response){
              $rootScope.detail = response.data;
              $rootScope.steps = response.data.steps;
              $rootScope.notes = response.data.brewnotes;
              var currentRating = $rootScope.detail.rating;
              $scope.rating = 0;
              $scope.ratings = [{
                  current: currentRating,
                  max: 5
              }];

          }) //end http.get

          $scope.rateRecipe = function (rating) {
            var newRating = {"rating": rating}
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", newRating)
          }

          $scope.EliminateRecipe = function() {
            if (window.confirm("Are you sure you want to delete " + $scope.detail.title + "?")){
              $http.delete('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/').then(function(){
                $location.path('/'+ username);
              })
            };
          }; //end Eliminate function

          $scope.deleteStep = function(stepNumber, stepId){
            if (window.confirm("Are you sure you want to delete step " + stepNumber + "?")){
              $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ stepId + "/").then(function(){
                $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id +'/')
                  .then(function(response){
                    $scope.steps = response.data.steps;
                    if(response.data.steps.length == 0){
                      $(".brew-it-button").addClass("hidden");
                      $(".no-steps").removeClass("hidden");
                    }
                  })
              })
            }
          } //end deleteStep function

          // $scope.showSteps = function(stepId){
          //   stepId= "div." + stepId.toString()
          //   $(stepId).removeClass("hidden")
          // };

          $scope.hideEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden")
          }

          $scope.editStep = function(step){
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step)
              .then(function() {
                $scope.hideEditStep(step.id)
                // $scope.showEditSteps(step.id)
              });
          } //end editStep function

          $scope.showEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden");
          }

          $scope.increaseStep = function(step){
            if(step.step_number >= $rootScope.steps.length){
              return
            }
            step.step_number++
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $rootScope.steps = response.data.steps;
                })
            })
          } //end increaseStep function

          $scope.decreaseStep = function(step){
            if(step.step_number <= 1){
              return
            }
            step.step_number--
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $rootScope.steps = response.data.steps;
                })
            })
          } //end decreaseStep function

          $scope.step = { }//Might need to prepopulate this with empty strings for each key... Maybe...
          $scope.addStep=function(){ //add step function
            $scope.step.step_number = $scope.steps.length + 1;
            $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/", $scope.step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $(".brew-it-button").removeClass("hidden");
                  $(".no-steps").addClass("hidden");
                  $rootScope.steps = response.data.steps;
                })
            })
            $scope.step= { };
          } //end new step submit function

        $scope.showAddSteps = function(){
          $("form.create-new-step").toggleClass("hidden");
        };//Reveal "Add Step" when new recipe form is submitted.
        // $scope.hideAddSteps = function(){
        //   $("form.create-new-step").addClass("hidden");
        //   $("button.done-adding").addClass("hidden");
        // }; //hides the add step form

        $('.edit-button').on('click', function(){
          $('.edit-recipe').removeClass("hidden");
          $('.recipe-view').addClass("hidden");
        });

        $(".no-steps").click(function(){
          $scope.showAddSteps()
        })

      $scope.editRecipe = function(recipe){
        $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", recipe)
        .then( function () {
          $('.edit-recipe').addClass("hidden");
          $('.recipe-view').removeClass("hidden");
        })
      } //end editRecipe function
        $('.cancel-recipe-edit').on('click', function(){
          $('.recipe-view').removeClass("hidden");
          $('.edit-recipe').addClass("hidden");
        });

        // Function for cloning recipes
        $scope.cloneRecipe = function(){
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };
          var cloneData = {}
          cloneData.title = "Clone of: " + $scope.detail.title;
          cloneData.bean_name = $scope.detail.bean_name;
          cloneData.roast = $scope.detail.roast;
          cloneData.orientation = $scope.detail.orientation;
          cloneData.general_recipe_comment = $scope.detail.general_recipe_comment;
          cloneData.grind = $scope.detail.grind;
          cloneData.total_bean_amount = $scope.detail.total_bean_amount;
          cloneData.bean_units = $scope.detail.bean_units;
          cloneData.water_type = $scope.detail.water_type;
          cloneData.total_water_amount = $scope.detail.total_water_amount;
          cloneData.water_units = $scope.detail.water_units;
          cloneData.temp = $scope.detail.temp;
          cloneData.steps = [];

          $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/", cloneData).success(function(response){

            newRecipeId = response.id;
            steps = [];
            for(step in $scope.detail.steps){
              steps[step] = {};
              steps[step].step_number = $scope.detail.steps[step].step_number;
              steps[step].step_title = $scope.detail.steps[step].step_title;
              steps[step].step_body = $scope.detail.steps[step].step_body;
              steps[step].duration = $scope.detail.steps[step].duration;
              steps[step].water_amount = $scope.detail.steps[step].water_amount;

              $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ newRecipeId +"/steps/", steps[step])//end step post
            };//end loop to clone steps
          })
          .then(function(){
            $location.path("/"+ username +"/clone/"+ newRecipeId);
          })//end post new recipe
        }; //end recipe clone function

        $scope.showEditNote = function(noteId) {
          noteView = "div.note-view" + noteId.toString();
          editNote = "article.edit-note" + noteId.toString();
          $(noteView).addClass("hidden")
          $(editNote).removeClass("hidden")
        }
          // $scope.showEditStep = function(stepId){
          //   stepId = "div." + stepId.toString();
          //   $(stepId).removeClass("hidden")
          // }

        $scope.editNote = function(note){
          var note_id = note.id
          $http.put("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + note_id + "/", note)
          .then( function () {
            $(editNote).addClass("hidden");
            $(noteView).removeClass("hidden");
          })
        } //end editNote function

        $scope.deleteNote = function(noteId) {
          var noteId = noteId
          if (window.confirm("Are you sure you want to delete this note?")){
            $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + noteId + "/")
            .then(function(){var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
            })
          };
        }; //end deleteNote function

        $scope.showAddBrewNote = function(){
          $(".brew-form").toggleClass("hidden");
        };

        $scope.addBrewNote=function(){
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/brewnotes/', $scope.brewnote)
          .success(function (data) {
            $(".brew-form").toggleClass("hidden");
            var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
          })
        $scope.brewnote = { };
        $scope.addNote = false;
        }//Add Brew Note Form

        $scope.showNoteIcons = function(noteId){
          $(".note-icons").filter($("."+ noteId)).toggleClass("hidden");
        }

        $scope.makePublic = function(){ //makes recipes public
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };

          var publicData = {}  //build the recipe
          publicData.title = "Public Copy of: " + $scope.detail.title;
          publicData.bean_name = $scope.detail.bean_name;
          publicData.roast = $scope.detail.roast;
          publicData.orientation = $scope.detail.orientation;
          publicData.general_recipe_comment = $scope.detail.general_recipe_comment;
          publicData.grind = $scope.detail.grind;
          publicData.total_bean_amount = $scope.detail.total_bean_amount;
          publicData.bean_units = $scope.detail.bean_units;
          publicData.water_type = $scope.detail.water_type;
          publicData.total_water_amount = $scope.detail.total_water_amount;
          publicData.water_units = $scope.detail.water_units;
          publicData.temp = $scope.detail.temp;
          publicData.steps = [];

          $http.post("https://brew-keeper-api.herokuapp.com/api/users/public/recipes/", publicData).success(function(response){

            newRecipeId = response.id;

            steps = []; //build the steps
            for(step in $scope.detail.steps){
              steps[step] = {};
              steps[step].step_number = $scope.detail.steps[step].step_number;
              steps[step].step_title = $scope.detail.steps[step].step_title;
              steps[step].step_body = $scope.detail.steps[step].step_body;
              steps[step].duration = $scope.detail.steps[step].duration;
              steps[step].water_amount = $scope.detail.steps[step].water_amount;

              $http.post("https://brew-keeper-api.herokuapp.com/api/users/pubic/recipes/"+ newRecipeId +"/steps/", steps[step])//end step post

            } //end for loop to build steps
          }) //end .success for posting new recipe to public
          .then(function(){
            window.alert("Thank you for sharing " + $scope.detail.title)
          })

      }; // end makePublic function

      }) //end recipDetail controller

})();//END Angular IFEE
<|endoftext|>


<commit_before>// it should be unique enough, and it has to be a valid js variable name,
// so probably in camelCase, no dashes, no spaces, no dots, etc.
var UNIQUEID = 'kuus';

var CREDITS = [
  '/**',
  ' * <%= pkg.name %> v<%= pkg.version %>',
  ' * <%= pkg.homepage %>',
  ' *',
  ' * <%= pkg.description %>',
  ' *',
  ' * Copyright (c) 2014 <%= pkg.author %>',
  ' * Released under <%= pkg.license %> License',
  ' */',
  ''
];

var gulp = require('gulp');
var notify = require('gulp-notify');
var minifyHtml = require('gulp-minify-html');
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');
var replace = require('gulp-replace');
var uglify = require('gulp-uglify');
var autoprefixer = require('gulp-autoprefixer');
var inlineSource = require('gulp-inline-source');
var header = require('gulp-header');
var browserSync = require('browser-sync');
var jsesc = require('jsesc');
var fs = require('fs');

var injectTagTemplates = '<!-- inject:templates -->';
var injectTagStyles = '<!-- inject:styles -->';
var injectTagApp = '<!-- inject:app -->';

gulp.task('watch', function() {
  gulp.watch('./src/**/*', ['assemble']);
});

gulp.task('styles', function() {
  return gulp.src('./src/**/*.scss')
    .pipe(sass({
      onError: function(error) {
        notify({
          title: 'Compile Error',
          message: '<%= error.message %>'
        })
      }
    }))
    .pipe(minifyCss())
    .pipe(autoprefixer())
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('scripts', function() {
  return gulp.src('./src/**/*.js')
    .pipe(uglify())
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('templates', function() {
  return gulp.src('./src/**/*.html')
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('inline', ['styles', 'scripts', 'templates'], function() {
  return gulp.src('./.tmp/app/*.html')
    .pipe(inlineSource('./.tmp/app'))
    .pipe(gulp.dest('./.tmp/app'));
});

gulp.task('minify', ['inline'], function() {
  return gulp.src('./.tmp/**/*.html')
    .pipe(minifyHtml({ empty: true, spare: true, quotes: true }))
    .pipe(gulp.dest('./.tmp'))
})

gulp.task('assemble', ['minify'], function() {
  var template = fs.readFileSync('./.tmp/bookmarklet-skeleton.html', 'utf8');
  var style = fs.readFileSync('./.tmp/bookmarklet-skeleton.css', 'utf8');
  var app = fs.readFileSync('./.tmp/app/index.html', 'utf8');
  var pkg = require('./package.json');
  return gulp.src('./src/bookmarklet-skeleton.js')
    .pipe(rename(pkg.name + '.js'))
    .pipe(replace(injectTagTemplates, jsesc(template)))
    .pipe(replace(injectTagStyles, jsesc(style)))
    .pipe(replace(injectTagApp, jsesc(app)))
    .pipe(replace('UNIQUEID', UNIQUEID))
    .pipe(header(CREDITS.join('\n'), { pkg: pkg }))
    .pipe(gulp.dest('./'))
    // create also a minified version
    .pipe(uglify())
    .pipe(rename(pkg.name + '.min.js'))
    .pipe(header(CREDITS.join('\n'), { pkg: pkg }))
    .pipe(gulp.dest('./'))
    // and also a file ready to be copy-pasted in a new bookmarklet
    .pipe(uglify())
    .pipe(rename(pkg.name + '.url.js'))
    .pipe(header('javascript:'))
    .pipe(gulp.dest('./'))
});

function browserSyncInit(startPath, baseDir, files) {
  browserSync.init(files, {
    notify: false,
    open: true,
    startPath: startPath,
    server: {
      baseDir: baseDir
    }
  });
}

gulp.task('browser-sync', function() {
  browserSyncInit('/', [
    '',
    '.tmp'
  ], [
    './index.html',
    './*.js',
  ]);
});

gulp.task('default', ['watch', 'browser-sync']);<commit_msg>longer unique id<commit_after>// it should be unique enough, and it has to be a valid js variable name,
// so probably in camelCase, no dashes, no spaces, no dots, etc.
var UNIQUEID = 'kuusbkmrkletsd';

var CREDITS = [
  '/**',
  ' * <%= pkg.name %> v<%= pkg.version %>',
  ' * <%= pkg.homepage %>',
  ' *',
  ' * <%= pkg.description %>',
  ' *',
  ' * Copyright (c) 2014 <%= pkg.author %>',
  ' * Released under <%= pkg.license %> License',
  ' */',
  ''
];

var gulp = require('gulp');
var notify = require('gulp-notify');
var minifyHtml = require('gulp-minify-html');
var sass = require('gulp-sass');
var minifyCss = require('gulp-minify-css');
var rename = require('gulp-rename');
var replace = require('gulp-replace');
var uglify = require('gulp-uglify');
var autoprefixer = require('gulp-autoprefixer');
var inlineSource = require('gulp-inline-source');
var header = require('gulp-header');
var browserSync = require('browser-sync');
var jsesc = require('jsesc');
var fs = require('fs');

var injectTagTemplates = '<!-- inject:templates -->';
var injectTagStyles = '<!-- inject:styles -->';
var injectTagApp = '<!-- inject:app -->';

gulp.task('watch', function() {
  gulp.watch('./src/**/*', ['assemble']);
});

gulp.task('styles', function() {
  return gulp.src('./src/**/*.scss')
    .pipe(sass({
      onError: function(error) {
        notify({
          title: 'Compile Error',
          message: '<%= error.message %>'
        })
      }
    }))
    .pipe(minifyCss())
    .pipe(autoprefixer())
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('scripts', function() {
  return gulp.src('./src/**/*.js')
    .pipe(uglify())
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('templates', function() {
  return gulp.src('./src/**/*.html')
    .pipe(gulp.dest('./.tmp'))
});

gulp.task('inline', ['styles', 'scripts', 'templates'], function() {
  return gulp.src('./.tmp/app/*.html')
    .pipe(inlineSource('./.tmp/app'))
    .pipe(gulp.dest('./.tmp/app'));
});

gulp.task('minify', ['inline'], function() {
  return gulp.src('./.tmp/**/*.html')
    .pipe(minifyHtml({ empty: true, spare: true, quotes: true }))
    .pipe(gulp.dest('./.tmp'))
})

gulp.task('assemble', ['minify'], function() {
  var template = fs.readFileSync('./.tmp/bookmarklet-skeleton.html', 'utf8');
  var style = fs.readFileSync('./.tmp/bookmarklet-skeleton.css', 'utf8');
  var app = fs.readFileSync('./.tmp/app/index.html', 'utf8');
  var pkg = require('./package.json');
  return gulp.src('./src/bookmarklet-skeleton.js')
    .pipe(rename(pkg.name + '.js'))
    .pipe(replace(injectTagTemplates, jsesc(template)))
    .pipe(replace(injectTagStyles, jsesc(style)))
    .pipe(replace(injectTagApp, jsesc(app)))
    .pipe(replace('UNIQUEID', UNIQUEID))
    .pipe(header(CREDITS.join('\n'), { pkg: pkg }))
    .pipe(gulp.dest('./'))
    // create also a minified version
    .pipe(uglify())
    .pipe(rename(pkg.name + '.min.js'))
    .pipe(header(CREDITS.join('\n'), { pkg: pkg }))
    .pipe(gulp.dest('./'))
    // and also a file ready to be copy-pasted in a new bookmarklet
    .pipe(uglify())
    .pipe(rename(pkg.name + '.url.js'))
    .pipe(header('javascript:'))
    .pipe(gulp.dest('./'))
});

function browserSyncInit(startPath, baseDir, files) {
  browserSync.init(files, {
    notify: false,
    open: true,
    startPath: startPath,
    server: {
      baseDir: baseDir
    }
  });
}

gulp.task('browser-sync', function() {
  browserSyncInit('/', [
    '',
    '.tmp'
  ], [
    './index.html',
    './*.js',
  ]);
});

gulp.task('default', ['watch', 'browser-sync']);<|endoftext|>


<commit_before>;(function(){//IFEE

angular.module('brewKeeper')
      .controller('recipeDetail', function($scope, $http, $location, $routeParams, $rootScope){
          var id = $routeParams.id;
          var username = $routeParams.username;
          $scope.username = $routeParams.username;
          $scope.id = $routeParams.id;
          $(document).scrollTop(0);

          $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
            .then(function(response){
              $rootScope.detail = response.data;
              $rootScope.steps = response.data.steps;
              $rootScope.notes = response.data.brewnotes;
              var currentRating = $rootScope.detail.rating;
              $scope.rating = 0;
              $scope.ratings = [{
                  current: currentRating,
                  max: 5
              }];

          }) //end http.get

          $scope.rateRecipe = function (rating) {
            var newRating = {"rating": rating}
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", newRating)
          }

          $scope.EliminateRecipe = function() {
            if (window.confirm("Are you sure you want to delete " + $scope.detail.title + "?")){
              $http.delete('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/').then(function(){
                $location.path('/users/'+ username);
              })
            };
          }; //end Eliminate function

          $scope.showSteps = function(stepId){
            stepId= "article." + stepId.toString()
            $(stepId).toggleClass("hidden")
          };

          // $scope.showNotes = function(){
          //   $("div.notes").toggleClass("hidden")
          // };
          $scope.showEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden")
          }

          $scope.deleteStep = function(stepNumber, stepId){
            if (window.confirm("Are you sure you want to delete step " + stepNumber + "?")){
              $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ stepId + "/").then(function(){
                $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id +'/')
                  .then(function(response){
                    $scope.steps = response.data.steps;
                    if(response.data.steps.length == 0){
                      $(".brew-it-button").addClass("hidden");
                      $(".no-steps").removeClass("hidden");
                    }
                  })
              })
            }
          } //end deleteStep function

          $scope.editStep = function(step){
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step)
              .then($scope.hideEditStep(step.id))
          } //end editStep function
          $scope.hideEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).addClass("hidden")
          }

          $scope.step = { }//Might need to prepopulate this with empty strings for each key... Maybe...
          $scope.addStep=function(){ //add step function
            $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/", $scope.step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $(".brew-it-button").removeClass("hidden");
                  $(".no-steps").addClass("hidden");
                  $rootScope.steps = response.data.steps;
                })
            })
            $scope.step= { };
          } //end new step submit function

        $scope.showAddSteps = function(){
          $("form.create-new-step").toggleClass("hidden");
        };//Reveal "Add Step" when new recipe form is submitted.
        // $scope.hideAddSteps = function(){
        //   $("form.create-new-step").addClass("hidden");
        //   $("button.done-adding").addClass("hidden");
        // }; //hides the add step form

        $('.edit-button').on('click', function(){
          $('.edit-recipe').removeClass("hidden");
          $('.recipe-view').addClass("hidden");
        });

        $(".no-steps").click(function(){
          $scope.showAddSteps()
        })

      $scope.editRecipe = function(recipe){
        $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", recipe)
        .then( function () {
          $('.edit-recipe').addClass("hidden");
          $('.recipe-view').removeClass("hidden");
        })
      } //end editRecipe function
        $('.cancel-recipe-edit').on('click', function(){
          $('.recipe-view').removeClass("hidden");
          $('.edit-recipe').addClass("hidden");
        });

        // Function for cloning recipes
        $scope.cloneRecipe = function(){
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };
          var cloneData = {}
          cloneData.title = "Clone of: " + $scope.detail.title;
          cloneData.bean_name = $scope.detail.bean_name;
          cloneData.roast = $scope.detail.roast;
          cloneData.orientation = $scope.detail.orientation;
          cloneData.general_recipe_comment = $scope.detail.general_recipe_comment;
          cloneData.grind = $scope.detail.grind;
          cloneData.total_bean_amount = $scope.detail.total_bean_amount;
          cloneData.bean_units = $scope.detail.bean_units;
          cloneData.water_type = $scope.detail.water_type;
          cloneData.total_water_amount = $scope.detail.total_water_amount;
          cloneData.water_units = $scope.detail.water_units;
          cloneData.temp = $scope.detail.temp;
          cloneData.steps = [];

          $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/", cloneData).success(function(response){

            newRecipeId = response.id;
            steps = [];
            for(step in $scope.detail.steps){
              steps[step] = {};
              steps[step].step_number = $scope.detail.steps[step].step_number;
              steps[step].step_title = $scope.detail.steps[step].step_title;
              steps[step].step_body = $scope.detail.steps[step].step_body;
              steps[step].duration = $scope.detail.steps[step].duration;
              steps[step].water_amount = $scope.detail.steps[step].water_amount;

              $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ newRecipeId +"/steps/", steps[step])//end step post
            };//end loop to clone steps
          })
          .then(function(){
            $location.path("/users/"+ username +"/clone/"+ newRecipeId);
          })//end post new recipe
        }; //end recipe clone function

        $scope.showEditNote = function(noteId) {
          noteView = "div.note-view" + noteId.toString();
          editNote = "article.edit-note" + noteId.toString();
          $(noteView).addClass("hidden")
          $(editNote).removeClass("hidden")
        }
          // $scope.showEditStep = function(stepId){
          //   stepId = "div." + stepId.toString();
          //   $(stepId).removeClass("hidden")
          // }

        $scope.editNote = function(note){
          var note_id = note.id
          $http.put("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + note_id + "/", note)
          .then( function () {
            $(editNote).addClass("hidden");
            $(noteView).removeClass("hidden");
          })
        } //end editNote function

        $scope.deleteNote = function(noteId) {
          var noteId = noteId
          if (window.confirm("Are you sure you want to delete this note?")){
            $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + noteId + "/")
            .then(function(){var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
            })
          };
        }; //end deleteNote function

        $scope.showAddBrewNote = function(){
          $(".brew-form").toggleClass("hidden");
        };

        $scope.addBrewNote=function(){
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/brewnotes/', $scope.brewnote)
          .success(function (data) {
            var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
          })
        $scope.brewnote = { };
        $scope.addNote = false;
        }//Add Brew Note Form

        $scope.showNoteIcons = function(noteId){
          $(".note-icons").filter($("."+ noteId)).toggleClass("hidden");
        }

      }) //end recipDetail controller

})();//END Angular IFEE
<commit_msg>new steps get a step number of steps.length+1<commit_after>;(function(){//IFEE

angular.module('brewKeeper')
      .controller('recipeDetail', function($scope, $http, $location, $routeParams, $rootScope){
          var id = $routeParams.id;
          var username = $routeParams.username;
          $scope.username = $routeParams.username;
          $scope.id = $routeParams.id;
          $(document).scrollTop(0);

          $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
            .then(function(response){
              $rootScope.detail = response.data;
              $rootScope.steps = response.data.steps;
              $rootScope.notes = response.data.brewnotes;
              var currentRating = $rootScope.detail.rating;
              $scope.rating = 0;
              $scope.ratings = [{
                  current: currentRating,
                  max: 5
              }];

          }) //end http.get

          $scope.rateRecipe = function (rating) {
            var newRating = {"rating": rating}
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", newRating)
          }

          $scope.EliminateRecipe = function() {
            if (window.confirm("Are you sure you want to delete " + $scope.detail.title + "?")){
              $http.delete('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/').then(function(){
                $location.path('/users/'+ username);
              })
            };
          }; //end Eliminate function

          $scope.showSteps = function(stepId){
            stepId= "article." + stepId.toString()
            $(stepId).toggleClass("hidden")
          };

          // $scope.showNotes = function(){
          //   $("div.notes").toggleClass("hidden")
          // };
          $scope.showEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).toggleClass("hidden")
          }

          $scope.deleteStep = function(stepNumber, stepId){
            if (window.confirm("Are you sure you want to delete step " + stepNumber + "?")){
              $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ stepId + "/").then(function(){
                $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id +'/')
                  .then(function(response){
                    $scope.steps = response.data.steps;
                    if(response.data.steps.length == 0){
                      $(".brew-it-button").addClass("hidden");
                      $(".no-steps").removeClass("hidden");
                    }
                  })
              })
            }
          } //end deleteStep function

          $scope.editStep = function(step){
            $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/"+ step.id + "/", step)
              .then($scope.hideEditStep(step.id))
          } //end editStep function
          $scope.hideEditStep = function(stepId){
            stepId = "div." + stepId.toString();
            $(stepId).addClass("hidden")
          }

          $scope.step = { }//Might need to prepopulate this with empty strings for each key... Maybe...
          $scope.addStep=function(){ //add step function
            $scope.step.step_number = $scope.steps.length + 1;
            $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ id +"/steps/", $scope.step).then(function(){
              $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/')
                .then(function(response){
                  $(".brew-it-button").removeClass("hidden");
                  $(".no-steps").addClass("hidden");
                  $rootScope.steps = response.data.steps;
                })
            })
            $scope.step= { };
          } //end new step submit function

        $scope.showAddSteps = function(){
          $("form.create-new-step").toggleClass("hidden");
        };//Reveal "Add Step" when new recipe form is submitted.
        // $scope.hideAddSteps = function(){
        //   $("form.create-new-step").addClass("hidden");
        //   $("button.done-adding").addClass("hidden");
        // }; //hides the add step form

        $('.edit-button').on('click', function(){
          $('.edit-recipe').removeClass("hidden");
          $('.recipe-view').addClass("hidden");
        });

        $(".no-steps").click(function(){
          $scope.showAddSteps()
        })

      $scope.editRecipe = function(recipe){
        $http.patch("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/", recipe)
        .then( function () {
          $('.edit-recipe').addClass("hidden");
          $('.recipe-view').removeClass("hidden");
        })
      } //end editRecipe function
        $('.cancel-recipe-edit').on('click', function(){
          $('.recipe-view').removeClass("hidden");
          $('.edit-recipe').addClass("hidden");
        });

        // Function for cloning recipes
        $scope.cloneRecipe = function(){
          if (!window.confirm("Are you sure you want to clone "+ $scope.detail.title +" ?")){
            return;
          };
          var cloneData = {}
          cloneData.title = "Clone of: " + $scope.detail.title;
          cloneData.bean_name = $scope.detail.bean_name;
          cloneData.roast = $scope.detail.roast;
          cloneData.orientation = $scope.detail.orientation;
          cloneData.general_recipe_comment = $scope.detail.general_recipe_comment;
          cloneData.grind = $scope.detail.grind;
          cloneData.total_bean_amount = $scope.detail.total_bean_amount;
          cloneData.bean_units = $scope.detail.bean_units;
          cloneData.water_type = $scope.detail.water_type;
          cloneData.total_water_amount = $scope.detail.total_water_amount;
          cloneData.water_units = $scope.detail.water_units;
          cloneData.temp = $scope.detail.temp;
          cloneData.steps = [];

          $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/", cloneData).success(function(response){

            newRecipeId = response.id;
            steps = [];
            for(step in $scope.detail.steps){
              steps[step] = {};
              steps[step].step_number = $scope.detail.steps[step].step_number;
              steps[step].step_title = $scope.detail.steps[step].step_title;
              steps[step].step_body = $scope.detail.steps[step].step_body;
              steps[step].duration = $scope.detail.steps[step].duration;
              steps[step].water_amount = $scope.detail.steps[step].water_amount;

              $http.post("https://brew-keeper-api.herokuapp.com/api/users/"+ username +"/recipes/"+ newRecipeId +"/steps/", steps[step])//end step post
            };//end loop to clone steps
          })
          .then(function(){
            $location.path("/users/"+ username +"/clone/"+ newRecipeId);
          })//end post new recipe
        }; //end recipe clone function

        $scope.showEditNote = function(noteId) {
          noteView = "div.note-view" + noteId.toString();
          editNote = "article.edit-note" + noteId.toString();
          $(noteView).addClass("hidden")
          $(editNote).removeClass("hidden")
        }
          // $scope.showEditStep = function(stepId){
          //   stepId = "div." + stepId.toString();
          //   $(stepId).removeClass("hidden")
          // }

        $scope.editNote = function(note){
          var note_id = note.id
          $http.put("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + note_id + "/", note)
          .then( function () {
            $(editNote).addClass("hidden");
            $(noteView).removeClass("hidden");
          })
        } //end editNote function

        $scope.deleteNote = function(noteId) {
          var noteId = noteId
          if (window.confirm("Are you sure you want to delete this note?")){
            $http.delete("https://brew-keeper-api.herokuapp.com/api/users/"+ username + "/recipes/"+ id + "/brewnotes/" + noteId + "/")
            .then(function(){var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
            })
          };
        }; //end deleteNote function

        $scope.showAddBrewNote = function(){
          $(".brew-form").toggleClass("hidden");
        };

        $scope.addBrewNote=function(){
          $http.post('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + '/brewnotes/', $scope.brewnote)
          .success(function (data) {
            var id = $scope.id;
            $http.get('https://brew-keeper-api.herokuapp.com/api/users/' + username + '/recipes/' + id + "/")
              .then(function(response){
                $rootScope.notes = response.data.brewnotes;
              })
          })
        $scope.brewnote = { };
        $scope.addNote = false;
        }//Add Brew Note Form

        $scope.showNoteIcons = function(noteId){
          $(".note-icons").filter($("."+ noteId)).toggleClass("hidden");
        }

      }) //end recipDetail controller

})();//END Angular IFEE
<|endoftext|>


<commit_before>import "whatwg-fetch";
import { push } from "react-router-redux";

// List
export const CHANGE_LIST_MODE = "change_list_mode";
export const CHANGE_ALBUM_MODE = "change_album_mode";
export const CHANGE_ALBUM_EXPANDED_SINGLE = "change_album_expanded_single";

export const listMode = {
    track: "track",
    album: "album"
};

export const albumMode = {
    collapsed: "collapsed",
    expanded: "expnded"
};

// Play
export const PLAY = "play";
export const PLAY_ALL = "play_all";
export const PLAY_NEXT = "play_next";
export const CLEAR = "clear";

// Keyboard
export const SWITCH_POINTER = "switch_pointer";
export const MOVE_RESET = "move_reset";
export const MOVE_TO = "move_to";
export const UPDATE_INDEX = "update_index";
export const SETUP_LIST = "setup_list";

// Genre
export const UPDATE_GENRES = "fetch_genres";

// Action creators

export const changeListModeToTrack = () => {
    return changeListMode(listMode.track);
};
export const changeListModeToAlbum = () => {
    return changeListMode(listMode.album);
};
const changeListMode = (mode) => {
    return {
        type: CHANGE_LIST_MODE,
        mode: mode
    };
};

export const collapseAlbum = () => {
    return changeAlbumMode(albumMode.collapsed);
};
export const expandAlbum = () => {
    return changeAlbumMode(albumMode.expanded);
};
const changeAlbumMode = (mode) => {
    return {
        type: CHANGE_ALBUM_MODE,
        mode: mode
    };
};

export const expandAlbumSingle = (collectionId) => {
    return changeAlbumExpandedSingle(collectionId, true);
};
export const collapseAlbumSingle = (collectionId) => {
    return changeAlbumExpandedSingle(collectionId, false);
};
const changeAlbumExpandedSingle = (collectionId, expanded) => {
    return {
        type: CHANGE_ALBUM_EXPANDED_SINGLE,
        collection_id: collectionId,
        expanded: expanded
    };
};

export const play = (track) => {
    return {
        type: PLAY,
        track: track
    };
};

export const playAll = (tracks) => {
    return {
        type: PLAY_ALL,
        tracks: tracks
    };
};

export const playNext = () => {
    return {
        type: PLAY_NEXT
    };
};

export const clear = () => {
    return {
        type: CLEAR
    };
};

export const switchPointer = (active) => {
    return {
        type: SWITCH_POINTER,
        active: active
    };
};

export const moveReset = () => {
    return {
        type: MOVE_RESET
    };
};

export const moveTo = (index) => {
    return {
        type: MOVE_TO,
        index: index
    };
};

export const updateIndex = (index) => {
    return {
        type: UPDATE_INDEX,
        index: index
    };
};

export const setupList = (tracks) => {
    return {
        type: SETUP_LIST,
        tracks: tracks
    };
};

export const focus = (element) => {
    return () => {
        const top = element.offsetTop - (window.innerHeight / 2);
        window.scrollTo(0, top);
    };
};

export const selectTrack = (track) => {
    return (dispatch, getState) => {
        const pointer = getState().app.pointer;
        const foundIndex = pointer.tracks.indexOf(track);
        dispatch(switchPointer(false));
        dispatch(updateIndex(foundIndex));
    };
};

export const watchKeyboard = () => {
    const findNextIndex = (state) => {
        const list = state.app.list;
        const pointer = state.app.pointer;
        const nextIndex = pointer.index + 1;

        if (list.mode === listMode.track || list.albumMode === albumMode.expanded) {
            return nextIndex;
        }

        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack || list.albumExpandMap[selectedTrack.collection_id]) {
            return nextIndex;
        }

        const tracks = pointer.tracks.slice(pointer.index + 1);
        const indexDiff = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
        return nextIndex + indexDiff;
    };

    const findPreviousIndex = (state) => {
        const list = state.app.list;
        const pointer = state.app.pointer;
        const previousIndex = pointer.index - 1;

        if (list.mode === listMode.track || list.albumMode === albumMode.expanded) {
            return previousIndex;
        }

        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack) {
            return previousIndex;
        }

        const tracks = pointer.tracks.slice(0, pointer.index).reverse();
        const foundIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);

        const selectingAlbumTracks = tracks[foundIndex];
        if (!selectingAlbumTracks || list.albumExpandMap[selectingAlbumTracks.collection_id]) {
            return previousIndex;
        }

        const secondTracks = tracks.slice(foundIndex + 1);
        const secondFoundIndex = secondTracks.findIndex(track => track.collection_id !== selectingAlbumTracks.collection_id);
        const skippable = foundIndex === 0 || !list.albumExpandMap[selectedTrack.collection_id];
        if (secondFoundIndex === -1 && skippable) {
            return 0;
        } else if (skippable) {
            return previousIndex - (foundIndex + secondFoundIndex);
        }
        return previousIndex;
    };

    const findAlbumTrackIndex = (state) => {
        const pointer = state.app.pointer;
        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack) {
            return -1;
        }

        const tracks = pointer.tracks.slice(0, pointer.index).reverse();
        const foundIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
        if (foundIndex === -1) {
            return 0;
        }

        return pointer.index - foundIndex;
    };

    return (dispatch, getState) => {
        window.addEventListener("click", () => {
            const pointer = getState().app.pointer;
            if (pointer.active) {
                dispatch(switchPointer(false));
            }
        });
        window.addEventListener("keydown", event => {
            const app = getState().app;
            const pointer = app.pointer;
            const list = app.list;

            switch (event.code) {
                case "KeyJ": {
                    dispatch(moveTo(findNextIndex(getState())));
                    break;
                }
                case "KeyK": {
                    dispatch(moveTo(findPreviousIndex(getState())));
                    break;
                }
                case "KeyP": {
                    if (event.shiftKey) {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            const artistTracks = pointer.tracks.filter(track => track.artist_id === selectedTrack.artist_id);
                            dispatch(playAll(artistTracks));
                            break;
                        }
                    }
                    dispatch(playAll(pointer.tracks));
                    break;
                }
                case "Enter": {
                    const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                    if (!selectedTrack) {
                        break;
                    }
                    event.preventDefault();
                    if (event.shiftKey) {
                        const albumTrackIndex = findAlbumTrackIndex(getState());
                        const tracks = pointer.tracks.slice(albumTrackIndex);
                        const nextAlbumTrackIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
                        dispatch(playAll(tracks.slice(0, nextAlbumTrackIndex)));
                    } else {
                        dispatch(play(selectedTrack));
                    }
                    break;
                }
                case "Escape":
                case "Period":
                case "KeyS": {
                    dispatch(clear());
                    dispatch(switchPointer(false));
                    break;
                }
                case "Tab": {
                    dispatch(switchPointer(false));
                    break;
                }
                case "KeyL": {
                    const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                    if (selectedTrack) {
                        const selectedCollectionId = selectedTrack.collection_id;
                        const albumExpanded = list.albumExpandMap[selectedCollectionId] || false;
                        dispatch(changeAlbumExpandedSingle(selectedCollectionId, !albumExpanded));
                        dispatch(moveTo(findAlbumTrackIndex(getState())));
                    }
                    break;
                }
                case "KeyA": {
                    if (event.shiftKey) {
                        const nextMode = list.albumMode === albumMode.expanded ? albumMode.collapsed : albumMode.expanded;
                        dispatch(changeAlbumMode(nextMode));
                    } else {
                        const nextMode = list.mode === listMode.album ? listMode.track : listMode.album;
                        dispatch(changeListMode(nextMode));
                    }
                    dispatch(switchPointer(false));
                    break;
                }
                case "KeyT": {
                    window.scrollTo(0, 0);
                    dispatch(moveReset());
                    break;
                }
                case "KeyO": {
                    if (event.shiftKey) {
                        const currentTrack = app.play.currentTrack;
                        if (currentTrack) {
                            window.open(currentTrack.track_view_url, "_blank");
                        }
                    } else {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            window.open(selectedTrack.track_view_url, "_blank");
                        }
                    }
                    break;
                }
                case "KeyG": {
                    if (event.shiftKey) {
                        dispatch(push("/"));
                    } else {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            dispatch(push(`/genres/${encodeURIComponent(selectedTrack.genre_name)}`));
                        }
                    }
                    break;
                }
                default:
                // Nothing to do
            }
        });
    };
};

export const fetchGenres = () => {
    return (dispatch) => {
        fetch("/api/genres")
            .then(res => res.json())
            .then(json =>
                dispatch(updateGenres(json.genre_names))
            ).catch((e) => { console.log(e); });
    };
};

export const updateGenres = (genreNames) => {
    return {
        type: UPDATE_GENRES,
        genreNames: genreNames
    };
};
<commit_msg>キーボードでだけ見られるアーティストページを追加<commit_after>import "whatwg-fetch";
import { push } from "react-router-redux";

// List
export const CHANGE_LIST_MODE = "change_list_mode";
export const CHANGE_ALBUM_MODE = "change_album_mode";
export const CHANGE_ALBUM_EXPANDED_SINGLE = "change_album_expanded_single";

export const listMode = {
    track: "track",
    album: "album"
};

export const albumMode = {
    collapsed: "collapsed",
    expanded: "expnded"
};

// Play
export const PLAY = "play";
export const PLAY_ALL = "play_all";
export const PLAY_NEXT = "play_next";
export const CLEAR = "clear";

// Keyboard
export const SWITCH_POINTER = "switch_pointer";
export const MOVE_RESET = "move_reset";
export const MOVE_TO = "move_to";
export const UPDATE_INDEX = "update_index";
export const SETUP_LIST = "setup_list";

// Genre
export const UPDATE_GENRES = "fetch_genres";

// Action creators

export const changeListModeToTrack = () => {
    return changeListMode(listMode.track);
};
export const changeListModeToAlbum = () => {
    return changeListMode(listMode.album);
};
const changeListMode = (mode) => {
    return {
        type: CHANGE_LIST_MODE,
        mode: mode
    };
};

export const collapseAlbum = () => {
    return changeAlbumMode(albumMode.collapsed);
};
export const expandAlbum = () => {
    return changeAlbumMode(albumMode.expanded);
};
const changeAlbumMode = (mode) => {
    return {
        type: CHANGE_ALBUM_MODE,
        mode: mode
    };
};

export const expandAlbumSingle = (collectionId) => {
    return changeAlbumExpandedSingle(collectionId, true);
};
export const collapseAlbumSingle = (collectionId) => {
    return changeAlbumExpandedSingle(collectionId, false);
};
const changeAlbumExpandedSingle = (collectionId, expanded) => {
    return {
        type: CHANGE_ALBUM_EXPANDED_SINGLE,
        collection_id: collectionId,
        expanded: expanded
    };
};

export const play = (track) => {
    return {
        type: PLAY,
        track: track
    };
};

export const playAll = (tracks) => {
    return {
        type: PLAY_ALL,
        tracks: tracks
    };
};

export const playNext = () => {
    return {
        type: PLAY_NEXT
    };
};

export const clear = () => {
    return {
        type: CLEAR
    };
};

export const switchPointer = (active) => {
    return {
        type: SWITCH_POINTER,
        active: active
    };
};

export const moveReset = () => {
    return {
        type: MOVE_RESET
    };
};

export const moveTo = (index) => {
    return {
        type: MOVE_TO,
        index: index
    };
};

export const updateIndex = (index) => {
    return {
        type: UPDATE_INDEX,
        index: index
    };
};

export const setupList = (tracks) => {
    return {
        type: SETUP_LIST,
        tracks: tracks
    };
};

export const focus = (element) => {
    return () => {
        const top = element.offsetTop - (window.innerHeight / 2);
        window.scrollTo(0, top);
    };
};

export const selectTrack = (track) => {
    return (dispatch, getState) => {
        const pointer = getState().app.pointer;
        const foundIndex = pointer.tracks.indexOf(track);
        dispatch(switchPointer(false));
        dispatch(updateIndex(foundIndex));
    };
};

export const watchKeyboard = () => {
    const findNextIndex = (state) => {
        const list = state.app.list;
        const pointer = state.app.pointer;
        const nextIndex = pointer.index + 1;

        if (list.mode === listMode.track || list.albumMode === albumMode.expanded) {
            return nextIndex;
        }

        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack || list.albumExpandMap[selectedTrack.collection_id]) {
            return nextIndex;
        }

        const tracks = pointer.tracks.slice(pointer.index + 1);
        const indexDiff = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
        return nextIndex + indexDiff;
    };

    const findPreviousIndex = (state) => {
        const list = state.app.list;
        const pointer = state.app.pointer;
        const previousIndex = pointer.index - 1;

        if (list.mode === listMode.track || list.albumMode === albumMode.expanded) {
            return previousIndex;
        }

        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack) {
            return previousIndex;
        }

        const tracks = pointer.tracks.slice(0, pointer.index).reverse();
        const foundIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);

        const selectingAlbumTracks = tracks[foundIndex];
        if (!selectingAlbumTracks || list.albumExpandMap[selectingAlbumTracks.collection_id]) {
            return previousIndex;
        }

        const secondTracks = tracks.slice(foundIndex + 1);
        const secondFoundIndex = secondTracks.findIndex(track => track.collection_id !== selectingAlbumTracks.collection_id);
        const skippable = foundIndex === 0 || !list.albumExpandMap[selectedTrack.collection_id];
        if (secondFoundIndex === -1 && skippable) {
            return 0;
        } else if (skippable) {
            return previousIndex - (foundIndex + secondFoundIndex);
        }
        return previousIndex;
    };

    const findAlbumTrackIndex = (state) => {
        const pointer = state.app.pointer;
        const selectedTrack = pointer.tracks[pointer.index];
        if (!selectedTrack) {
            return -1;
        }

        const tracks = pointer.tracks.slice(0, pointer.index).reverse();
        const foundIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
        if (foundIndex === -1) {
            return 0;
        }

        return pointer.index - foundIndex;
    };

    return (dispatch, getState) => {
        window.addEventListener("click", () => {
            const pointer = getState().app.pointer;
            if (pointer.active) {
                dispatch(switchPointer(false));
            }
        });
        window.addEventListener("keydown", event => {
            const app = getState().app;
            const pointer = app.pointer;
            const list = app.list;

            switch (event.code) {
                case "KeyJ": {
                    dispatch(moveTo(findNextIndex(getState())));
                    break;
                }
                case "KeyK": {
                    dispatch(moveTo(findPreviousIndex(getState())));
                    break;
                }
                case "KeyP": {
                    if (event.shiftKey) {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            const artistTracks = pointer.tracks.filter(track => track.artist_id === selectedTrack.artist_id);
                            dispatch(playAll(artistTracks));
                            break;
                        }
                    }
                    dispatch(playAll(pointer.tracks));
                    break;
                }
                case "Enter": {
                    const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                    if (!selectedTrack) {
                        break;
                    }
                    event.preventDefault();
                    if (event.shiftKey) {
                        const albumTrackIndex = findAlbumTrackIndex(getState());
                        const tracks = pointer.tracks.slice(albumTrackIndex);
                        const nextAlbumTrackIndex = tracks.findIndex(track => track.collection_id !== selectedTrack.collection_id);
                        dispatch(playAll(tracks.slice(0, nextAlbumTrackIndex)));
                    } else {
                        dispatch(play(selectedTrack));
                    }
                    break;
                }
                case "Escape":
                case "Period":
                case "KeyS": {
                    dispatch(clear());
                    dispatch(switchPointer(false));
                    break;
                }
                case "Tab": {
                    dispatch(switchPointer(false));
                    break;
                }
                case "KeyL": {
                    const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                    if (selectedTrack) {
                        const selectedCollectionId = selectedTrack.collection_id;
                        const albumExpanded = list.albumExpandMap[selectedCollectionId] || false;
                        dispatch(changeAlbumExpandedSingle(selectedCollectionId, !albumExpanded));
                        dispatch(moveTo(findAlbumTrackIndex(getState())));
                    }
                    break;
                }
                case "KeyA": {
                    if (event.shiftKey) {
                        const nextMode = list.albumMode === albumMode.expanded ? albumMode.collapsed : albumMode.expanded;
                        dispatch(changeAlbumMode(nextMode));
                    } else {
                        const nextMode = list.mode === listMode.album ? listMode.track : listMode.album;
                        dispatch(changeListMode(nextMode));
                    }
                    dispatch(switchPointer(false));
                    break;
                }
                case "KeyT": {
                    window.scrollTo(0, 0);
                    dispatch(moveReset());
                    break;
                }
                case "KeyO": {
                    if (event.shiftKey) {
                        const currentTrack = app.play.currentTrack;
                        if (currentTrack) {
                            window.open(currentTrack.track_view_url, "_blank");
                        }
                    } else {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            window.open(selectedTrack.track_view_url, "_blank");
                        }
                    }
                    break;
                }
                case "KeyG": {
                    if (event.shiftKey) {
                        dispatch(push("/"));
                    } else {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            dispatch(push(`/genres/${encodeURIComponent(selectedTrack.genre_name)}`));
                        }
                    }
                    break;
                }
                case "KeyM": {
                    if (event.shiftKey) {
                        dispatch(push("/"));
                    } else {
                        const selectedTrack = pointer.active && pointer.tracks[pointer.index];
                        if (selectedTrack) {
                            dispatch(push(`/artists/${encodeURIComponent(selectedTrack.artist_name)}/${selectedTrack.artist_id}`));
                        }
                    }
                    break;
                }
                default:
                // Nothing to do
            }
        });
    };
};

export const fetchGenres = () => {
    return (dispatch) => {
        fetch("/api/genres")
            .then(res => res.json())
            .then(json =>
                dispatch(updateGenres(json.genre_names))
            ).catch((e) => { console.log(e); });
    };
};

export const updateGenres = (genreNames) => {
    return {
        type: UPDATE_GENRES,
        genreNames: genreNames
    };
};
<|endoftext|>


<commit_before>// GULP TEMPLATE - Gulfile.js - Victor Allegret
//
//   - $ gulp
//   - $ gulp build
//   - $ gulp clean
//
// --------------------------------------------------------

////////////////////
// VARIABLES
////////////////////////////////////////////////////////////////////////////////

// REQUIRE
// ---------------------------------------------------------

var gulp    = require('gulp'),
    // require every plugins
    $ = require('gulp-load-plugins')({
        pattern: '*'
    })

// PATH
// ---------------------------------------------------------

///// PATHS FOR DEV
var slim_dev    = './dev/views/',
    sass_dev    = './dev/assets/stylesheets/',
    coffee_dev  = './dev/assets/javascripts/',
    fonts_dev   = './dev/assets/fonts/',
    img_dev     = './dev/assets/images/',
    dev         = './dev';

///// PATH FOR PROD
var slim_build     = './build/views/',
    sass_build     = './build/assets/stylesheets/',
    coffee_build   = './build/assets/javascripts/',
    fonts_build    = './build/assets/fonts/',
    img_build      = './build/assets/images/',
    build          = './build';

////////////////////
// TASKS
////////////////////////////////////////////////////////////////////////////////

// COMPILE SLIM TO HTML
// ---------------------------------------------------------
gulp.task('slim', function () {
  return gulp.src([slim_dev + '/**/*.slim', '!./dev/views/partials/**/*.slim', '!./dev/views/layout/**/*.slim'])
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin
      })(err);
    }}))
    // Keep non-updated files infos in cache
    .pipe($.cached(slim_dev + '/**/*.slim'))
    // compile slim to html
    .pipe($.slim({
      pretty: false,
      include: true
    }))
    // remove all folder
    .pipe($.rename({dirname: ''}))
    // copy result to build folder
    .pipe(gulp.dest(build))
    // notify when task completed
    .pipe($.notify({message: 'Slim compilation completed !', onLast: true}));
});

// COMPILE SASS TO CSS
// ---------------------------------------------------------
gulp.task('sass', function () {
  return gulp.src([sass_dev + '/**/*.sass', '!./dev/assets/stylesheets/vendors/**/*.{css,scss,sass}'])
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin,
      })(err);
    }}))
    // init sourcemaps
    .pipe($.sourcemaps.init())
    // add sass glob import
    .pipe($.sassGlob())
    // compile sass to css
    .pipe($.sass())
    // add auto-prefixes
    .pipe($.autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    // concat all files
    .pipe($.concat('main.css'))
    // rename to .min
    .pipe($.rename('main.min.css'))
    // write sourcemaps
    .pipe($.sourcemaps.write())
    // copy result to build folder
    .pipe(gulp.dest(sass_build))
    // notify when task completed
    .pipe($.notify({message: 'Sass compilation completed !', onLast: true}));
});

// CONCAT CSS VENDORS
// ---------------------------------------------------------
gulp.task('cssVendors', function () {
  return gulp.src(sass_dev + '/vendors/**/*.{css,scss,sass}')
    // compile sass to css
    .pipe($.sass())
    // concat all files
    .pipe($.concat('vendors.css'))
    // rename to .min
    .pipe($.rename('vendors.min.css'))
    // copy result to build folder
    .pipe(gulp.dest(sass_build))
    // notify when task completed
    .pipe($.notify({message: 'Css vendors compilation completed !', onLast: true}));
});

// COMPILE COFFEE TO JS
// ---------------------------------------------------------
gulp.task('coffee', function() {
  return gulp.src(coffee_dev + '/main.coffee')
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin,
      })(err);
    }}))
    // add include for coffee
    .pipe($.include({ extensions: "coffee" }))
    // compile coffee to js
    .pipe($.coffee())
    // concat all files
    .pipe($.concat('main.js'))
    // rename to .min
    .pipe($.rename('main.min.js'))
    // minify js
    .pipe($.uglify())
    // // copy result to build folder
    .pipe(gulp.dest(coffee_build))
    // notify when task completed
    .pipe($.notify({message: 'Coffee compilation completed !', onLast: true}));
});

// INSTALL VENDORS WITH BROWSERIFY
// ---------------------------------------------------------
gulp.task('jsVendors', function() {
  return gulp.src(coffee_dev + '/vendors.js')
    // require node packages
    .pipe($.browserify({
      insertGlobals: true
    }))
    // minify js
    .pipe($.uglify())
    // concat all files
    .pipe($.concat('vendors.js'))
    // rename to .min
    .pipe($.rename('vendors.min.js'))
    // copy result to build folder
    .pipe(gulp.dest(coffee_build))
    // notify when task completed
    .pipe($.notify({message: 'Js vendors compilation completed !', onLast: true}));
});

// FONTS
// ---------------------------------------------------------
gulp.task('fonts', function() {
  return gulp.src(fonts_dev + '/**/*.{eot,svg,ttf,woff,woff2}')
    // remove under-folder
    .pipe($.rename({dirname: ''}))
    // copy result to build folder
    .pipe(gulp.dest(fonts_build))
    // notify when task completed
    .pipe($.notify({message: 'Fonts compilation completed !', onLast: true}));
});

// REMOVE UNUSED CSS
// ---------------------------------------------------------
gulp.task('uncss', function () {
  return gulp.src(sass_build + '/*.min.css')
  // remove unused css
   .pipe($.uncss({
      html: [build + '/**/*.html']
   }))
   // minify css
   .pipe($.cleanCss())
   // copy result to build folder
   .pipe(gulp.dest(sass_build))
   // notify when task completed
   .pipe($.notify({message: 'Css are optimized !', onLast: true}));
});

// MINIFY IMAGES
// ---------------------------------------------------------
gulp.task('img', function () {
  return gulp.src(img_dev + '/**/*.{png,jpg,jpeg,gif,svg,ico}')
    // minify images
    .pipe($.imagemin())
    // copy result to build folder
    .pipe(gulp.dest(img_build))
    // notify when task completed
    .pipe($.notify({message: 'Image are optimized !', onLast: true}));
});

// RELOAD
// ---------------------------------------------------------

///// RELOAD SLIM
gulp.task('reload-slim', ['slim'], function(){
  $.browserSync.reload();
});

///// RELOAD SASS
gulp.task('reload-sass', ['sass'], function(){
  $.browserSync.reload();
});

///// RELOAD COFFEE
gulp.task('reload-coffee', ['coffee'], function(){
  $.browserSync.reload();
});

////////////////////
// COMMANDS
////////////////////////////////////////////////////////////////////////////////

// TASK CLEAN ($ gulp clean)
// ---------------------------------------------------------
gulp.task('clean', function () {
  return gulp.src(build, {read: false})
    // remove folder build
    .pipe($.rimraf())
    // notify when task completed
    .pipe($.notify('Prod folder deleted !'));
});

// TASK DEV ($ gulp dev)
// ---------------------------------------------------------
gulp.task('dev', ['slim', 'sass', 'cssVendors', 'coffee', 'jsVendors', 'fonts', 'img']);

// TASK BUILD ($ gulp build)
// ---------------------------------------------------------
gulp.task('build', ['dev'], function(){
  gulp.start(['uncss']);
});

// RUN SERVER ($ gulp)
// ---------------------------------------------------------

///// WATCH
gulp.task('watch', ['dev'], function () {
  $.browserSync.init({
    port: 3000,
    server: {
      baseDir: build,
      index: "index.html"
    },
    online: true,
    scrollProportionally: true,
    notify: false,
    snippetOptions: {
      rule: {
        match: /<\/head>/i,
        fn: function (snippet, match) {
          return snippet + match;
        }
      }
    }
  })
  gulp.watch(dev + '/**/*.slim', ['reload-slim']);
  gulp.watch(dev + '/**/*.sass', ['reload-sass']);
  gulp.watch(dev + '/**/*.coffee', ['reload-coffee']);
});

////// COMMAND
gulp.task('default', ['clean'], function(){
  gulp.start(['watch']);
});<commit_msg>UP [gulpfile, slim task] Slim task now ignore _partials files<commit_after>// GULP TEMPLATE - Gulfile.js - Victor Allegret
//
//   - $ gulp
//   - $ gulp build
//   - $ gulp clean
//
// --------------------------------------------------------

////////////////////
// VARIABLES
////////////////////////////////////////////////////////////////////////////////

// REQUIRE
// ---------------------------------------------------------
var gulp    = require('gulp'),
    // require every plugins
    $ = require('gulp-load-plugins')({
        pattern: '*'
    })

// PATH
// ---------------------------------------------------------

///// PATHS FOR DEV
var slim_dev    = './dev/views/',
    sass_dev    = './dev/assets/stylesheets/',
    coffee_dev  = './dev/assets/javascripts/',
    fonts_dev   = './dev/assets/fonts/',
    img_dev     = './dev/assets/images/',
    dev         = './dev';

///// PATH FOR PROD
var slim_build     = './build/views/',
    sass_build     = './build/assets/stylesheets/',
    coffee_build   = './build/assets/javascripts/',
    fonts_build    = './build/assets/fonts/',
    img_build      = './build/assets/images/',
    build          = './build';

////////////////////
// TASKS
////////////////////////////////////////////////////////////////////////////////

// COMPILE SLIM TO HTML
// ---------------------------------------------------------
gulp.task('slim', function () {
  return gulp.src(slim_dev + '/**/*.slim')
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin
      })(err);
    }}))
    // Keep non-updated files infos in cache
    .pipe($.cached(slim_dev + '/**/*.slim'))
    // Ignore partials
    .pipe($.filter(function (file) {
        return !/\/_/.test(file.path) && !/^_/.test(file.relative);
    }))
    // compile slim to html
    .pipe($.slim({
      pretty: false,
      include: true
    }))
    // remove all folder
    .pipe($.rename({dirname: ''}))
    // copy result to build folder
    .pipe(gulp.dest(build))
    // notify when task completed
    .pipe($.notify({message: 'Slim compilation completed !', onLast: true}));
});

// COMPILE SASS TO CSS
// ---------------------------------------------------------
gulp.task('sass', function () {
  return gulp.src([sass_dev + '/**/*.sass', '!./dev/assets/stylesheets/vendors/**/*.{css,scss,sass}'])
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin,
      })(err);
    }}))
    // init sourcemaps
    .pipe($.sourcemaps.init())
    // add sass glob import
    .pipe($.sassGlob())
    // compile sass to css
    .pipe($.sass())
    // add auto-prefixes
    .pipe($.autoprefixer({
      browsers: ['last 2 versions'],
      cascade: false
    }))
    // concat all files
    .pipe($.concat('main.css'))
    // rename to .min
    .pipe($.rename('main.min.css'))
    // write sourcemaps
    .pipe($.sourcemaps.write())
    // copy result to build folder
    .pipe(gulp.dest(sass_build))
    // notify when task completed
    .pipe($.notify({message: 'Sass compilation completed !', onLast: true}));
});

// CONCAT CSS VENDORS
// ---------------------------------------------------------
gulp.task('cssVendors', function () {
  return gulp.src(sass_dev + '/vendors/**/*.{css,scss,sass}')
    // compile sass to css
    .pipe($.sass())
    // concat all files
    .pipe($.concat('vendors.css'))
    // rename to .min
    .pipe($.rename('vendors.min.css'))
    // copy result to build folder
    .pipe(gulp.dest(sass_build))
    // notify when task completed
    .pipe($.notify({message: 'Css vendors compilation completed !', onLast: true}));
});

// COMPILE COFFEE TO JS
// ---------------------------------------------------------
gulp.task('coffee', function() {
  return gulp.src(coffee_dev + '/main.coffee')
    // prevent server from crashing
    .pipe($.plumber({ errorHandler: function(err) {
      $.notify.onError({
          title: "Gulp error in " + err.plugin,
      })(err);
    }}))
    // add include for coffee
    .pipe($.include({ extensions: "coffee" }))
    // compile coffee to js
    .pipe($.coffee())
    // concat all files
    .pipe($.concat('main.js'))
    // rename to .min
    .pipe($.rename('main.min.js'))
    // minify js
    .pipe($.uglify())
    // // copy result to build folder
    .pipe(gulp.dest(coffee_build))
    // notify when task completed
    .pipe($.notify({message: 'Coffee compilation completed !', onLast: true}));
});

// INSTALL VENDORS WITH BROWSERIFY
// ---------------------------------------------------------
gulp.task('jsVendors', function() {
  return gulp.src(coffee_dev + '/vendors.js')
    // require node packages
    .pipe($.browserify({
      insertGlobals: true
    }))
    // minify js
    .pipe($.uglify())
    // concat all files
    .pipe($.concat('vendors.js'))
    // rename to .min
    .pipe($.rename('vendors.min.js'))
    // copy result to build folder
    .pipe(gulp.dest(coffee_build))
    // notify when task completed
    .pipe($.notify({message: 'Js vendors compilation completed !', onLast: true}));
});

// FONTS
// ---------------------------------------------------------
gulp.task('fonts', function() {
  return gulp.src(fonts_dev + '/**/*.{eot,svg,ttf,woff,woff2}')
    // remove under-folder
    .pipe($.rename({dirname: ''}))
    // copy result to build folder
    .pipe(gulp.dest(fonts_build))
    // notify when task completed
    .pipe($.notify({message: 'Fonts compilation completed !', onLast: true}));
});

// REMOVE UNUSED CSS
// ---------------------------------------------------------
gulp.task('uncss', function () {
  return gulp.src(sass_build + '/*.min.css')
  // remove unused css
   .pipe($.uncss({
      html: [build + '/**/*.html']
   }))
   // minify css
   .pipe($.cleanCss())
   // copy result to build folder
   .pipe(gulp.dest(sass_build))
   // notify when task completed
   .pipe($.notify({message: 'Css are optimized !', onLast: true}));
});

// MINIFY IMAGES
// ---------------------------------------------------------
gulp.task('img', function () {
  return gulp.src(img_dev + '/**/*.{png,jpg,jpeg,gif,svg,ico}')
    // minify images
    .pipe($.imagemin())
    // copy result to build folder
    .pipe(gulp.dest(img_build))
    // notify when task completed
    .pipe($.notify({message: 'Image are optimized !', onLast: true}));
});

// RELOAD
// ---------------------------------------------------------

///// RELOAD SLIM
gulp.task('reload-slim', ['slim'], function(){
  $.browserSync.reload();
});

///// RELOAD SASS
gulp.task('reload-sass', ['sass'], function(){
  $.browserSync.reload();
});

///// RELOAD COFFEE
gulp.task('reload-coffee', ['coffee'], function(){
  $.browserSync.reload();
});

////////////////////
// COMMANDS
////////////////////////////////////////////////////////////////////////////////

// TASK CLEAN ($ gulp clean)
// ---------------------------------------------------------
gulp.task('clean', function () {
  return gulp.src(build, {read: false})
    // remove folder build
    .pipe($.rimraf())
    // notify when task completed
    .pipe($.notify('Prod folder deleted !'));
});

// TASK DEV ($ gulp dev)
// ---------------------------------------------------------
gulp.task('dev', ['slim', 'sass', 'cssVendors', 'coffee', 'jsVendors', 'fonts', 'img']);

// TASK BUILD ($ gulp build)
// ---------------------------------------------------------
gulp.task('build', ['dev'], function(){
  gulp.start(['uncss']);
});

// RUN SERVER ($ gulp)
// ---------------------------------------------------------

///// WATCH
gulp.task('watch', ['dev'], function () {
  $.browserSync.init({
    port: 3000,
    server: {
      baseDir: build,
      index: "index.html"
    },
    online: true,
    scrollProportionally: true,
    notify: false,
    snippetOptions: {
      rule: {
        match: /<\/head>/i,
        fn: function (snippet, match) {
          return snippet + match;
        }
      }
    }
  })
  gulp.watch(dev + '/**/*.slim', ['reload-slim']);
  gulp.watch(dev + '/**/*.sass', ['reload-sass']);
  gulp.watch(dev + '/**/*.coffee', ['reload-coffee']);
});

////// COMMAND
gulp.task('default', ['clean'], function(){
  gulp.start(['watch']);
});<|endoftext|>


<commit_before>/// <reference lib="webworker" />
/* eslint-disable no-restricted-globals */

// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.

import { clientsClaim } from "workbox-core"
import { ExpirationPlugin } from "workbox-expiration"
import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching"
import { registerRoute } from "workbox-routing"
import { StaleWhileRevalidate } from "workbox-strategies"

clientsClaim()

// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST)

// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$")
registerRoute(
  // Return false to exempt requests from being fulfilled by index.html.
  ({ request, url }) => {
    // If this isn't a navigation, skip.
    if (request.mode !== "navigate") {
      return false
    }

    // If this is a URL that starts with /_, skip.
    if (url.pathname.startsWith("/_")) {
      return false
    }

    // If this looks like a URL for a resource, because it contains
    // a file extension, skip.
    if (url.pathname.match(fileExtensionRegexp)) {
      return false
    }

    // Return true to signal that we want to use the handler.
    return true
  },
  createHandlerBoundToURL("/index.html"),
)

// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
  // Add in any other file extensions or routing criteria as needed.
  ({ url }) =>
    url.origin === self.location.origin && url.pathname.endsWith(".png"),
  // Customize this strategy as needed, e.g., by changing to CacheFirst.
  new StaleWhileRevalidate({
    cacheName: "images",
    plugins: [
      // Ensure that once this runtime cache reaches a maximum size the
      // least-recently used images are removed.
      new ExpirationPlugin({ maxEntries: 50 }),
    ],
  }),
)

// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener("message", (event) => {
  if (event.data && event.data.type === "SKIP_WAITING") {
    self.skipWaiting()
  }
})
<commit_msg>fix(new-client): exclude storybook and styleguide from service worker routing<commit_after>/// <reference lib="webworker" />
/* eslint-disable no-restricted-globals */

// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.

import { clientsClaim } from "workbox-core"
import { ExpirationPlugin } from "workbox-expiration"
import { precacheAndRoute, createHandlerBoundToURL } from "workbox-precaching"
import { registerRoute } from "workbox-routing"
import { StaleWhileRevalidate } from "workbox-strategies"

clientsClaim()

// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST)

// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp("/[^/?]+\\.[^/]+$")
registerRoute(
  // Return false to exempt requests from being fulfilled by index.html.
  ({ request, url }) => {
    // If this isn't a navigation, skip.
    if (request.mode !== "navigate") {
      return false
    }

    if (url.pathname.startsWith("/storybook")) {
      return false
    }

    if (url.pathname.startsWith("/styleguide")) {
      return false
    }

    // If this is a URL that starts with /_, skip.
    if (url.pathname.startsWith("/_")) {
      return false
    }

    // If this looks like a URL for a resource, because it contains
    // a file extension, skip.
    if (url.pathname.match(fileExtensionRegexp)) {
      return false
    }

    // Return true to signal that we want to use the handler.
    return true
  },
  createHandlerBoundToURL("/index.html"),
)

// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
  // Add in any other file extensions or routing criteria as needed.
  ({ url }) =>
    url.origin === self.location.origin && url.pathname.endsWith(".png"),
  // Customize this strategy as needed, e.g., by changing to CacheFirst.
  new StaleWhileRevalidate({
    cacheName: "images",
    plugins: [
      // Ensure that once this runtime cache reaches a maximum size the
      // least-recently used images are removed.
      new ExpirationPlugin({ maxEntries: 50 }),
    ],
  }),
)

// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener("message", (event) => {
  if (event.data && event.data.type === "SKIP_WAITING") {
    self.skipWaiting()
  }
})
<|endoftext|>