<commit_before>/**
 *
 *  Material Design Lite
 *  Copyright 2015 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var path = require('path');
var pkg = require('./package.json');
var through = require('through2');
var swig = require('swig');
var banner = ['/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' * @version v<%= pkg.version %>',
  ' * @link <%= pkg.homepage %>',
  ' * @license <%= pkg.license.type %>',
  ' */',
  ''].join('\n');

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// ***** Development tasks ****** //

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('src/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// ***** Production build tasks ****** //

// Optimize Images
// TODO: Update image paths in final CSS to match root/images
gulp.task('images', function () {
  return gulp.src('src/**/*.{svg,png,jpg}')
    .pipe($.flatten())
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Compile and Automatically Prefix Stylesheets (dev)
gulp.task('styles:dev', function () {
  return gulp.src([
    'src/**/*.scss'
  ])
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/styles'))
    .pipe($.size({title: 'styles'}));
});

// Compile and Automatically Prefix Stylesheet Templates (production)
gulp.task('styletemplates', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'src/template.scss'
  ])
    // Generate Source Maps
    .pipe ($.sourcemaps.init())
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp'))
    // Concatenate Styles
    .pipe($.concat('material.css.template'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Styles
    .pipe($.if('*.css.template', $.csso()))
    .pipe($.concat('material.min.css.template'))
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'styles'}));
});

// Compile and Automatically Prefix Stylesheets (production)
gulp.task('styles', ['styletemplates'], function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'src/styleguide.scss'
  ])
    // Generate Source Maps
    .pipe ($.sourcemaps.init())
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp'))
    // Concatenate Styles
    .pipe($.concat('material.css'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe($.concat('material.min.css'))
    //.pipe($.header(banner, {pkg: pkg}))
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'styles'}));
});

// Concatenate And Minify JavaScript
gulp.task('scripts', function () {
  var sources = [
    // Component handler
    'src/mdlComponentHandler.js',
    // Polyfills/dependencies
    'src/third_party/**/*.js',
    // Base components
    'src/button/button.js',
    'src/checkbox/checkbox.js',
    'src/icon-toggle/icon-toggle.js',
    'src/menu/menu.js',
    'src/progress/progress.js',
    'src/radio/radio.js',
    'src/slider/slider.js',
    'src/spinner/spinner.js',
    'src/switch/switch.js',
    'src/tabs/tabs.js',
    'src/textfield/textfield.js',
    'src/tooltip/tooltip.js',
    // Complex components (which reuse base components)
    'src/layout/layout.js',
    'src/data-table/data-table.js',
    // And finally, the ripples
    'src/ripple/ripple.js'
  ];
  return gulp.src(sources)
    .pipe($.sourcemaps.init())
    // Concatenate Scripts
    .pipe($.concat('material.js'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Scripts
    .pipe($.uglify({preserveComments: 'some', sourceRoot: '.',
        sourceMapIncludeSources: true}))
    .pipe($.concat('material.min.js'))
    // Write Source Maps
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'scripts'}));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['dist'], {dot: true}));

// Build Production Files, the Default Task
gulp.task('default', ['clean', 'mocha'], function (cb) {
  runSequence(
    'styles',
    ['jshint', 'scripts', 'styles', 'assets', 'pages', 'demos', 'templates',
     'images'],
    cb);
});

// ***** Testing tasks ***** //

gulp.task('mocha', ['styles'], function () {
  return gulp.src('./test/index.html')
    .pipe($.mochaPhantomjs({reporter: 'list'}));
});

gulp.task('test', ['jshint', 'mocha']);

gulp.task('test:visual', function() {
  browserSync({
    notify: false,
    server: './',
    startPath: 'test/visual/index.html'
  });

  gulp.watch(['test/visual/**'], reload);
});

// ***** Landing page tasks ***** //

/**
 * Site metadata for use with templates.
 * @type {Object}
 */
var site = {};

/**
 * Generates an HTML file based on a template and file metadata.
 */
function applyTemplate() {
  return through.obj(function(file, enc, cb) {
    var data = {
      site: site,
      page: file.page,
      content: file.contents.toString()
    };

    var templateFile = path.join(
        __dirname, 'docs', '_templates', file.page.layout + '.html');
    var tpl = swig.compileFile(templateFile, {cache: false});
    file.contents = new Buffer(tpl(data), 'utf8');
    this.push(file);
    cb();
  });
}

/**
 * Generates an index.html file for each README in MDL/src directory.
 */
gulp.task('components', function() {
  return gulp.src(['./src/**/README.md'], {base: './src'})
    // Add basic front matter.
    .pipe($.header('---\nlayout: component\nbodyclass: components\ninclude_prefix: ../../\n---\n\n'))
    .pipe($.frontMatter({property: 'page', remove: true}))
    .pipe($.marked())
    .pipe((function () {
      var componentPages = [];
      return through.obj(function(file, enc, cb) {
        file.page.component = file.relative.split('/')[0];
        componentPages.push(file.page);
        this.push(file);
        cb();
      },
      function(cb) {
        site.components = componentPages;
        cb();
      });
    })())
    .pipe(applyTemplate())
    .pipe($.rename(function (path) {
      path.basename = 'index';
    }))
    .pipe(gulp.dest('dist/components'));
});

/**
 * Copies demo files from MDL/src directory.
 */
gulp.task('demos', function () {
  return gulp.src([
      './src/**/*.css',
      './src/**/demo.*',
      './src/**/*.js'
    ], {base: './src'})
      .pipe($.if('*.scss', $.sass({
        precision: 10,
        onError: console.error.bind(console, 'Sass error:')
      })))
      .pipe($.cssInlineImages({
        webRoot: 'src'
      }))
      .pipe($.if('*.css', $.autoprefixer(AUTOPREFIXER_BROWSERS)))
      .pipe(gulp.dest('dist/components'));
});

/**
 * Generates an HTML file for each md file in _pages directory.
 */
gulp.task('pages', ['components'], function() {
  return gulp.src(['docs/_pages/*.md'])
    .pipe($.frontMatter({property: 'page', remove: true}))
    .pipe($.marked())
    .pipe(applyTemplate())
    .pipe($.rename(function(path) {
      if (path.basename !== 'index') {
        path.dirname = path.basename;
        path.basename = 'index';
      }
    }))
    .pipe(gulp.dest('dist'));
});

/**
 * Copies assets from MDL and _assets directory.
 */
gulp.task('assets', function () {
  return gulp.src(['docs/_assets/**'])
    .pipe($.if(/\.(svg|jpg|png)$/i, $.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/assets'));
});

/**
 * Serves the landing page from "out" directory.
 */
gulp.task('serve', ['default'], function () {
  browserSync({
    notify: false,
    server: {
      baseDir: ['dist']
    }
  });

  gulp.watch(['src/**/*.js', '!src/**/README.md'],
      ['scripts', 'demos', 'components', reload]);
  gulp.watch(['src/**/*.{scss,css}'], ['styles', 'demos', reload]);
  gulp.watch(['src/**/*.html'], ['demos', reload]);
  gulp.watch(['src/**/README.md'], ['components', reload]);
  gulp.watch(['templates/**/*'], ['templates', reload]);
  gulp.watch(['docs/**/*'], ['pages', 'assets', reload]);
});

gulp.task('publish', ['default', 'templates', 'assets', 'pages', 'demos'],
    function() {
  var push = !!process.env.GH_PUSH;
  if (!push) {
    console.log('Dry run! To push set $GH_PUSH to true');
  }

  return gulp.src('dist/**/*')
  .pipe($.ghPages({
    push: push,
  }));
});

gulp.task('templates:mdl', function() {
  return gulp.src([
    'templates/**/*.scss'
  ])
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe($.csso())
    .pipe($.rename({suffix: '.min'}))
    .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:styles', function() {
  return gulp.src([
    'templates/**/*.css'
  ])
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    // FIXME: This crashes. It's a bug in gulp-csso,
    // not csso itself.
    //.pipe($.csso())
    .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:static', function() {
  return gulp.src([
    'templates/**/*.html',
  ])
  .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:images', function() {
  return gulp.src([
    'templates/*/images/**/*'
  ])
  .pipe($.imagemin({
    progressive: true,
    interlaced: true
  }))
  .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:fonts', function() {
  return gulp.src([
    'src/fonts/**/*'
  ], {base: '.'})
  .pipe(gulp.dest('dist/templates/'));
});

gulp.task('templates', ['templates:static', 'templates:images', 'templates:mdl',
    'templates:styles']);
<commit_msg>Add CSS prefixing to microsite assets (fixes #321)<commit_after>/**
 *
 *  Material Design Lite
 *  Copyright 2015 Google Inc. All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 *
 */

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var path = require('path');
var pkg = require('./package.json');
var through = require('through2');
var swig = require('swig');
var banner = ['/**',
  ' * <%= pkg.name %> - <%= pkg.description %>',
  ' * @version v<%= pkg.version %>',
  ' * @link <%= pkg.homepage %>',
  ' * @license <%= pkg.license.type %>',
  ' */',
  ''].join('\n');

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

// ***** Development tasks ****** //

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src('src/**/*.js')
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// ***** Production build tasks ****** //

// Optimize Images
// TODO: Update image paths in final CSS to match root/images
gulp.task('images', function () {
  return gulp.src('src/**/*.{svg,png,jpg}')
    .pipe($.flatten())
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Compile and Automatically Prefix Stylesheets (dev)
gulp.task('styles:dev', function () {
  return gulp.src([
    'src/**/*.scss'
  ])
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/styles'))
    .pipe($.size({title: 'styles'}));
});

// Compile and Automatically Prefix Stylesheet Templates (production)
gulp.task('styletemplates', function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'src/template.scss'
  ])
    // Generate Source Maps
    .pipe ($.sourcemaps.init())
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp'))
    // Concatenate Styles
    .pipe($.concat('material.css.template'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Styles
    .pipe($.if('*.css.template', $.csso()))
    .pipe($.concat('material.min.css.template'))
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'styles'}));
});

// Compile and Automatically Prefix Stylesheets (production)
gulp.task('styles', ['styletemplates'], function () {
  // For best performance, don't add Sass partials to `gulp.src`
  return gulp.src([
    'src/styleguide.scss'
  ])
    // Generate Source Maps
    .pipe ($.sourcemaps.init())
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp'))
    // Concatenate Styles
    .pipe($.concat('material.css'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Styles
    .pipe($.if('*.css', $.csso()))
    .pipe($.concat('material.min.css'))
    //.pipe($.header(banner, {pkg: pkg}))
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'styles'}));
});

// Concatenate And Minify JavaScript
gulp.task('scripts', function () {
  var sources = [
    // Component handler
    'src/mdlComponentHandler.js',
    // Polyfills/dependencies
    'src/third_party/**/*.js',
    // Base components
    'src/button/button.js',
    'src/checkbox/checkbox.js',
    'src/icon-toggle/icon-toggle.js',
    'src/menu/menu.js',
    'src/progress/progress.js',
    'src/radio/radio.js',
    'src/slider/slider.js',
    'src/spinner/spinner.js',
    'src/switch/switch.js',
    'src/tabs/tabs.js',
    'src/textfield/textfield.js',
    'src/tooltip/tooltip.js',
    // Complex components (which reuse base components)
    'src/layout/layout.js',
    'src/data-table/data-table.js',
    // And finally, the ripples
    'src/ripple/ripple.js'
  ];
  return gulp.src(sources)
    .pipe($.sourcemaps.init())
    // Concatenate Scripts
    .pipe($.concat('material.js'))
    .pipe($.header(banner, {pkg: pkg}))
    .pipe(gulp.dest('./dist'))
    // Minify Scripts
    .pipe($.uglify({preserveComments: 'some', sourceRoot: '.',
        sourceMapIncludeSources: true}))
    .pipe($.concat('material.min.js'))
    // Write Source Maps
    .pipe($.sourcemaps.write('./'))
    .pipe(gulp.dest('./dist'))
    .pipe($.size({title: 'scripts'}));
});

// Clean Output Directory
gulp.task('clean', del.bind(null, ['dist'], {dot: true}));

// Build Production Files, the Default Task
gulp.task('default', ['clean', 'mocha'], function (cb) {
  runSequence(
    'styles',
    ['jshint', 'scripts', 'styles', 'assets', 'pages', 'demos', 'templates',
     'images'],
    cb);
});

// ***** Testing tasks ***** //

gulp.task('mocha', ['styles'], function () {
  return gulp.src('./test/index.html')
    .pipe($.mochaPhantomjs({reporter: 'list'}));
});

gulp.task('test', ['jshint', 'mocha']);

gulp.task('test:visual', function() {
  browserSync({
    notify: false,
    server: './',
    startPath: 'test/visual/index.html'
  });

  gulp.watch(['test/visual/**'], reload);
});

// ***** Landing page tasks ***** //

/**
 * Site metadata for use with templates.
 * @type {Object}
 */
var site = {};

/**
 * Generates an HTML file based on a template and file metadata.
 */
function applyTemplate() {
  return through.obj(function(file, enc, cb) {
    var data = {
      site: site,
      page: file.page,
      content: file.contents.toString()
    };

    var templateFile = path.join(
        __dirname, 'docs', '_templates', file.page.layout + '.html');
    var tpl = swig.compileFile(templateFile, {cache: false});
    file.contents = new Buffer(tpl(data), 'utf8');
    this.push(file);
    cb();
  });
}

/**
 * Generates an index.html file for each README in MDL/src directory.
 */
gulp.task('components', function() {
  return gulp.src(['./src/**/README.md'], {base: './src'})
    // Add basic front matter.
    .pipe($.header('---\nlayout: component\nbodyclass: components\ninclude_prefix: ../../\n---\n\n'))
    .pipe($.frontMatter({property: 'page', remove: true}))
    .pipe($.marked())
    .pipe((function () {
      var componentPages = [];
      return through.obj(function(file, enc, cb) {
        file.page.component = file.relative.split('/')[0];
        componentPages.push(file.page);
        this.push(file);
        cb();
      },
      function(cb) {
        site.components = componentPages;
        cb();
      });
    })())
    .pipe(applyTemplate())
    .pipe($.rename(function (path) {
      path.basename = 'index';
    }))
    .pipe(gulp.dest('dist/components'));
});

/**
 * Copies demo files from MDL/src directory.
 */
gulp.task('demos', function () {
  return gulp.src([
      './src/**/*.css',
      './src/**/demo.*',
      './src/**/*.js'
    ], {base: './src'})
      .pipe($.if('*.scss', $.sass({
        precision: 10,
        onError: console.error.bind(console, 'Sass error:')
      })))
      .pipe($.cssInlineImages({
        webRoot: 'src'
      }))
      .pipe($.if('*.css', $.autoprefixer(AUTOPREFIXER_BROWSERS)))
      .pipe(gulp.dest('dist/components'));
});

/**
 * Generates an HTML file for each md file in _pages directory.
 */
gulp.task('pages', ['components'], function() {
  return gulp.src(['docs/_pages/*.md'])
    .pipe($.frontMatter({property: 'page', remove: true}))
    .pipe($.marked())
    .pipe(applyTemplate())
    .pipe($.rename(function(path) {
      if (path.basename !== 'index') {
        path.dirname = path.basename;
        path.basename = 'index';
      }
    }))
    .pipe(gulp.dest('dist'));
});

/**
 * Copies assets from MDL and _assets directory.
 */
gulp.task('assets', function () {
  return gulp.src(['docs/_assets/**'])
    .pipe($.if(/\.(svg|jpg|png)$/i, $.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe($.if(/\.css/i, $.autoprefixer(AUTOPREFIXER_BROWSERS)))
    .pipe(gulp.dest('dist/assets'));
});

/**
 * Serves the landing page from "out" directory.
 */
gulp.task('serve', ['default'], function () {
  browserSync({
    notify: false,
    server: {
      baseDir: ['dist']
    }
  });

  gulp.watch(['src/**/*.js', '!src/**/README.md'],
      ['scripts', 'demos', 'components', reload]);
  gulp.watch(['src/**/*.{scss,css}'], ['styles', 'demos', reload]);
  gulp.watch(['src/**/*.html'], ['demos', reload]);
  gulp.watch(['src/**/README.md'], ['components', reload]);
  gulp.watch(['templates/**/*'], ['templates', reload]);
  gulp.watch(['docs/**/*'], ['pages', 'assets', reload]);
});

gulp.task('publish', ['default', 'templates', 'assets', 'pages', 'demos'],
    function() {
  var push = !!process.env.GH_PUSH;
  if (!push) {
    console.log('Dry run! To push set $GH_PUSH to true');
  }

  return gulp.src('dist/**/*')
  .pipe($.ghPages({
    push: push,
  }));
});

gulp.task('templates:mdl', function() {
  return gulp.src([
    'templates/**/*.scss'
  ])
    .pipe($.sass({
      precision: 10,
      onError: console.error.bind(console, 'Sass error:')
    }))
    .pipe($.cssInlineImages({
      webRoot: 'src'
    }))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe($.csso())
    .pipe($.rename({suffix: '.min'}))
    .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:styles', function() {
  return gulp.src([
    'templates/**/*.css'
  ])
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    // FIXME: This crashes. It's a bug in gulp-csso,
    // not csso itself.
    //.pipe($.csso())
    .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:static', function() {
  return gulp.src([
    'templates/**/*.html',
  ])
  .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:images', function() {
  return gulp.src([
    'templates/*/images/**/*'
  ])
  .pipe($.imagemin({
    progressive: true,
    interlaced: true
  }))
  .pipe(gulp.dest('dist/templates'));
});

gulp.task('templates:fonts', function() {
  return gulp.src([
    'src/fonts/**/*'
  ], {base: '.'})
  .pipe(gulp.dest('dist/templates/'));
});

gulp.task('templates', ['templates:static', 'templates:images', 'templates:mdl',
    'templates:styles']);
<|endoftext|>


<commit_before>
class Maker {
  constructor() {
    this.ctx = {
      before: [],
      after: [],
      scoped: {},
      local: {},
    }
  }

  build() {
    const scoped = {}
    Object.keys(this.ctx.scoped).forEach(name => {
      scoped[name] = this.ctx.scoped[name].build()
    })
    return Object.assign({}, this.ctx, { scoped })
  }

  before(...list) {
    this.ctx.before = this.ctx.before.concat(list)
  }

  after(...list) {
    this.ctx.after = this.ctx.after.concat(list)
  }

  scope(name, creator) {
    if (name.indexOf('/') !== -1) {
      throw new Error('Name of the scope should be simple string!')
    }

    const scopedCtx = new Maker()
    creator(scopedCtx)
    this.ctx.scoped[name] = scopedCtx
  }

  /**
   * Add HTTP method listeners to local
   * @param {string} method
   * @param {function[]} listeners
   */
  local(method, listeners) {
    // if listeners for that methods exists, simple merge
    if (this.ctx.local[method]) {
      this.ctx.local[method] = this.ctx.local[method].concat(listeners)
    }
    else {
      this.ctx.local[method] = listeners
    }
  }

  /**
   * Handle method creator
   * @param {string} name
   * @param {string} method
   * @param {function[]} listeners
   */
  method(method, listeners) {
    let name = ''
    if (typeof listeners[0] === 'string') {
      name = listeners.splice(0, 1)[0]
    }
    name = name.replace(/^\//gm, '')

    if ((name.match(/\//g) || []).length > 1) {
      throw new Error('Path should not be deep')
    }
    if (listeners.length === 0) {
      throw new Error('Maybe you forget to add listener?')
    }

    if (name !== '') {
      let scoped = this.ctx.scoped[name]
      if (!scoped) {
        scoped = new Maker()
        this.ctx.scoped[name] = scoped
      }

      scoped.local(method, listeners)
    }

    this.local(method, listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  post(...listeners) {
    this.method('POST', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  get(...listeners) {
    this.method('GET', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  put(...listeners) {
    this.method('PUT', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  delete(...listeners) {
    this.method('DELETE', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  patch(...listeners) {
    this.method('PATCH', listeners)
  }
}

/**
 *
 * @param {function} creator
 */
export function createRest(creator) {
  const ctx = new Maker('')
  creator(ctx)
  return ctx.build()
}

export { flattenRoutes } from './flatten'
export { printRoutes } from './printer'

<commit_msg>More tests for methods<commit_after>
class Maker {
  constructor() {
    this.ctx = {
      before: [],
      after: [],
      scoped: {},
      local: {},
    }
  }

  build() {
    const scoped = {}
    Object.keys(this.ctx.scoped).forEach(name => {
      scoped[name] = this.ctx.scoped[name].build()
    })
    return Object.assign({}, this.ctx, { scoped })
  }

  before(...list) {
    this.ctx.before = this.ctx.before.concat(list)
  }

  after(...list) {
    this.ctx.after = this.ctx.after.concat(list)
  }

  scope(name, creator) {
    if (name.indexOf('/') !== -1) {
      throw new Error('Name of the scope should be simple string!')
    }

    const scopedCtx = new Maker()
    creator(scopedCtx)
    this.ctx.scoped[name] = scopedCtx
  }

  /**
   * Add HTTP method listeners to local
   * @param {string} method
   * @param {function[]} listeners
   */
  local(method, listeners) {
    // if listeners for that methods exists, simple merge
    if (this.ctx.local[method]) {
      this.ctx.local[method] = this.ctx.local[method].concat(listeners)
    }
    else {
      this.ctx.local[method] = listeners
    }
  }

  /**
   * Handle method creator
   * @param {string} name
   * @param {string} method
   * @param {function[]} listeners
   */
  method(method, listeners) {
    let name = ''
    if (typeof listeners[0] === 'string') {
      name = listeners.splice(0, 1)[0]
    }
    name = name.replace(/^\//gm, '')

    if (name.indexOf('/') !== -1) {
      throw new Error('Path should not be deep')
    }
    if (listeners.length === 0) {
      throw new Error('Maybe you forget to add listener?')
    }

    if (name !== '') {
      let scoped = this.ctx.scoped[name]
      if (!scoped) {
        scoped = new Maker()
        this.ctx.scoped[name] = scoped
      }

      scoped.local(method, listeners)
    }
    else {
      this.local(method, listeners)
    }
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  post(...listeners) {
    this.method('POST', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  get(...listeners) {
    this.method('GET', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  put(...listeners) {
    this.method('PUT', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  delete(...listeners) {
    this.method('DELETE', listeners)
  }

  /**
   * @param {string} [name]
   * @param {...function[]} listeners
   */
  patch(...listeners) {
    this.method('PATCH', listeners)
  }
}

/**
 *
 * @param {function} creator
 */
export function createRest(creator) {
  const ctx = new Maker('')
  creator(ctx)
  return ctx.build()
}

export { flattenRoutes } from './flatten'
export { printRoutes } from './printer'

<|endoftext|>


<commit_before>/*
 *     (C) Copyright 2012 Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/*global document, EzWebExt, LayoutManagerFactory, gettext, interpolate, StyledElements, Wirecloud */

(function () {

    "use strict";

    var ResourcePainter = function ResourcePainter(catalogue, resource_template, container, extra_context) {
        if (arguments.length === 0) {
            return;
        }

        this.builder = new StyledElements.GUIBuilder();
        this.catalogue = catalogue;
        this.structure_template = resource_template;
        this.error_template = '<s:styledgui xmlns:s="http://wirecloud.conwet.fi.upm.es/StyledElements" xmlns:t="http://wirecloud.conwet.fi.upm.es/Template" xmlns="http://www.w3.org/1999/xhtml">
<t:message/>
</s:styledgui>';
        this.container = container;
        if (typeof extra_context === 'object' || typeof extra_context === 'function') {
            this.extra_context = extra_context;
        } else {
            this.extra_context = {};
        }
    };

    ResourcePainter.prototype.setError = function setError(message) {
        this.container.clear();

        var contents = this.builder.parse(this.error_template, {
            'message': message
        });

        this.container.appendChild(contents);
    };

    ResourcePainter.prototype.paint = function paint(resource) {
        var extra_context, i, context, resource_element;

        if (typeof this.extra_context === 'function') {
            extra_context = this.extra_context(resource);
        } else {
            extra_context = EzWebExt.clone(this.extra_context);
        }

        context = EzWebExt.merge(extra_context, {
            'displayname': resource.getDisplayName(),
            'name': resource.getName(),
            'vendor': resource.getVendor(),
            'version': resource.getVersion().text,
            'author': resource.getCreator(),
            'description': resource.getDescription(),
            'lastupdate': function () { return resource.date.strftime('%x'); },
            'doc': function () {
                var button;

                button = new StyledElements.StyledButton({
                    'plain': true,
                    'class': 'icon-doc',
                    'title': gettext('Documentation')
                });
                button.addEventListener('click', function () {
                    window.open(resource.getUriWiki(), '_blank');
                });

                return button;
            },
            'home': function () {
                var button;

                button = new StyledElements.StyledButton({
                    'plain': true,
                    'class': 'icon-home',
                    'title': gettext('Home page')
                });
                button.addEventListener('click', function () {
                    window.open(resource.getUriWiki(), '_blank');
                });

                return button;
            },
            'popularity': this.get_popularity_html.bind(this, resource.getPopularity()),
            'mainbutton': function () {
                var button, local_catalogue_view;

                local_catalogue_view = LayoutManagerFactory.getInstance().viewsByName.marketplace.viewsByName.local;
                if (this.resource.getType() === 'operator') {

                    if (Wirecloud.LocalCatalogue.resourceExists(this.resource)) {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Uninstall')
                        });
                        button.addEventListener('click', local_catalogue_view.createUserCommand('uninstall', this.resource, this.catalogue));
                    } else {

                        button = new StyledElements.StyledButton({
                            'text': gettext('Install')
                        });

                        button.addEventListener('click', local_catalogue_view.createUserCommand('import', this.resource, this.catalogue));
                    }
                } else if (this.catalogue.getLabel() === 'local') {
                    switch (this.resource.getType()) {
                    case 'mashup':
                    case 'widget':
                        button = new StyledElements.StyledButton({
                            'class': 'instantiate_button',
                            'text': gettext('Add to workspace')
                        });
                        button.addEventListener('click', this.catalogue.createUserCommand('instantiate', this.resource));
                        break;
                    default:
                        button = new StyledElements.StyledButton({text: gettext('Download')});
                        button.addEventListener('click', function (resource) {
                            window.open(resource.getUriTemplate(), '_blank');
                        }.bind(null, this.resource));
                    }
                } else {
                    if (Wirecloud.LocalCatalogue.resourceExists(this.resource)) {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Uninstall')
                        });
                        button.addEventListener('click', local_catalogue_view.createUserCommand('uninstall', this.resource, this.catalogue));
                    } else {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Install')
                        });

                        button.addEventListener('click', local_catalogue_view.createUserCommand('import', this.resource, this.catalogue));
                    }
                }
                button.addClassName('mainbutton');
                return button;
            }.bind({catalogue: this.catalogue, resource: resource}),
            'image': function () {
                var image = document.createElement('img');
                image.onerror = function (event) {
                    event.target.src = '/static/images/noimage.png';
                };
                image.src = resource.getUriImage();
                return image;
            },
            'tags': function (options) {
                return this.painter.renderTagList(this.resource, options.max);
            }.bind({painter: this, resource: resource}),
            'advancedops': this.renderAdvancedOperations.bind(this, resource),
            'uploader': function () {
                if (resource.uploader != null) {
                    return resource.uploader;
                } else {
                    return gettext('Anonymous');
                }
            }
        });

        resource_element = this.builder.parse(this.structure_template, context);

        // TODO "Show details"
        for (i = 0; i < resource_element.elements.length; i += 1) {
            if (!EzWebExt.XML.isElement(resource_element.elements[i])) {
                continue;
            }
            this.create_simple_command(resource_element.elements[i], '.click_for_details', 'click', this.catalogue.createUserCommand('showDetails', resource));
        }

        return resource_element;
    };

    ResourcePainter.prototype.renderAdvancedOperations = function renderAdvancedOperations(resource) {
        var button, fragment = new StyledElements.Fragment();

        button = new StyledElements.StyledButton({
            'text': gettext('Download')
        });
        button.addEventListener('click', function () {
            window.open(resource.getUriTemplate(), '_blank');
        });
        fragment.appendChild(button);

        if (resource.isAllow('delete')) {
            button = new StyledElements.StyledButton({
                'class': 'btn-danger',
                'text': gettext('Delete')
            });
            button.addEventListener('click', this.catalogue.createUserCommand('delete', resource));
            fragment.appendChild(button);
        }

        if ((resource.getAllVersions().length > 1) && resource.isAllow('delete-all')) {
            button = new StyledElements.StyledButton({
                'class': 'btn-danger',
                'text': gettext('Delete all versions')
            });
            button.addEventListener('click', this.catalogue.createUserCommand('delete', resource));
            fragment.appendChild(button);
        }

        return fragment;
    };

    ResourcePainter.prototype.renderTagList = function renderTagList(resource, listener, max) {
        var i, fragment, tags, tag, tag_element, len;

        fragment = new StyledElements.Fragment();

        tags = resource.getTags();
        tags = tags.sort(function (a, b) {
            return b.apparences - a.apparences;
        });

        if (typeof max === 'undefined') {
            max = tags.length;
        }
        len = Math.min(max, tags.length);

        for (i = 0; i < len; i += 1) {
            tag = tags[i];

            tag_element = document.createElement('a');
            tag_element.textContent = tag.value;
            fragment.appendChild(tag_element);
        }

        return fragment;
    };

    ResourcePainter.prototype.create_simple_command = function (element, selector, _event, handler, required) {
        var i, elements = element.getElementsBySelector(selector);

        if (required && elements.length < 1) {
            throw new Error();
        }

        for (i = 0; i < elements.length; i += 1) {
            EzWebExt.addEventListener(elements[i], _event, handler);
        }
    };

    ResourcePainter.prototype.get_popularity_html = function (popularity) {
        var on_stars, md_star, off_stars, stars, star, i;

        on_stars = Math.floor(popularity);
        md_star = popularity - on_stars;
        off_stars = 5 - popularity;

        stars = document.createElement('div');
        stars.className = 'popularity';

        // "On" stars
        for (i = 0; i < on_stars; i += 1) {
            star = document.createElement('span');
            star.className = 'on star';
            stars.appendChild(star);
        }

        if (md_star) {
            star = document.createElement('span');
            star.className = 'md star';
            stars.appendChild(star);
        }

        // "Off" stars
        for (i = 0; i < Math.floor(off_stars); i += 1) {
            star = document.createElement('span');
            star.className = 'off star';
            stars.appendChild(star);
        }

        return stars;
    };

    Wirecloud.ui.ResourcePainter = ResourcePainter;
})();
<commit_msg>Use btn-primary for the primary button of resources in the catalogue/marketplace view<commit_after>/*
 *     (C) Copyright 2012 Universidad Politécnica de Madrid
 *
 *     This file is part of Wirecloud Platform.
 *
 *     Wirecloud Platform is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU Affero General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Wirecloud is distributed in the hope that it will be useful, but WITHOUT
 *     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
 *     License for more details.
 *
 *     You should have received a copy of the GNU Affero General Public License
 *     along with Wirecloud Platform.  If not, see
 *     <http://www.gnu.org/licenses/>.
 *
 */

/*global document, EzWebExt, LayoutManagerFactory, gettext, interpolate, StyledElements, Wirecloud */

(function () {

    "use strict";

    var ResourcePainter = function ResourcePainter(catalogue, resource_template, container, extra_context) {
        if (arguments.length === 0) {
            return;
        }

        this.builder = new StyledElements.GUIBuilder();
        this.catalogue = catalogue;
        this.structure_template = resource_template;
        this.error_template = '<s:styledgui xmlns:s="http://wirecloud.conwet.fi.upm.es/StyledElements" xmlns:t="http://wirecloud.conwet.fi.upm.es/Template" xmlns="http://www.w3.org/1999/xhtml">
<t:message/>
</s:styledgui>';
        this.container = container;
        if (typeof extra_context === 'object' || typeof extra_context === 'function') {
            this.extra_context = extra_context;
        } else {
            this.extra_context = {};
        }
    };

    ResourcePainter.prototype.setError = function setError(message) {
        this.container.clear();

        var contents = this.builder.parse(this.error_template, {
            'message': message
        });

        this.container.appendChild(contents);
    };

    ResourcePainter.prototype.paint = function paint(resource) {
        var extra_context, i, context, resource_element;

        if (typeof this.extra_context === 'function') {
            extra_context = this.extra_context(resource);
        } else {
            extra_context = EzWebExt.clone(this.extra_context);
        }

        context = EzWebExt.merge(extra_context, {
            'displayname': resource.getDisplayName(),
            'name': resource.getName(),
            'vendor': resource.getVendor(),
            'version': resource.getVersion().text,
            'author': resource.getCreator(),
            'description': resource.getDescription(),
            'lastupdate': function () { return resource.date.strftime('%x'); },
            'doc': function () {
                var button;

                button = new StyledElements.StyledButton({
                    'plain': true,
                    'class': 'icon-doc',
                    'title': gettext('Documentation')
                });
                button.addEventListener('click', function () {
                    window.open(resource.getUriWiki(), '_blank');
                });

                return button;
            },
            'home': function () {
                var button;

                button = new StyledElements.StyledButton({
                    'plain': true,
                    'class': 'icon-home',
                    'title': gettext('Home page')
                });
                button.addEventListener('click', function () {
                    window.open(resource.getUriWiki(), '_blank');
                });

                return button;
            },
            'popularity': this.get_popularity_html.bind(this, resource.getPopularity()),
            'mainbutton': function () {
                var button, local_catalogue_view;

                local_catalogue_view = LayoutManagerFactory.getInstance().viewsByName.marketplace.viewsByName.local;
                if (this.resource.getType() === 'operator') {

                    if (Wirecloud.LocalCatalogue.resourceExists(this.resource)) {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Uninstall')
                        });
                        button.addEventListener('click', local_catalogue_view.createUserCommand('uninstall', this.resource, this.catalogue));
                    } else {

                        button = new StyledElements.StyledButton({
                            'text': gettext('Install')
                        });

                        button.addEventListener('click', local_catalogue_view.createUserCommand('import', this.resource, this.catalogue));
                    }
                } else if (this.catalogue.getLabel() === 'local') {
                    switch (this.resource.getType()) {
                    case 'mashup':
                    case 'widget':
                        button = new StyledElements.StyledButton({
                            'class': 'instantiate_button',
                            'text': gettext('Add to workspace')
                        });
                        button.addEventListener('click', this.catalogue.createUserCommand('instantiate', this.resource));
                        break;
                    default:
                        button = new StyledElements.StyledButton({text: gettext('Download')});
                        button.addEventListener('click', function (resource) {
                            window.open(resource.getUriTemplate(), '_blank');
                        }.bind(null, this.resource));
                    }
                } else {
                    if (Wirecloud.LocalCatalogue.resourceExists(this.resource)) {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Uninstall')
                        });
                        button.addEventListener('click', local_catalogue_view.createUserCommand('uninstall', this.resource, this.catalogue));
                    } else {
                        button = new StyledElements.StyledButton({
                            'text': gettext('Install')
                        });

                        button.addEventListener('click', local_catalogue_view.createUserCommand('import', this.resource, this.catalogue));
                    }
                }
                button.addClassName('mainbutton btn-primary');
                return button;
            }.bind({catalogue: this.catalogue, resource: resource}),
            'image': function () {
                var image = document.createElement('img');
                image.onerror = function (event) {
                    event.target.src = '/static/images/noimage.png';
                };
                image.src = resource.getUriImage();
                return image;
            },
            'tags': function (options) {
                return this.painter.renderTagList(this.resource, options.max);
            }.bind({painter: this, resource: resource}),
            'advancedops': this.renderAdvancedOperations.bind(this, resource),
            'uploader': function () {
                if (resource.uploader != null) {
                    return resource.uploader;
                } else {
                    return gettext('Anonymous');
                }
            }
        });

        resource_element = this.builder.parse(this.structure_template, context);

        // TODO "Show details"
        for (i = 0; i < resource_element.elements.length; i += 1) {
            if (!EzWebExt.XML.isElement(resource_element.elements[i])) {
                continue;
            }
            this.create_simple_command(resource_element.elements[i], '.click_for_details', 'click', this.catalogue.createUserCommand('showDetails', resource));
        }

        return resource_element;
    };

    ResourcePainter.prototype.renderAdvancedOperations = function renderAdvancedOperations(resource) {
        var button, fragment = new StyledElements.Fragment();

        button = new StyledElements.StyledButton({
            'text': gettext('Download')
        });
        button.addEventListener('click', function () {
            window.open(resource.getUriTemplate(), '_blank');
        });
        fragment.appendChild(button);

        if (resource.isAllow('delete')) {
            button = new StyledElements.StyledButton({
                'class': 'btn-danger',
                'text': gettext('Delete')
            });
            button.addEventListener('click', this.catalogue.createUserCommand('delete', resource));
            fragment.appendChild(button);
        }

        if ((resource.getAllVersions().length > 1) && resource.isAllow('delete-all')) {
            button = new StyledElements.StyledButton({
                'class': 'btn-danger',
                'text': gettext('Delete all versions')
            });
            button.addEventListener('click', this.catalogue.createUserCommand('delete', resource));
            fragment.appendChild(button);
        }

        return fragment;
    };

    ResourcePainter.prototype.renderTagList = function renderTagList(resource, listener, max) {
        var i, fragment, tags, tag, tag_element, len;

        fragment = new StyledElements.Fragment();

        tags = resource.getTags();
        tags = tags.sort(function (a, b) {
            return b.apparences - a.apparences;
        });

        if (typeof max === 'undefined') {
            max = tags.length;
        }
        len = Math.min(max, tags.length);

        for (i = 0; i < len; i += 1) {
            tag = tags[i];

            tag_element = document.createElement('a');
            tag_element.textContent = tag.value;
            fragment.appendChild(tag_element);
        }

        return fragment;
    };

    ResourcePainter.prototype.create_simple_command = function (element, selector, _event, handler, required) {
        var i, elements = element.getElementsBySelector(selector);

        if (required && elements.length < 1) {
            throw new Error();
        }

        for (i = 0; i < elements.length; i += 1) {
            EzWebExt.addEventListener(elements[i], _event, handler);
        }
    };

    ResourcePainter.prototype.get_popularity_html = function (popularity) {
        var on_stars, md_star, off_stars, stars, star, i;

        on_stars = Math.floor(popularity);
        md_star = popularity - on_stars;
        off_stars = 5 - popularity;

        stars = document.createElement('div');
        stars.className = 'popularity';

        // "On" stars
        for (i = 0; i < on_stars; i += 1) {
            star = document.createElement('span');
            star.className = 'on star';
            stars.appendChild(star);
        }

        if (md_star) {
            star = document.createElement('span');
            star.className = 'md star';
            stars.appendChild(star);
        }

        // "Off" stars
        for (i = 0; i < Math.floor(off_stars); i += 1) {
            star = document.createElement('span');
            star.className = 'off star';
            stars.appendChild(star);
        }

        return stars;
    };

    Wirecloud.ui.ResourcePainter = ResourcePainter;
})();
<|endoftext|>


<commit_before>const autoprefixer = require(`gulp-autoprefixer`);
const CleanCSS = require(`clean-css`);
const gulp = require(`gulp`);
const htmlmin = require(`gulp-htmlmin`);
const inline = require(`gulp-inline-source`);
const nodeSassMagicImporter = require(`node-sass-magic-importer`);
const path = require(`path`);
const rimraf = require(`rimraf`);
const sass = require(`gulp-sass`);
const sourcemaps = require(`gulp-sourcemaps`);
const uncss = require(`uncss`);

const stylesDestDirectory = `static/dist/css`;
const themeSrcDirectory = `themes/mao/src`;
const scssRoot = `scss`;
const scssPath = path.join(themeSrcDirectory, scssRoot, `**/*.scss`);

const publicDirectory = `public`;
const htmlPath = path.join(publicDirectory, `**/*.html`);

gulp.task(`watch`, () => {
  gulp.watch(scssPath, [`styles`]);
});

gulp.task(`styles`, [`clean:styles`], () =>
  gulp.src(scssPath)
    .pipe(sourcemaps.init())
    .pipe(sass({
      importer: nodeSassMagicImporter(),
    }).on(`error`, sass.logError))
    .pipe(autoprefixer())
    .pipe(sourcemaps.write({ sourceRoot: path.join(`/`, scssRoot) }))
    .pipe(gulp.dest(stylesDestDirectory))
);

gulp.task(`minify:markup`, () =>
  gulp.src(htmlPath)
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(inline({
      rootpath: path.join(publicDirectory, `/`),
      handlers: (source, context, next) => {
        if (source.type === `css` && source.fileContent && !source.content) {
          uncss(context.html, { htmlroot: publicDirectory }, (error, css) => {
            if (error) throw error;
            // eslint-disable-next-line no-param-reassign
            source.content = `<style>${new CleanCSS({ level: 2 }).minify(css).styles}</style>`;
            next();
          });
        } else {
          next();
        }
      },
    }))
    .pipe(gulp.dest(publicDirectory))
);

gulp.task(`clean:styles`, () => rimraf.sync(stylesDestDirectory));

gulp.task(`build`, [`styles`, `minify:markup`]);

gulp.task(`default`, [`watch`, `build`]);
<commit_msg>Reorder<commit_after>const autoprefixer = require(`gulp-autoprefixer`);
const CleanCSS = require(`clean-css`);
const gulp = require(`gulp`);
const htmlmin = require(`gulp-htmlmin`);
const inline = require(`gulp-inline-source`);
const nodeSassMagicImporter = require(`node-sass-magic-importer`);
const path = require(`path`);
const rimraf = require(`rimraf`);
const sass = require(`gulp-sass`);
const sourcemaps = require(`gulp-sourcemaps`);
const uncss = require(`uncss`);

const themeSrcDirectory = `themes/mao/src`;
const publicDirectory = `public`;
const stylesDestDirectory = `static/dist/css`;
const scssRoot = `scss`;
const scssPath = path.join(themeSrcDirectory, scssRoot, `**/*.scss`);
const htmlPath = path.join(publicDirectory, `**/*.html`);

gulp.task(`watch`, () => {
  gulp.watch(scssPath, [`styles`]);
});

gulp.task(`styles`, [`clean:styles`], () =>
  gulp.src(scssPath)
    .pipe(sourcemaps.init())
    .pipe(sass({
      importer: nodeSassMagicImporter(),
    }).on(`error`, sass.logError))
    .pipe(autoprefixer())
    .pipe(sourcemaps.write({ sourceRoot: path.join(`/`, scssRoot) }))
    .pipe(gulp.dest(stylesDestDirectory))
);

gulp.task(`minify:markup`, () =>
  gulp.src(htmlPath)
    .pipe(htmlmin({ collapseWhitespace: true }))
    .pipe(inline({
      rootpath: path.join(publicDirectory, `/`),
      handlers: (source, context, next) => {
        if (source.type === `css` && source.fileContent && !source.content) {
          uncss(context.html, { htmlroot: publicDirectory }, (error, css) => {
            if (error) throw error;
            // eslint-disable-next-line no-param-reassign
            source.content = `<style>${new CleanCSS({ level: 2 }).minify(css).styles}</style>`;
            next();
          });
        } else {
          next();
        }
      },
    }))
    .pipe(gulp.dest(publicDirectory))
);

gulp.task(`clean:styles`, () => rimraf.sync(stylesDestDirectory));

gulp.task(`build`, [`styles`, `minify:markup`]);

gulp.task(`default`, [`watch`, `build`]);
<|endoftext|>


<commit_before>import _ from 'lodash'
import React, { Component } from 'react'
import PropType from 'prop-types'
import classNames from 'classnames'

import Arrow from './Arrow'
import Clear from './Clear'
import FocusPlaceholder from './FocusPlaceholder'
import Input from './Input'
import Menu from './Menu'
import Option from './Option'
import Value from './Value'
import ValueGroup from './ValueGroup'

import './select.scss'

// credit to https://github.com/JedWatson/react-select for many patterns and techniques used here
class SimpleSelect extends Component {
  static propTypes = {
    autoClearInput: PropType.bool,
    autoCloseMenu: PropType.bool,
    arrowComponent: PropType.func,
    arrowRenderer: PropType.func,
    clearable: PropType.bool,
    clearComponent: PropType.func,
    clearRenderer: PropType.func,
    focusPlaceholderComponent: PropType.func,
    inputComponent: PropType.func,
    inputValue: PropType.string,
    isOpen: PropType.bool,
    labelKey: PropType.string,
    menuComponent: PropType.func,
    onBlur: PropType.func,
    onChange: PropType.func,
    onFocus: PropType.func,
    onInputChange: PropType.func,
    optionComponent: PropType.func,
    optionRenderer: PropType.func,
    options: PropType.array,
    placeholder: PropType.string,
    showInput: PropType.bool,
    value: PropType.oneOfType([PropType.object, PropType.array]),
    valueComponent: PropType.func,
    valueRenderer: PropType.func,
    valueGroupComponent: PropType.func,
    valueGroupRenderer: PropType.func,
    valueKey: PropType.string
  }

  static defaultProps = {
    autoClearInput: true,
    autoCloseMenu: true,
    arrowComponent: Arrow,
    arrowRenderer: null,
    clearable: false,
    clearComponent: Clear,
    clearRenderer: null,
    focusPlaceholderComponent: FocusPlaceholder,
    inputComponent: Input,
    inputValue: '',
    isOpen: false,
    labelKey: 'label',
    menuComponent: Menu,
    onBlur: null,
    onChange: null,
    onFocus: null,
    onInputChange: null,
    optionComponent: Option,
    optionRenderer: null,
    options: [],
    placeholder: 'Select value...',
    showInput: false,
    value: null,
    valueComponent: Value,
    valueRenderer: null,
    valueGroupComponent: ValueGroup,
    valueGroupRenderer: null,
    valueKey: 'value'
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      isFocused: false
    }
  }

  componentDidMount() {
    document.addEventListener('touchstart', this.onDocumentClick, false)
  }

  componentWillUnmount() {
    document.removeEventListener('touchstart', this.onDocumentClick, false)
  }

  onDocumentClick = (event) => {
    if (this.container && !this.container.contains(event.target)) {
      this.setState({ isOpen: false })
    }
  }

  onValueTouchEnd = (event) => {
    this.onValueMouseDown(event)
  }

  onValueMouseDown = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    const isOpen = this.props.isOpen || this.props.showInput ? true : !this.state.isOpen

    event.stopPropagation()
    event.preventDefault()

    this.focus()

    if (!this.props.showInput) {
      this.setState({ isOpen })
      return
    }

    // thanks https://github.com/JedWatson/react-select/blob/master/src/Select.js#L238
    if (this.state.isFocused) {
      // On iOS, we can get into a state where we think the input is focused but it isn't really,
      // since iOS ignores programmatic calls to input.focus() that weren't
      // triggered by a click event. Call focus() again here to be safe.

      // clears the value so that the cursor will be at the end of input this re-renders
      this.input.value = ''

      // if the input is focused, ensure the menu is open
      this.setState({
        isOpen: true,
        isOuterFocused: false
      })
    } else {
      this.setState({ isOpen })
    }
  }

  onInputBlur = (event) => {
    if (this.props.onBlur) {
      this.props.onBlur(event)
    }

    this.setState({
      isOpen: false,
      isFocused: false,
      isOuterFocused: false
    })
  }

  onInputFocus = (event) => {
    if (this.props.onFocus) {
      this.props.onFocus(event)
    }

    const isOpen = this.props.isOpen || this.state.isOpen

    this.setState({
      isOpen,
      isFocused: true
    })
  }

  onInputChange = (event) => {
    const inputValue = event.target.value

    if (this.props.inputValue === inputValue) {
      return
    }

    this.setState({
      isOpen: true,
      isOuterFocused: false
    }, () => {
      this.setInputValue(event, inputValue)
    })
  }

  onArrowTouchEnd = (event) => {
    this.onArrowClick(event)
  }

  onArrowClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (this.props.isOpen || !this.state.isOpen) {
      // let it bubble up
      return
    }

    event.stopPropagation()
    event.preventDefault()

    // we know we are closing menu
    this.setState({ isOpen: false }, this.focus)
  }

  onClearTouchEnd = (event) => {
    this.onClearClick(event)
  }

  onClearClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.setState({ isOpen: false, isOuterFocused: false }, this.emitValueChange(null, event))
  }

  onOptionClick = (event, option) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    const isOpen = !this.props.autoCloseMenu
    const newState = { isOpen, isOuterFocused: !isOpen && this.props.autoClearInput }
    this.setState(newState, this.emitValueChange(option, event))
  }

  setInputValue = (event, inputVal) => {
    if (this.props.onInputChange) {
      this.props.onInputChange(inputVal, event)
    }
  }

  emitValueChange = (option, event) => {
    const { autoClearInput, value, valueKey, labelKey } = this.props
    const valueSelected = (option === null || value === null) && option !== value
    const valueChanged = _.isArray(value)
      ? true
      : value && option && value[valueKey] !== option[valueKey]

    if ((valueSelected || valueChanged) && this.props.onChange) {
      this.props.onChange(option)
      const newInputVal = !autoClearInput && option !== null ? option[labelKey] : ''
      this.setInputValue(event, newInputVal)
    }

    if (this.props.autoCloseMenu) {
      this.focus()
    }
  }

  // check for right-clicks, etc
  isSecondayClick = event => (event.type === 'mousedown' && event.button !== 0)

  focus = () => { this.input.focus() }

  blur = () => { this.input.blur() }

  renderArrow() {
    const ArrowComponent = this.props.arrowComponent
    const { arrowRenderer } = this.props

    return (<ArrowComponent
      arrowRenderer={arrowRenderer}
      onArrowClick={this.onArrowClick}
      onArrowTouchEnd={this.onArrowTouchEnd}
    />)
  }

  renderClear() {
    const ClearComponent = this.props.clearComponent
    const { clearable, clearRenderer, value } = this.props
    const hasValue = _.isArray(value) ? value.length : value

    if (!clearable || !hasValue) {
      return null
    }

    return (<ClearComponent
      clearRenderer={clearRenderer}
      onClearClick={this.onClearClick}
      onClearTouchEnd={this.onClearTouchEnd}
    />)
  }

  renderMenu() {
    const menuProps = _.omit(this.props, 'menuComponent')
    const MenuComponent = this.props.menuComponent
    return (

        <MenuComponent {...menuProps} onOptionClick={this.onOptionClick} />

    )
  }

  renderInput(isOpen) {
    const inputProps = {
      // TODO implement static list of select ids
      ariaControls: '',
      ariaExpanded: isOpen ? 'true' : 'false',
      getRef: (ref) => { this.input = ref },
      inputValue: this.props.inputValue,
      onBlur: this.onInputBlur,
      onChange: this.onInputChange,
      onFocus: this.onInputFocus
    }
    const InputComponent = this.props.inputComponent

    return this.props.showInput
      ? <InputComponent {...inputProps} />
      : this.renderFocusPlaceholder(inputProps)
  }

  renderFocusPlaceholder(inputProps) {
    const FocusPlaceholderComponent = this.props.focusPlaceholderComponent
    const props = _.omit(inputProps, 'onChange', 'inputValue')
    return <FocusPlaceholderComponent {...props} />
  }

  render() {
    const { isFocused, isOuterFocused } = this.state
    const isOpen = this.props.isOpen || this.state.isOpen
    const valueGroupProps = _.omit(this.props, 'valueGroupComponent')
    const ValueGroupComponent = this.props.valueGroupComponent
    const selectClassName = classNames('crane-select', {
      open: isOpen, focus: isFocused, 'outer-focus': isOuterFocused
    })

    return (

 { this.container = container }}>

          <ValueGroupComponent {...valueGroupProps} />
          {this.renderInput(isOpen)}
          {this.renderClear()}
          {this.renderArrow()}

        {isOpen && this.renderMenu()}

    )
  }
}

export default SimpleSelect
<commit_msg>If input was clicked, do not stop propagation so user can select text.<commit_after>import _ from 'lodash'
import React, { Component } from 'react'
import PropType from 'prop-types'
import classNames from 'classnames'

import Arrow from './Arrow'
import Clear from './Clear'
import FocusPlaceholder from './FocusPlaceholder'
import Input from './Input'
import Menu from './Menu'
import Option from './Option'
import Value from './Value'
import ValueGroup from './ValueGroup'

import './select.scss'

// credit to https://github.com/JedWatson/react-select for many patterns and techniques used here
class SimpleSelect extends Component {
  static propTypes = {
    autoClearInput: PropType.bool,
    autoCloseMenu: PropType.bool,
    arrowComponent: PropType.func,
    arrowRenderer: PropType.func,
    clearable: PropType.bool,
    clearComponent: PropType.func,
    clearRenderer: PropType.func,
    focusPlaceholderComponent: PropType.func,
    inputComponent: PropType.func,
    inputValue: PropType.string,
    isOpen: PropType.bool,
    labelKey: PropType.string,
    menuComponent: PropType.func,
    onBlur: PropType.func,
    onChange: PropType.func,
    onFocus: PropType.func,
    onInputChange: PropType.func,
    optionComponent: PropType.func,
    optionRenderer: PropType.func,
    options: PropType.array,
    placeholder: PropType.string,
    showInput: PropType.bool,
    value: PropType.oneOfType([PropType.object, PropType.array]),
    valueComponent: PropType.func,
    valueRenderer: PropType.func,
    valueGroupComponent: PropType.func,
    valueGroupRenderer: PropType.func,
    valueKey: PropType.string
  }

  static defaultProps = {
    autoClearInput: true,
    autoCloseMenu: true,
    arrowComponent: Arrow,
    arrowRenderer: null,
    clearable: false,
    clearComponent: Clear,
    clearRenderer: null,
    focusPlaceholderComponent: FocusPlaceholder,
    inputComponent: Input,
    inputValue: '',
    isOpen: false,
    labelKey: 'label',
    menuComponent: Menu,
    onBlur: null,
    onChange: null,
    onFocus: null,
    onInputChange: null,
    optionComponent: Option,
    optionRenderer: null,
    options: [],
    placeholder: 'Select value...',
    showInput: false,
    value: null,
    valueComponent: Value,
    valueRenderer: null,
    valueGroupComponent: ValueGroup,
    valueGroupRenderer: null,
    valueKey: 'value'
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      isFocused: false
    }
  }

  componentDidMount() {
    document.addEventListener('touchstart', this.onDocumentClick, false)
  }

  componentWillUnmount() {
    document.removeEventListener('touchstart', this.onDocumentClick, false)
  }

  onDocumentClick = (event) => {
    if (this.container && !this.container.contains(event.target)) {
      this.setState({ isOpen: false })
    }
  }

  onValueTouchEnd = (event) => {
    this.onValueMouseDown(event)
  }

  onValueMouseDown = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    const isOpen = this.props.isOpen || this.props.showInput ? true : !this.state.isOpen

    event.stopPropagation()

    if (event.target.tagName !== 'INPUT') {
      event.preventDefault()
    }

    this.focus()

    if (!this.props.showInput) {
      this.setState({ isOpen })
      return
    }

    // thanks https://github.com/JedWatson/react-select/blob/master/src/Select.js#L238
    if (this.state.isFocused) {
      // On iOS, we can get into a state where we think the input is focused but it isn't really,
      // since iOS ignores programmatic calls to input.focus() that weren't
      // triggered by a click event. Call focus() again here to be safe.

      // clears the value so that the cursor will be at the end of input this re-renders
      this.input.value = ''

      // if the input is focused, ensure the menu is open
      this.setState({
        isOpen: true,
        isOuterFocused: false
      })
    } else {
      this.setState({ isOpen })
    }
  }

  onInputBlur = (event) => {
    if (this.props.onBlur) {
      this.props.onBlur(event)
    }

    this.setState({
      isOpen: false,
      isFocused: false,
      isOuterFocused: false
    })
  }

  onInputFocus = (event) => {
    if (this.props.onFocus) {
      this.props.onFocus(event)
    }

    const isOpen = this.props.isOpen || this.state.isOpen

    this.setState({
      isOpen,
      isFocused: true
    })
  }

  onInputChange = (event) => {
    const inputValue = event.target.value

    if (this.props.inputValue === inputValue) {
      return
    }

    this.setState({
      isOpen: true,
      isOuterFocused: false
    }, () => {
      this.setInputValue(event, inputValue)
    })
  }

  onArrowTouchEnd = (event) => {
    this.onArrowClick(event)
  }

  onArrowClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    if (this.props.isOpen || !this.state.isOpen) {
      // let it bubble up
      return
    }

    event.stopPropagation()
    event.preventDefault()

    // we know we are closing menu
    this.setState({ isOpen: false }, this.focus)
  }

  onClearTouchEnd = (event) => {
    this.onClearClick(event)
  }

  onClearClick = (event) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    this.setState({ isOpen: false, isOuterFocused: false }, this.emitValueChange(null, event))
  }

  onOptionClick = (event, option) => {
    if (this.isSecondayClick(event)) {
      return
    }

    event.stopPropagation()
    event.preventDefault()

    const isOpen = !this.props.autoCloseMenu
    const newState = { isOpen, isOuterFocused: !isOpen && this.props.autoClearInput }
    this.setState(newState, this.emitValueChange(option, event))
  }

  setInputValue = (event, inputVal) => {
    if (this.props.onInputChange) {
      this.props.onInputChange(inputVal, event)
    }
  }

  emitValueChange = (option, event) => {
    const { autoClearInput, value, valueKey, labelKey } = this.props
    const valueSelected = (option === null || value === null) && option !== value
    const valueChanged = _.isArray(value)
      ? true
      : value && option && value[valueKey] !== option[valueKey]

    if ((valueSelected || valueChanged) && this.props.onChange) {
      this.props.onChange(option)
      const newInputVal = !autoClearInput && option !== null ? option[labelKey] : ''
      this.setInputValue(event, newInputVal)
    }

    if (this.props.autoCloseMenu) {
      this.focus()
    }
  }

  // check for right-clicks, etc
  isSecondayClick = event => (event.type === 'mousedown' && event.button !== 0)

  focus = () => { this.input.focus() }

  blur = () => { this.input.blur() }

  renderArrow() {
    const ArrowComponent = this.props.arrowComponent
    const { arrowRenderer } = this.props

    return (<ArrowComponent
      arrowRenderer={arrowRenderer}
      onArrowClick={this.onArrowClick}
      onArrowTouchEnd={this.onArrowTouchEnd}
    />)
  }

  renderClear() {
    const ClearComponent = this.props.clearComponent
    const { clearable, clearRenderer, value } = this.props
    const hasValue = _.isArray(value) ? value.length : value

    if (!clearable || !hasValue) {
      return null
    }

    return (<ClearComponent
      clearRenderer={clearRenderer}
      onClearClick={this.onClearClick}
      onClearTouchEnd={this.onClearTouchEnd}
    />)
  }

  renderMenu() {
    const menuProps = _.omit(this.props, 'menuComponent')
    const MenuComponent = this.props.menuComponent
    return (

        <MenuComponent {...menuProps} onOptionClick={this.onOptionClick} />

    )
  }

  renderInput(isOpen) {
    const inputProps = {
      // TODO implement static list of select ids
      ariaControls: '',
      ariaExpanded: isOpen ? 'true' : 'false',
      getRef: (ref) => { this.input = ref },
      inputValue: this.props.inputValue,
      onBlur: this.onInputBlur,
      onChange: this.onInputChange,
      onFocus: this.onInputFocus
    }
    const InputComponent = this.props.inputComponent

    return this.props.showInput
      ? <InputComponent {...inputProps} />
      : this.renderFocusPlaceholder(inputProps)
  }

  renderFocusPlaceholder(inputProps) {
    const FocusPlaceholderComponent = this.props.focusPlaceholderComponent
    const props = _.omit(inputProps, 'onChange', 'inputValue')
    return <FocusPlaceholderComponent {...props} />
  }

  render() {
    const { isFocused, isOuterFocused } = this.state
    const isOpen = this.props.isOpen || this.state.isOpen
    const valueGroupProps = _.omit(this.props, 'valueGroupComponent')
    const ValueGroupComponent = this.props.valueGroupComponent
    const selectClassName = classNames('crane-select', {
      open: isOpen, focus: isFocused, 'outer-focus': isOuterFocused
    })

    return (

 { this.container = container }}>

          <ValueGroupComponent {...valueGroupProps} />
          {this.renderInput(isOpen)}
          {this.renderClear()}
          {this.renderArrow()}

        {isOpen && this.renderMenu()}

    )
  }
}

export default SimpleSelect
<|endoftext|>


<commit_before>'use strict';

import {expect} from 'chai';
import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils';

import {groupPost} from './GroupPost.mock.js';

import GroupPostStore from '../../../stores/GroupPost.store.js';

import GroupPostActions from '../../../actions/GroupPost.action.js';

import GroupPostContainer from '../Post/GroupPostContainer.component.js';
import GroupPostComponent from '../Post/GroupPost.component.js';

describe('Test the group post components', () => {
  it('Create GroupPostContainer with props', () => {
    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });
    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');
  });

  it('Create GroupPostContainer with props and data in state', () => {
    let mock = sinon.mock(GroupPostStore); // eslint-disable-line
    mock.expects('getInitialState').once().returns({data: groupPost.groupPostData, pending: false});

    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');
    mock.verify();
    mock.restore();
  });

  it('Tests GroupPostActions get called', () => {
    let sandbox = sinon.sandbox.create(); // eslint-disable-line
    sandbox.spy(GroupPostActions, 'fetchGroupPost');

    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');

    expect(GroupPostActions.fetchGroupPost.calledOnce).to.equal(true);
    GroupPostActions.fetchGroupPost.calledWith(groupPost.groupPostId);
    sandbox.restore();
  });

  it('Tests GroupPost conditionals', () => {
    let commentText = '';

    let element = React.createElement(GroupPostComponent, {
      loggedIn: true,
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId,
      commentCb: (text) => {
        commentText = text;
      }
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = TestUtils.findRenderedComponentWithType(dom, GroupPostComponent);
    let commentField = React.findDOMNode(GPost.refs.commentField);
    let commentButton = React.findDOMNode(GPost.refs.commentButton);
    commentField.value = 'commenting is fun';
    TestUtils.Simulate.change(commentField);
    TestUtils.Simulate.click(commentButton);

    expect(commentText).to.equal('commenting is fun');
  });
});
<commit_msg>Accumulated commit of the following from branch 'origin/ready/jph-groups':<commit_after>'use strict';

import {expect} from 'chai';
import React from 'react';
import TestUtils from 'react/lib/ReactTestUtils';

import {groupPost} from './GroupPost.mock.js';

import GroupPostStore from '../../../stores/GroupPost.store.js';

import GroupPostActions from '../../../actions/GroupPost.action.js';

import GroupPostContainer from '../Post/GroupPostContainer.component.js';
import GroupPostComponent from '../Post/GroupPost.component.js';

describe('Test the group post components', () => {
  it('Create GroupPostContainer without data', () => {
    let element = React.createElement(GroupPostContainer, {groupPostData: {}});
    let dom = TestUtils.renderIntoDocument(element);
    React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
  });

  it('Create GroupPostContainer with props', () => {
    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });
    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');
  });

  it('Create GroupPostContainer with props and data in state', () => {
    let mock = sinon.mock(GroupPostStore); // eslint-disable-line
    mock.expects('getInitialState').once().returns({data: groupPost.groupPostData, pending: false});

    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');
    mock.verify();
    mock.restore();
  });

  it('Tests GroupPostActions get called', () => {
    let sandbox = sinon.sandbox.create(); // eslint-disable-line
    sandbox.spy(GroupPostActions, 'fetchGroupPost');

    let element = React.createElement(GroupPostContainer, {
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = React.findDOMNode(TestUtils.findRenderedComponentWithType(dom, GroupPostComponent));
    expect(GPost.innerHTML).to.contain('Dette er en kommentar');

    expect(GroupPostActions.fetchGroupPost.calledOnce).to.equal(true);
    GroupPostActions.fetchGroupPost.calledWith(groupPost.groupPostId);
    sandbox.restore();
  });

  it('Tests GroupPost conditionals', () => {
    let commentText = '';

    let element = React.createElement(GroupPostComponent, {
      loggedIn: true,
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId,
      commentCb: (text) => {
        commentText = text;
      }
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = TestUtils.findRenderedComponentWithType(dom, GroupPostComponent);
    let commentField = React.findDOMNode(GPost.refs.commentField);
    let commentButton = React.findDOMNode(GPost.refs.commentButton);
    commentField.value = 'commenting is fun';
    TestUtils.Simulate.change(commentField);
    TestUtils.Simulate.click(commentButton);

    expect(commentText).to.equal('commenting is fun');
  });

  it('create GroupPost and comment on it', () => {
    let commentText = '';

    let element = React.createElement(GroupPostComponent, {
      loggedIn: true,
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId,
      commentCb: (text) => {
        commentText = text;
      }
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = TestUtils.findRenderedComponentWithType(dom, GroupPostComponent);
    let commentField = React.findDOMNode(GPost.refs.commentField);
    let commentButton = React.findDOMNode(GPost.refs.commentButton);
    commentField.value = '';
    TestUtils.Simulate.change(commentField);
    TestUtils.Simulate.click(commentButton);

    expect(commentText).to.equal('');
  });

  it('Create GroupPost and comment on it without a commentcb', () => {
    let commentText = '';

    let element = React.createElement(GroupPostComponent, {
      loggedIn: true,
      groupId: groupPost.groupId,
      groupPostData: groupPost.groupPostData,
      groupPostId: groupPost.groupPostId
    });

    let dom = TestUtils.renderIntoDocument(element);
    let GPost = TestUtils.findRenderedComponentWithType(dom, GroupPostComponent);
    let commentField = React.findDOMNode(GPost.refs.commentField);
    let commentButton = React.findDOMNode(GPost.refs.commentButton);
    commentField.value = 'this comment won\'t display';
    TestUtils.Simulate.change(commentField);
    TestUtils.Simulate.click(commentButton);

    expect(commentText).to.equal('');
  });

  it('Create grouppost with grouppost data', () => {
    let element = React.createElement(GroupPostComponent, {
      groupPostData: groupPost.groupPostData
    });

    let dom = TestUtils.renderIntoDocument(element);
    TestUtils.findRenderedComponentWithType(dom, GroupPostComponent);
  });
});
<|endoftext|>


<commit_before>flickrParser = function(json){
	var imageList = []
	$(json.items).each(function(i, image){
		imageList.push({
			'title':image.title,
			'artist':image.author,
			'url':image.link,
			'image':image.media.m});
	})
	return imageList;
}

distributeimages = function(imgNum, pxWidth){
	builder = function(remainder, acumFun){
		if(remainder == 0)
			return acumFun;
		var a = Math.ceil(imgNum / remainder)
		var newFun = function(x){ return acumFun(x) + (x % a == 0) }
		return builder(remainder - Math.floor(imgNum / a), newFun);
	}

	base = function(x){ return Math.floor(pxWidth / imgNum) };
	return builder(pxWidth % imgNum, base)
}

renderGallery = function(domObj, imgList){
	var distribution = distributeimages(imgList.length, domObj.width());

	$(imgList).each(function(i, img){
		var hoverTemp = "
<\/div>"
		var elm = $(hoverTemp);

		domObj.append(elm);
		elm.click(function(){ window.open(img.url, '_blank'); });
	});
}

$(document).ready(function(){
	$('.swipeGal').each(function(i, obj){
		var feedid = $(obj).data('feed-id');
		var url = 'http://ycpi.api.flickr.com/services/feeds/groups_pool.gne?id='+feedid+'&jsoncallback=?';

		$.getJSON(url, {format: "json"})
			.done(function(data){
					parsedImages = flickrParser(data);
					renderGallery($(obj), parsedImages)
				});
	});
});
<commit_msg>Change Gallery bg-image on rollover.<commit_after>flickrParser = function(json){
	var imageList = []
	$(json.items).each(function(i, image){
		imageList.push({
			'title':image.title,
			'artist':image.author,
			'url':image.link,
			'image':image.media.m});
	})
	return imageList;
}

distributeimages = function(imgNum, pxWidth){
	builder = function(remainder, acumFun){
		if(remainder == 0)
			return acumFun;
		var a = Math.ceil(imgNum / remainder)
		var newFun = function(x){ return acumFun(x) + (x % a == 0) }
		return builder(remainder - Math.floor(imgNum / a), newFun);
	}

	base = function(x){ return Math.floor(pxWidth / imgNum) };
	return builder(pxWidth % imgNum, base)
}

changeImage = function(domObj, img){
	domObj.css('background-image', "URL('"+img.image+"')");
}

renderGallery = function(domObj, imgList){
	var distribution = distributeimages(imgList.length, domObj.width());

	$(imgList).each(function(i, img){
		var hoverTemp = "
<\/div>"
		var elm = $(hoverTemp);

		domObj.append(elm);
		elm.click(function(){ window.open(img.url, '_blank'); });
		elm.mouseover(function(){ changeImage(domObj, img); });
	});
}

$(document).ready(function(){
	$('.swipeGal').each(function(i, obj){
		var feedid = $(obj).data('feed-id');
		var url = 'http://ycpi.api.flickr.com/services/feeds/groups_pool.gne?id='+feedid+'&jsoncallback=?';

		$.getJSON(url, {format: "json"})
			.done(function(data){
					parsedImages = flickrParser(data);
					renderGallery($(obj), parsedImages)
				});
	});
});
<|endoftext|>


<commit_before>var vows = require("vows"),
    load = require("../load"),
    assert = require("../assert");

var suite = vows.describe("selection.append");

suite.addBatch({
  "select(body)": {
    topic: load("selection/append").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body");
      },
      "appends an HTML element": function(body) {
        var div = body.append("div");
        assert.equal(div[0][0].tagName, "DIV");
        assert.isNull(div[0][0].namespaceURI);
        assert.isTrue(div[0][0].parentNode === body.node());
        assert.isTrue(div[0][0] === body.node().lastChild);
      },
      "appends an SVG element": function(body) {
        var svg = body.append("svg:svg");
        assert.equal(svg[0][0].tagName, "SVG");
        assert.equal(svg[0][0].namespaceURI, "http://www.w3.org/2000/svg");
        assert.isTrue(svg[0][0].parentNode === body.node());
        assert.isTrue(svg[0][0] === body.node().lastChild);
      },
      "propagates data to new element": function(body) {
        var data = new Object(), div = body.data([data]).append("div");
        assert.strictEqual(div[0][0].__data__, data);
      },
      "returns a new selection": function(body) {
        assert.isFalse(body.append("div") === body);
      },
      "inherits namespace from parent node": function(body) {
        var g = body.append("svg:svg").append("g");
        assert.equal(g[0][0].namespaceURI, "http://www.w3.org/2000/svg");
      }
    }
  }
});

suite.addBatch({
  "selectAll(div)": {
    topic: load("selection/selection").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body").selectAll("div").data([0, 1]).enter().append("div");
      },
      "appends an HTML element": function(div) {
        var span = div.append("span");
        assert.equal(span[0].length, 2);
        assert.equal(span[0][0].tagName, "SPAN");
        assert.equal(span[0][1].tagName, "SPAN");
        assert.isNull(span[0][0].namespaceURI);
        assert.isNull(span[0][1].namespaceURI);
        assert.isTrue(span[0][0].parentNode === div[0][0]);
        assert.isTrue(span[0][1].parentNode === div[0][1]);
        assert.isTrue(div[0][0].lastChild === span[0][0]);
        assert.isTrue(div[0][1].lastChild === span[0][1]);
      },
      "appends an SVG element": function(div) {
        var svg = div.append("svg:svg");
        assert.equal(svg[0].length, 2);
        assert.equal(svg[0][0].tagName, "SVG");
        assert.equal(svg[0][1].tagName, "SVG");
        assert.equal(svg[0][0].namespaceURI, "http://www.w3.org/2000/svg");
        assert.equal(svg[0][1].namespaceURI, "http://www.w3.org/2000/svg");
        assert.isTrue(svg[0][0].parentNode === div[0][0]);
        assert.isTrue(svg[0][1].parentNode === div[0][1]);
        assert.isTrue(div[0][0].lastChild === svg[0][0]);
        assert.isTrue(div[0][1].lastChild === svg[0][1]);
      },
      "propagates data to new elements": function(div) {
        var a = new Object(), b = new Object(), span = div.data([a, b]).append("span");
        assert.strictEqual(span[0][0].__data__, a);
        assert.strictEqual(span[0][1].__data__, b);
      },
      "returns a new selection": function(div) {
        assert.isFalse(div.append("div") === div);
      },
      "ignores null nodes": function(div) {
        var node = div.html("")[0][1];
        div[0][1] = null;
        var span = div.append("span");
        assert.equal(span[0].length, 2);
        assert.equal(span[0][0].tagName, "SPAN");
        assert.isNull(span[0][1]);
        assert.isTrue(span[0][0].parentNode === div[0][0]);
        assert.isTrue(div[0][0].lastChild === span[0][0]);
        assert.isNull(node.lastChild);
      }
    }
  }
});

suite.addBatch({
  "selectAll(div).data(…).enter()": {
    topic: load("selection/selection").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body");
      },
      "appends to the parent node": function(body) {
        var div = body.selectAll("div").data([0, 1]).enter().append("div");
        assert.equal(div.length, 1);
        assert.equal(div[0].length, 2);
        assert.domEqual(div[0][0].parentNode, body.node());
        assert.domEqual(div[0][1].parentNode, body.node());
      },
      "propagates data to new elements": function(body) {
        var a = new Object(), b = new Object(), div = body.html("").selectAll("div").data([a, b]).enter().append("div");
        assert.strictEqual(div[0][0].__data__, a);
        assert.strictEqual(div[0][1].__data__, b);
      },
      "ignores null nodes": function(body) {
        body.html("").append("div");
        var div = body.selectAll("div").data([0, 1, 2]).enter().append("div");
        assert.equal(div.length, 1);
        assert.equal(div[0].length, 3);
        assert.domNull(div[0][0]);
        assert.domEqual(div[0][1].parentNode, body.node());
        assert.domEqual(div[0][2].parentNode, body.node());
      }
    }
  }
});

suite.export(module);
<commit_msg>Test for append(function).<commit_after>var vows = require("vows"),
    load = require("../load"),
    assert = require("../assert");

var suite = vows.describe("selection.append");

suite.addBatch({
  "select(body)": {
    topic: load("selection/append").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body");
      },
      "appends an HTML element": function(body) {
        var div = body.append("div");
        assert.equal(div[0][0].tagName, "DIV");
        assert.isNull(div[0][0].namespaceURI);
        assert.isTrue(div[0][0].parentNode === body.node());
        assert.isTrue(div[0][0] === body.node().lastChild);
      },
      "appends an SVG element": function(body) {
        var svg = body.append("svg:svg");
        assert.equal(svg[0][0].tagName, "SVG");
        assert.equal(svg[0][0].namespaceURI, "http://www.w3.org/2000/svg");
        assert.isTrue(svg[0][0].parentNode === body.node());
        assert.isTrue(svg[0][0] === body.node().lastChild);
      },
      "appends an element specified as a function": function(body) {
        var svg = body.select("svg").remove().node();
        assert.isFalse(svg === body.node().lastChild);
        body.append(function() { return svg; });
        assert.isTrue(svg === body.node().lastChild);
      },
      "propagates data to new element": function(body) {
        var data = new Object(), div = body.data([data]).append("div");
        assert.strictEqual(div[0][0].__data__, data);
      },
      "returns a new selection": function(body) {
        assert.isFalse(body.append("div") === body);
      },
      "inherits namespace from parent node": function(body) {
        var g = body.append("svg:svg").append("g");
        assert.equal(g[0][0].namespaceURI, "http://www.w3.org/2000/svg");
      }
    }
  }
});

suite.addBatch({
  "selectAll(div)": {
    topic: load("selection/selection").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body").selectAll("div").data([0, 1]).enter().append("div");
      },
      "appends an HTML element": function(div) {
        var span = div.append("span");
        assert.equal(span[0].length, 2);
        assert.equal(span[0][0].tagName, "SPAN");
        assert.equal(span[0][1].tagName, "SPAN");
        assert.isNull(span[0][0].namespaceURI);
        assert.isNull(span[0][1].namespaceURI);
        assert.isTrue(span[0][0].parentNode === div[0][0]);
        assert.isTrue(span[0][1].parentNode === div[0][1]);
        assert.isTrue(div[0][0].lastChild === span[0][0]);
        assert.isTrue(div[0][1].lastChild === span[0][1]);
      },
      "appends an SVG element": function(div) {
        var svg = div.append("svg:svg");
        assert.equal(svg[0].length, 2);
        assert.equal(svg[0][0].tagName, "SVG");
        assert.equal(svg[0][1].tagName, "SVG");
        assert.equal(svg[0][0].namespaceURI, "http://www.w3.org/2000/svg");
        assert.equal(svg[0][1].namespaceURI, "http://www.w3.org/2000/svg");
        assert.isTrue(svg[0][0].parentNode === div[0][0]);
        assert.isTrue(svg[0][1].parentNode === div[0][1]);
        assert.isTrue(div[0][0].lastChild === svg[0][0]);
        assert.isTrue(div[0][1].lastChild === svg[0][1]);
      },
      "propagates data to new elements": function(div) {
        var a = new Object(), b = new Object(), span = div.data([a, b]).append("span");
        assert.strictEqual(span[0][0].__data__, a);
        assert.strictEqual(span[0][1].__data__, b);
      },
      "returns a new selection": function(div) {
        assert.isFalse(div.append("div") === div);
      },
      "ignores null nodes": function(div) {
        var node = div.html("")[0][1];
        div[0][1] = null;
        var span = div.append("span");
        assert.equal(span[0].length, 2);
        assert.equal(span[0][0].tagName, "SPAN");
        assert.isNull(span[0][1]);
        assert.isTrue(span[0][0].parentNode === div[0][0]);
        assert.isTrue(div[0][0].lastChild === span[0][0]);
        assert.isNull(node.lastChild);
      }
    }
  }
});

suite.addBatch({
  "selectAll(div).data(…).enter()": {
    topic: load("selection/selection").document(),
    "on a simple page": {
      topic: function(d3) {
        return d3.select("body");
      },
      "appends to the parent node": function(body) {
        var div = body.selectAll("div").data([0, 1]).enter().append("div");
        assert.equal(div.length, 1);
        assert.equal(div[0].length, 2);
        assert.domEqual(div[0][0].parentNode, body.node());
        assert.domEqual(div[0][1].parentNode, body.node());
      },
      "propagates data to new elements": function(body) {
        var a = new Object(), b = new Object(), div = body.html("").selectAll("div").data([a, b]).enter().append("div");
        assert.strictEqual(div[0][0].__data__, a);
        assert.strictEqual(div[0][1].__data__, b);
      },
      "ignores null nodes": function(body) {
        body.html("").append("div");
        var div = body.selectAll("div").data([0, 1, 2]).enter().append("div");
        assert.equal(div.length, 1);
        assert.equal(div[0].length, 3);
        assert.domNull(div[0][0]);
        assert.domEqual(div[0][1].parentNode, body.node());
        assert.domEqual(div[0][2].parentNode, body.node());
      }
    }
  }
});

suite.export(module);
<|endoftext|>