<|start_filename|>index.js<|end_filename|>
/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2011-2015 Webcomm Pty Ltd
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

module.exports = function (config, callback) {

  // Load modules
  var gulp = require('gulp');
  var browserSync = require('browser-sync');
  var del = require('del');
  var mergeStream = require('merge-stream');
  var runSequence = require('run-sequence');
  var tempWrite = require('temp-write');
  var _ = require('underscore');

  // Gulp plugins
  var $ = require('gulp-load-plugins')();

  // Prepare configuration (defaults plus user overrides).
  // See config.example.json for documentation.
  config = _.extend({
    production: false,
    sites: []
  }, config);

  // A list of all available components, mapping any additional assets required.
  // It's a simple way of ensuring only required assets are loaded.
  var availableComponents = {
    'grid':             { javascripts: ['interchange'] },
    'accordion':        { javascripts: ['accordion'] },
    'alert-boxes':      { javascripts: ['alert'] },
    'block-grid':       { javascripts: [] },
    'breadcrumbs':      { javascripts: [] },
    'button-groups':    { javascripts: [] },
    'buttons':          { javascripts: [] },
    'clearing':         { javascripts: ['clearing'] },
    'dropdown':         { javascripts: ['dropdown'] },
    'dropdown-buttons': { javascripts: [] },
    'equalizer':        { javascripts: ['equalizer'] },
    'flex-video':       { javascripts: [] },
    'forms':            { javascripts: ['abide'] },
    'icon-bar':         { javascripts: [] },
    'inline-lists':     { javascripts: [] },
    'joyride':          { javascripts: ['joyride'] },
    'keystrokes':       { javascripts: [] },
    'labels':           { javascripts: [] },
    'magellan':         { javascripts: ['magellan'] },
    'orbit':            { javascripts: ['orbit'] },
    'pagination':       { javascripts: [] },
    'panels':           { javascripts: [] },
    'pricing-tables':   { javascripts: [] },
    'progress-bars':    { javascripts: [] },
    'range-slider':     { javascripts: ['slider'] },
    'reveal':           { javascripts: ['reveal'] },
    'side-nav':         { javascripts: [] },
    'split-buttons':    { javascripts: ['dropdown'] },
    'sub-nav':          { javascripts: [] },
    'switches':         { javascripts: [] },
    'tables':           { javascripts: [] },
    'tabs':             { javascripts: ['tab'] },
    'thumbs':           { javascripts: [] },
    'tooltips':         { javascripts: ['tooltip'] },
    'top-bar':          { javascripts: ['topbar'] },
    'type':             { javascripts: [] },
    'offcanvas':        { javascripts: ['offcanvas'] },
    'visibility':       { javascripts: [] },
  };

  // Components which are required for the boilerplate and must be included at all times
  var requiredComponents = [
    'grid',
    'alert-boxes',
    'breadcrumbs',
    'equalizer',
    'forms',
    'inline-lists',
    'pagination',
    'tables',
    'top-bar',
    'type',
    'visibility'
  ];

  function templatePath(site) {
    return 'app/design/frontend/'+site.package+'/'+site.theme;
  }

  function skinPath(site) {
    return 'skin/frontend/'+site.package+'/'+site.theme;
  }

  // Compile stylesheets for every site
  gulp.task('stylesheets', function () {
    var streams = [];

    _.each(config.sites, function (site) {
      var site = config.sites[0];

      // We're going to create a temporary file for each site, which is a valid
      // SASS stylesheet that defines variables. We'll then prepend this to
      // our SASS compilation so that variables defined in a configuration
      // file can lead to conditional inclusion of components.
      var sassVariables = [];

      // We begin by declaring all of our included components (both required and user-defined)
      _.each(requiredComponents.slice(0).concat(site.components), function (component) {
        sassVariables.push('$include-'+component+'-component: true !default;');
      });

      // We then declare all components are not included, but because of the "!default" flag,
      // these variables are only recognised if they weren't previously defined. This
      // means that we're effectively making sure all the variables are defined
      // so that the SASS file does not error out (see magento-boilerplate.scss)
      _.each(availableComponents, function (noop, component) {
        sassVariables.push('$include-'+component+'-component: false !default;');
      });

      // Create an array of stylesheets
      var stylesheets = [
        tempWrite.sync(sassVariables.join('\n')) // Temporary file made up of concatenated variables
      ];

      // Attach any site-specific stylesheets
      _.each(site.compilation.stylesheets, function (stylesheet) {
        stylesheets.push(stylesheet);
      });

      // Finally, attach the stylesheet in the skin path
      stylesheets.push(skinPath(site)+'/assets/stylesheets/styles.scss');

      // Create an array of default include paths
      var includePaths = [
        'node_modules/magento-boilerplate/node_modules/foundation-sites/scss',
        'node_modules/magento-boilerplate/node_modules/font-awesome/scss',
        'node_modules/magento-boilerplate/assets/stylesheets'
      ];

      // Attach any site-specific include paths
      _.each(site.compilation.includePaths, function (includePath) {
        includePaths.push(includePath);
      });

      // Finally, include the theme's directory in the include paths
      includePaths.push(skinPath(site)+'/assets/stylesheets');

      streams.push(
        gulp
          .src(_.uniq(stylesheets))
          .pipe($.concat('styles.scss'))
          .pipe($.sass({
            outputStyle: config.production ? 'compressed' : 'nested',
            sourceComments: !config.production,
            includePaths: _.uniq(includePaths)
          }))
          .on('error', $.notify.onError())
          .pipe($.autoprefixer('last 2 versions'))
          .pipe(gulp.dest(skinPath(site)+'/css'))
          .pipe(browserSync.stream())
          .pipe($.notify('Compiled Stylesheets.'))
      );
    });

    return mergeStream(streams);
  });

  // JavaScripts
  gulp.task('javascripts', function () {
    var streams = [];

    _.each(config.sites, function (site) {
      var site = config.sites[0];

      // By default, we will load jQuery and the core Foundation library
      var javascripts = [
        'node_modules/magento-boilerplate/node_modules/foundation-sites/js/vendor/jquery.js',
        'node_modules/magento-boilerplate/assets/javascripts/no-conflict.js',
        'node_modules/magento-boilerplate/node_modules/foundation-sites/js/foundation/foundation.js'
      ];

      // Now we will loop through all required components as well as user-defined
      // components to create a list of JavaScripts that we need to include.
      _.each(requiredComponents.slice(0).concat(site.components), function (component) {
        _.each(availableComponents[component].javascripts, function (javascript) {
          javascripts.push('node_modules/magento-boilerplate/node_modules/foundation-sites/js/foundation/foundation.'+javascript+'.js');
        });
      });

      // Now, include any site-specific javascripts
      _.each(site.compilation.javascripts, function (javascript) {
        javascripts.push(javascript);
      });

      // Finally, we'll compile all JavaScripts located in the skin path for the site
      javascripts.push('node_modules/magento-boilerplate/assets/javascripts/magento-boilerplate.js');
      javascripts.push(skinPath(site)+'/assets/javascripts/**/*.js');

      streams.push(
        gulp
          .src(_.uniq(javascripts)) // Avoid duplicates
          .pipe($.concat('scripts.js'))
          .pipe($.if(config.production, $.uglify()))
          .pipe(gulp.dest(skinPath(site)+'/js'))
          .pipe(browserSync.stream())
          .pipe($.notify('Compiled JavaScripts.'))
        );
    });

    return mergeStream(streams);
  });

  // Modernizr is a little different becuase it sits in the head of Magento rather
  // than at the end of hte page
  gulp.task('modernizr', function () {
    var streams = [];

    _.each(config.sites, function (site) {

      streams.push(
        gulp
          .src('node_modules/magento-boilerplate/node_modules/foundation-sites/js/vendor/modernizr.js')
          .pipe($.if(config.production, $.uglify()))
          .pipe(gulp.dest(skinPath(site)+'/js'))
          .pipe(browserSync.stream())
          .pipe($.notify('Compiled Modernizr.'))
      );
    });

    return mergeStream(streams);
  });

  // Images
  gulp.task('images', function () {
    var streams = [];

    _.each(config.sites, function (site) {

      // Build array of images
      var images = [
        'node_modules/magento-boilerplate/assets/images/**/*'
      ];

      // Attach site-specific images
      _.each(site.compilation.images, function (image) {
        images.push(image);
      });

      // Add skin images
      images.push(skinPath(site)+'/assets/images/**/*');

      streams.push(
        gulp
          .src(images)
          .pipe(gulp.dest(skinPath(site)+'/images'))
          .pipe(browserSync.stream({
            once: true
          }))
      );
    });

    return mergeStream(streams);
  });

  gulp.task('fonts', function () {
    var streams = [];

    _.each(config.sites, function (site) {

      var fonts = [
        'node_modules/magento-boilerplate/node_modules/font-awesome/fonts/*.{eot,otf,svg,ttf,woff,woff2}',
        skinPath(site)+'/assets/fonts/*.{eot,otf,svg,ttf,woff,woff2}'
      ];

      streams.push(
        gulp
          .src(fonts)
          .pipe(gulp.dest(skinPath(site)+'/fonts'))
          .pipe(browserSync.stream())
      );
    });

    return mergeStream(streams);
  });

  gulp.task('manifest', function () {
    var streams = [];

    // Because the manifest task actually produces a unique stylesheet and
    // JavaScript file every change, BrowserSync doesn't detect a change
    // with an existing file in the DOM, and therefore won't reload
    // the page or reinject stylesheets, defeating the whole
    // purpose of BrowserSync even being integreated.
    // We'll only generate a manifest on a
    // production environment, which is
    // fine becuase that's the only
    // place it's needed.
    if (config.production) {
      _.each(config.sites, function (site) {
        streams.push(
          gulp
            .src([
              skinPath(site)+'/css/styles.css',
              skinPath(site)+'/js/scripts.js'
            ], {
              base: skinPath(site)
            })
            .pipe($.rev())
            .pipe(gulp.dest(skinPath(site)))
            .pipe($.rev.manifest())
            .pipe(gulp.dest(skinPath(site)))
        );
      });
    }

    return mergeStream(streams);
  });

  // Clean out compiled files synchronously (so errors are not raised in other tasks)
  gulp.task('clean', function () {
    _.each(config.sites, function (site) {
      del.sync([
        skinPath(site)+'/css',
        skinPath(site)+'/fonts',
        skinPath(site)+'/images',
        skinPath(site)+'/js',
        skinPath(site)+'/rev-manifest.json'
      ]);
    });
  });

  // Serve the website with live reloading
  gulp.task('serve', function() {
    _.each(config.sites, function (site) {
      browserSync(site.server);
    });
  });

  // Custom tasks
  gulp.task('custom', function () {
    if (typeof callback === 'function') {
      return callback.apply(this, [gulp]);
    }
  });

  // Our default task will clean and build
  gulp.task('default', ['clean'], function () {
    runSequence(
      ['stylesheets', 'javascripts', 'modernizr', 'images', 'fonts'],
      'manifest',
      'custom'
    );
  });

  // Watching assets will start a build in web server that will
  // automatically synchronise browsers and reload assets
  gulp.task('watch', ['serve'], function () {
    _.each(config.sites, function (site) {

      var stylesheets = [skinPath(site)+'/assets/stylesheets/**/*.scss'];
      _.each(site.watch.stylesheets, function (stylesheet) {
        stylesheets.push(stylesheet);
      });
      gulp.watch(stylesheets, ['stylesheets']);

      var javascripts = [skinPath(site)+'/assets/javascripts/**/*.js'];
      _.each(site.watch.javascripts, function (javascript) {
        javascripts.push(javascript);
      });
      gulp.watch(javascripts, ['javascripts', 'modernizr']);

      var images = [skinPath(site)+'/assets/images/**/*'];
      _.each(site.watch.images, function (image) {
        images.push(image);
      });
      gulp.watch(images, ['images']);

      var others = [templatePath(site)+'/**/*'];
      _.each(site.watch.others, function (other) {
        others.push(other);
      });
      gulp.watch(others, function () {
        browserSync.reload(); // @todo find the new, proper API
      });
    });
  });
};


<|start_filename|>config.example.json<|end_filename|>
{
  // Whether we are in production mode or not
  "production": false,

  // Each frontend separation of Magento is just considered
  // a site within the context of the Boilerplate
  "sites": [
    {
      // The package and theme respectively represent the
      // location of both template files and skin files
      "package": "boilerplate",
      "theme": "default",

      // Below you may specify any components that you would like to include
      // with your site. This will ensure that the required JavaScripts and
      // stylesheets are available. Only choose the ones you need to
      // include, and no more. There are a number of required
      // components which, whether you include or not,
      // will be compiled.
      //
      // Available:
      //     accordion, block-grid, button-groups, buttons, clearing, dropdown, dropdown-buttons, flex-video, icon-bar, joyride, keystrokes, labels, magellan, orbit, panels, pricing-tables, progress-bars, range-slider, reveal, side-nav, split-buttons, sub-nav, switches, tabs, thumbs, tooltips, offcanvas.
      //
      // Required:
      //     grid, alert-boxes, breadcrumbs, equalizer, forms, inline-lists, pagination, tables, top-bar, type, visibility
      "components": [],

      // BrowserSync server configuration details. These
      // options are passed directly into BrowserSync.
      "server": {
        "proxy": "magentoboilerplate.dev",
        "port": 3000
      },

      // Here we declare any customisations to the behaviour of compilation that are required, such as including
      // additional Stylesheets, JavaScripts and images to be merged into the produced files, as well as any
      // additional include paths which may assist in the production of clean and concise SCSS.
      "compilation": {
        "stylesheets": [],
        "javascripts": [],
        "images": [],
        "fonts": [],
        "includePaths": []
      },

      // Finally, you may declare any additional paths to watch
      "watch": {
        "stylesheets": [],
        "javascripts": [],
        "images": [],
        "others": []
      }
    }
  ]
}


<|start_filename|>assets/javascripts/magento-boilerplate.js<|end_filename|>
jQuery(document).ready(function ($) {

  function syncValidationLabels() {
    var $validated = $('.input-text');

    // Only watch validation items if required
    if (!$validated.length) return;

    // As to save on DOM rendering, we will grab and cache some things
    $validated.each(function () {
      $(this).data('label', $(this).parent().prev('label'));
    });

    // Every second, we'll check and update wrapper classes for input elements
    setInterval(function () {
      $validated.each(function () {
        $(this).data('label')[$(this).hasClass('validation-failed') ? 'addClass' : 'removeClass']('error');
      });
    }, 1000);
  }

  function equaliseProductListings() {

    // Add equalizer attributs to all product in listings
    $('.products-grid').attr('data-equalizer', '');
    $('.products-grid .item').attr('data-equalizer-watch', '');
  }

  // Prepare some supporting JavaScript
  syncValidationLabels();
  equaliseProductListings();

  // Kickstart Foundation (must be after we prepare product listings)
  $(document).foundation({
    equalizer: {
      equalize_on_stack: true
    }
  });
});


<|start_filename|>package.json<|end_filename|>
{
  "name": "magento-boilerplate",
  "version": "3.0.22",
  "description": "A boilerplate project for creating beautiful sites using Magento, powered by Foundation 5.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/webcomm/magento-boilerplate.git"
  },
  "author": "Webcomm <<EMAIL>> (http://webcomm.io)",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/webcomm/magento-boilerplate/issues"
  },
  "homepage": "https://github.com/webcomm/magento-boilerplate",
  "dependencies": {
    "browser-sync": "^2.6.0",
    "del": "^1.1.1",
    "font-awesome": "^4.4.0",
    "foundation-sites": "^5.5.2",
    "gulp-autoprefixer": "^2.1.0",
    "gulp-concat": "^2.5.2",
    "gulp-if": "^1.2.5",
    "gulp-load-plugins": "^0.8.1",
    "gulp-notify": "^2.2.0",
    "gulp-rev": "^5.0.1",
    "gulp-sass": "^1.3.3",
    "gulp-uglify": "^1.1.0",
    "merge-stream": "^0.1.8",
    "run-sequence": "^1.1.1",
    "temp-write": "^1.1.0",
    "underscore": "^1.8.2"
  }
}
