<commit_before>var express = require('express')
var router  = express.Router()
var path    = require('path')
var debug   = require('debug')('app:' + path.basename(__filename).replace('.js', ''))
var async   = require('async')
var op      = require('object-path')
var mapper  = require('../helpers/mapper')
// var helper  = require('../helpers/helper')

function renderEcho(res, currentEchoId) {
    debug('Loading "' + path.basename(__filename).replace('.js', '') + '"')

    var echoA = {}
    var echoFA = {}
    var minDate = false
    var maxDate = false
    var latestEchoId
    async.each(SDC.get(['local_entities', 'by_class', 'echo']), function(entity, callback) {
        var echo = mapper.echo(entity.id)
        if (!echo.date) { return callback() }

        if (maxDate === false || new Date(echo.date) > maxDate) {
            maxDate = new Date(echo.date)
            latestEchoId = echo.id
        }
        if (minDate === false || new Date(echo.date) < minDate) { minDate = new Date(echo.date) }

        if (echo.featured) {
            op.push(echoFA, [echo.year, echo.month, echo.day], echo)
        }
        else {
            op.push(echoA, [echo.year, echo.month, echo.day], echo)
        }
        callback()
    }, function(err) {
        if (err) {
            console.log('Failed to render echo.', err)
            return
        }

        var echoCategories = Object.keys(SDC.get(['local_entities', 'by_class', 'echoCategory'], {}))
        .map(function(eId) {
            var mappedCategory = mapper.category(eId)
            eId = String(eId)
            mappedCategory.checked = false
            var echoCatIds = SDC.get(['relationships', currentEchoId, 'category'], [])
            // debug(echoCatIds, echoCatIds.indexOf(eId), eId)
            if (echoCatIds.indexOf(eId) > -1) {
                mappedCategory.checked = true
            }
            return mappedCategory
        })

        if(!currentEchoId) {
            res.render('magazine-article', {
                'allEchos': {feature: echoFA, others: echoA},
                'echoCategories': echoCategories,
                'minDate': minDate,
                'maxDate': maxDate
            })
        }
        else {
            res.render('magazine', {
                'echo': mapper.echo(currentEchoId),
                'echoArray': echoA,
                'echoCategories': echoCategories,
                'minDate': minDate,
                'maxDate': maxDate
            })
        }
        res.end()
    })
}

router
    .get('/', function(req, res) {
        renderEcho(res)
        res.end()
    })
    .get('/:id', function(req, res) {
        debug('Requested "' + req.url + '"' + JSON.stringify(req.params, null, 2))
        renderEcho(res, req.params.id)
    })

module.exports = router
<commit_msg>fix magazine<commit_after>var express = require('express')
var router  = express.Router()
var path    = require('path')
var debug   = require('debug')('app:' + path.basename(__filename).replace('.js', ''))
var async   = require('async')
var op      = require('object-path')
var mapper  = require('../helpers/mapper')
// var helper  = require('../helpers/helper')

function renderEcho(res, currentEchoId) {
    debug('Loading "' + path.basename(__filename).replace('.js', '') + '"')

    var echoA = {}
    var echoFA = {}
    var minDate = false
    var maxDate = false
    var latestEchoId
    async.each(SDC.get(['local_entities', 'by_class', 'echo']), function(entity, callback) {
        var echo = mapper.echo(entity.id)
        if (!echo.date) { return callback() }

        if (maxDate === false || new Date(echo.date) > maxDate) {
            maxDate = new Date(echo.date)
            latestEchoId = echo.id
        }
        if (minDate === false || new Date(echo.date) < minDate) { minDate = new Date(echo.date) }

        if (echo.featured) {
            op.push(echoFA, [echo.year, echo.month, echo.day], echo)
        }
        else {
            op.push(echoA, [echo.year, echo.month, echo.day], echo)
        }
        callback()
    }, function(err) {
        if (err) {
            console.log('Failed to render echo.', err)
            return
        }

        var echoCategories = Object.keys(SDC.get(['local_entities', 'by_class', 'echoCategory'], {}))
        .map(function(eId) {
            var mappedCategory = mapper.category(eId)
            eId = String(eId)
            mappedCategory.checked = false
            var echoCatIds = SDC.get(['relationships', currentEchoId, 'category'], [])
            // debug(echoCatIds, echoCatIds.indexOf(eId), eId)
            if (echoCatIds.indexOf(eId) > -1) {
                mappedCategory.checked = true
            }
            return mappedCategory
        })

        if(!currentEchoId) {
            res.render('magazine-article', {
                'allEchos': {feature: echoFA, others: echoA},
                'echoCategories': echoCategories,
                'minDate': minDate,
                'maxDate': maxDate
            })
        }
        else {
            res.render('magazine', {
                'echo': mapper.echo(currentEchoId),
                'echoArray': {feature: echoFA, others: echoA},
                'echoCategories': echoCategories,
                'minDate': minDate,
                'maxDate': maxDate
            })
        }
        res.end()
    })
}

router
    .get('/', function(req, res) {
        renderEcho(res)
        res.end()
    })
    .get('/:id', function(req, res) {
        debug('Requested "' + req.url + '"' + JSON.stringify(req.params, null, 2))
        renderEcho(res, req.params.id)
    })

module.exports = router
<|endoftext|>


<commit_before>/*
 * gulpfile.js
 *  Gulp task runner configuration and definitions
 *
 *  Type 'gulp' to see a list (and maybe even a small description) of each task
 *  available to you.
 *
 */

// Included plugins
var gulp = require('gulp'),
  livereload = require('gulp-livereload'),
  nodemon = require('gulp-nodemon'),
  gutil = require('gulp-util'),
  del = require('del'),
  path = require('path'),
  browserify = require('browserify'),
  reactify = require('reactify'),
  source = require('vinyl-source-stream'),
  server = require('./server'),
  isDebug = (process.env.NODE_ENV !== 'production'),
  vendorLibs = [
    'react',
    'react-bootstrap',
    'async',
    'domready',
    'request',
    'masonry-layout'
  ];

var reactifyES6 = function (file) {
  return reactify(file, {harmony: true});
};

gulp.task('dev', ['watch', 'build', 'server']);
gulp.task('build', ['vendor-js', 'app']);

gulp.task('clean', function () {
  del([
    './public/build/**/*.js',
    './public/build/**/*.css'
  ]);
});

gulp.task('vendor-js', function () {
  return browserify({debug:isDebug})
    .transform(reactifyES6)
    .require(vendorLibs)
    .bundle()
    .on('error', logAndEndStream)
    .pipe(source('vendor.js'))
    .pipe(gulp.dest('public/build/js'));
});

gulp.task('app', function () {
  return browserify({
    entries: ['./app/App.js'],
    debug: isDebug
  }).transform(reactifyES6)
    .bundle()
    .on('error', logAndEndStream)
    .pipe(source('app.js'))
    .pipe(gulp.dest('public/build/js'))
    .pipe(livereload({auto: false}));
});

gulp.task('watch', function () {
  livereload.listen();
  gulp.watch([
    './app/components/**/*.js',
    './app/services/**/*.js',
    './app/App.js'
  ], ['app']);
});

gulp.task('server', function () {

  nodemon({ script: 'server.js', ext: 'html js', env : { 'NODE_ENV': 'development' } })
   .on('restart', function () {
   });
});

function logAndEndStream(err) {
  gutil.log(err.stack);
  this.end();
}
<commit_msg>Faster gulp file<commit_after>/*
 * gulpfile.js
 *  Gulp task runner configuration and definitions
 *
 *  Type 'gulp' to see a list (and maybe even a small description) of each task
 *  available to you.
 *
 */

// Included plugins
var gulp = require('gulp'),
  livereload = require('gulp-livereload'),
  nodemon = require('gulp-nodemon'),
  gutil = require('gulp-util'),
  del = require('del'),
  path = require('path'),
  browserify = require('browserify'),
  reactify = require('reactify'),
  source = require('vinyl-source-stream'),
  server = require('./server'),
  isDebug = (process.env.NODE_ENV !== 'production'),
  vendorLibs = [
    'react/addons',
    'react-bootstrap',
    'async',
    'domready',
    'request',
    'masonry-layout'
  ];

var reactifyES6 = function (file) {
  return reactify(file, {harmony: true});
};

gulp.task('dev', ['watch', 'build', 'server']);
gulp.task('build', ['vendor-js', 'app']);

gulp.task('clean', function () {
  del([
    './public/build/**/*.js',
    './public/build/**/*.css'
  ]);
});

gulp.task('vendor-js', function () {
  return browserify({debug:isDebug})
    .transform(reactifyES6)
    .require(vendorLibs)
    .bundle()
    .on('error', logAndEndStream)
    .pipe(source('vendor.js'))
    .pipe(gulp.dest('public/build/js'));
});

gulp.task('app', function () {
  return browserify({
    entries: ['./app/App.js'],
    debug: isDebug
  }).transform(reactifyES6)
    .external(vendorLibs)
    .bundle()
    .on('error', logAndEndStream)
    .pipe(source('app.js'))
    .pipe(gulp.dest('public/build/js'))
    .pipe(livereload({auto: false}));
});

gulp.task('watch', function () {
  livereload.listen();
  gulp.watch([
    './app/components/**/*.js',
    './app/services/**/*.js',
    './app/App.js'
  ], ['app']);
});

gulp.task('server', function () {

  nodemon({ script: 'server.js', ext: 'html js', env : { 'NODE_ENV': 'development' } })
   .on('restart', function () {
   });
});

function logAndEndStream(err) {
  gutil.log(err.stack);
  this.end();
}
<|endoftext|>


<commit_before>export const GITHUB_API_BASE = 'https://api.github.com';
export const ORG_NAME = 'willowtreeapps';
export const DASHBOARD_NAME = 'Salix Web Repositories';
export const SELECTED_REPOS = [
    {
        user: 'willowtreeapps',
        name: 'salix-web',
        mergeableCriteria: ['cbc approved', 'lionsgate approved']
    },
    {
        user: 'willowtreeapps',
        name: 'salix-css',
        mergeableCriteria: ['cbc approved', 'lionsgate approved']
    },
    {
        user: 'willowtreeapps',
        name: 'lionsgate-web',
        mergeableCriteria: ['approved']
    }
];
<commit_msg>remove old constants<commit_after>export const GITHUB_API_BASE = 'https://api.github.com';
<|endoftext|>


<commit_before>/**
 * Products module, contains all the views and code related to products.
 * @module
 */

var express = require('express');
var elasticsearch = require('elasticsearch');
var router = express.Router();
var common = require('../common');
var client = common.createClient();
var esErrors = elasticsearch.errors;
var productsPath = '/products';
var moment = require('moment');

router.use(common.isAuthenticated);
router.use(function (request, response, next) {
  // everything inside this file is under the active view 'products'
  response.locals.isProductsActive = true;
  next();
});

/**
 * Helper function to build the url for a single product based route.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {string} route the base route path.
 * @param {string} [productId] the product id, which will be extracted from the request if not provided.
 */
function getProductUrl(req, route, productId) {
    var pid = typeof productId == 'undefined' ? req.params.id : productId;
    return getProductsUrl(req, pid + '/' + route);
}

/**
 * Helper function to build the url for a product based route.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {string} route the base route path.
 */
function getProductsUrl(req, route) {
    return req.protocol + "://" + req.get('host') + productsPath + '/' + route;
}

/**
 * Parses the elasticsearch response and returns an array of objects that represent the products.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {object} resp the elasticsearch response.
 */
function processElasticsearchResults(req, resp) {

    function getField(hit, field, field_type) {
        if (hit.highlight && hit.highlight[field + '.' + field_type])
            return hit.highlight[field + '.' + field_type][0];

        return hit._source[field];
    }

    var hits = resp.hits.hits;
    var aggregations = resp.aggregations.prods.buckets;

    var lookup = {};
    for (var i = 0; i < hits.length; i++) {
        lookup[hits[i]._id] = hits[i];
    }

    var results = [];
    var obj;
    var j;
    var objects;

    for (i = 0; i < aggregations.length; i++) {
        objects = [];
        for (j = 0; j < aggregations[i].hits.hits.hits.length; j++) {
            obj = lookup[aggregations[i].hits.hits.hits[j]._id];

            objects[objects.length] = {
                urlDelete: getProductUrl(req, 'delete', obj._id),
                deleteText: req.i18n.__('Delete product'),
                urlEdit: getProductUrl(req, 'edit', obj._id),
                date: obj._source.sold_date ? common.toLocalFormattedDate(req, obj._source.sold_date) : '-',
                notes: obj._source.notes ? obj._source.notes : '-'
            };
        }

        obj = lookup[aggregations[i].hits.hits.hits[0]._id];
        results[results.length] = {
            urlClone: getProductUrl(req, 'clone', obj._id),
            cloneText: req.i18n.__('Add another'),
            name: getField(obj, 'name', 'autocomplete'),
            brand: getField(obj, 'brand', 'autocomplete'),
            count: aggregations[i].doc_count,
            headerDate: req.i18n.__('Sold date'),
            headerNotes: req.i18n.__('Notes'),
            objects: objects
        };
    }

    return results;
}

/**
 * The elasticsearch aggregation logic for products. To maintain good performance, it
 * contains the assumption that a product (name + brand) cannot be sold
 * more than 10000 times.
 * @var
 */
var aggregate_product = {
    prods: {
      terms: {
        field: 'complete_name'
      },
      aggs: {
        hits: {
          top_hits: {
            size: 10000,
            _source: {
              include: "_id"
            },
            sort: {
                created_at: { order: "asc" }
            }
          }
        }
      }
    }
};

router.get('/', common.exposeTemplates, function(req, res, next) {
    client.search({
        index: req.config.mainIndex,
        type: 'product',
        size: 50,
        body: {
            query: {
                match_all: {}
            },
            aggs: aggregate_product
        }
    }, function(err, resp, respcode) {
        res.render('products', {
            i18n: {
                title: req.i18n.__('Products'),
                addNewProduct: req.i18n.__('Add product'),
                search: req.i18n.__('Search...'),
                btnConfirm: req.i18n.__('Confirm'),
                btnCancel: req.i18n.__('Cancel'),
                deleteTitle: req.i18n.__('Delete the product?'),
                deleteMsg: req.i18n.__('The operation cannot be undone. Continue?')
            },
            productsData: {
                headerName: req.i18n.__('Name'),
                headerBrand: req.i18n.__('Brand'),
                headerCount: req.i18n.__('Sold'),
                emptyMsg: req.i18n.__('No products to display.'),
                products: processElasticsearchResults(req, resp)
            },
            urlNew: getProductsUrl(req, 'new'),
            urlSearch: getProductsUrl(req, 'search')
        });
    });
});

router.get('/search', function(req, res, next) {
    var queryBody;
    if (req.query.text.trim()) {
        queryBody = {
            query: {
                multi_match: {
                    query: req.query.text,
                    operator: 'and',
                    type: 'cross_fields',
                    fields: [
                        "name.autocomplete",
                        "brand.autocomplete"
                    ]
                }
            },
            highlight: {
                fields: {
                    '*': {
                        'pre_tags': ['<b>'],
                        'post_tags': ['</b>']
                    }
                }
            },
            aggs: aggregate_product
        };
    }
    else {
        queryBody = {
            query: {
                match_all: {}
            },
            aggs: aggregate_product
        };
    }

    client.search({
        index: req.config.mainIndex,
        type: 'product',
        size: 50,
        body: queryBody
    }, function(err, resp, respcode) {
        res.json({
            headerName: req.i18n.__('Name'),
            headerBrand: req.i18n.__('Brand'),
            headerCount: req.i18n.__('Sold'),
            emptyMsg: req.i18n.__('No products to display.'),
            products: processElasticsearchResults(req, resp)
        });
    });
});

/**
 * Creates a new ProductUtils object, which encapsulates some common utility
 * functions and properties to handle the Product form and the related documents
 * on elasticsearch.
 * @class ProductUtils
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {object} res the {@link http://expressjs.com/4x/api.html#res|response object}.
 */
var ProductUtils = function(req, res) {
    this.req = req;
    this.res = res;
};

/**
 * The fields of the Product form.
 * @var
 */
ProductUtils.formFields = ['name', 'brand', 'sold_date', 'notes'];

/**
 * Transforms a local formatted date to the iso format.
 * @method
 *
 * @param {string} localFormattedDate the local formatted date.
 */
ProductUtils.prototype.toISODate = function(localFormattedDate) {
    return common.toISODate(this.req, localFormattedDate);
};

/**
 * Transforms an iso format date to the local format defined in the configuration.
 * @method
 *
 * @param {string} ISODate the iso date.
 */
ProductUtils.prototype.toLocalFormattedDate = function(ISODate) {
    return common.toLocalFormattedDate(this.req, ISODate);
};

/**
 * Returns an object which maps the form fields and buttons of the Product form
 * with the related translated names.
 * @method
 *
 * @param {bool} editForm true if the form is for edit.
 */
ProductUtils.prototype.formNames = function(editForm) {
    return {
        name: this.req.i18n.__('Name'),
        brand: this.req.i18n.__('Brand'),
        sold_date: this.req.i18n.__('Sold date'),
        notes: this.req.i18n.__('Notes'),
        submit: editForm ? this.req.i18n.__('Edit product') : this.req.i18n.__('Add product')
    };
};

/**
 * Converts the source object to the format used to save the related document
 * on elasticsearch.
 * @method
 *
 * @param {object} sourceObj the source object originated from the Product form.
 */
ProductUtils.prototype.toElasticsearchFormat = function(sourceObj) {
    var obj = {};
    for (var i = 0; i < ProductUtils.formFields.length; i++) {
        var field = ProductUtils.formFields[i];
        if (sourceObj[field]) {
            if (field == 'sold_date')
                obj[field] = this.toISODate(sourceObj[field]);
            else
                obj[field] = sourceObj[field];
        }
    }

    obj.complete_name = obj.name;
    if (obj.brand) {
        obj.complete_name += obj.brand;
    }

    return obj;
};

/**
 * Converts the source object to the format used to present the data in the
 * Product form.
 * @method
 *
 * @param {object} sourceObj the source object originated from the elasticsearch response.
 */
ProductUtils.prototype.toViewFormat = function(sourceObj) {
    var obj = {};
    for (var i = 0; i < ProductUtils.formFields.length; i++) {
        var field = ProductUtils.formFields[i];
        if (sourceObj[field]) {
            if (field == 'sold_date')
                obj[field] = this.toLocalFormattedDate(sourceObj[field]);
            else
                obj[field] = sourceObj[field];
        }
    }
    return obj;
};

/**
 * Handles the Product form, creating / updating a new document if the form
 * content validation passes, or displaying the proper error messages if not.
 * @method
 *
 * @param {string} title the title of the form.
 * @param {bool} editForm true if the form is for edit.
 */
ProductUtils.prototype.handleForm = function(title, editForm) {
    // Trim all the fields that allow the user to write text
    for (var i = 0; i < ProductUtils.formFields.length; i++)
        this.req.sanitize(ProductUtils.formFields[i]).trim();

    this.req.checkBody('name', this.req.i18n.__('The name is mandatory')).notEmpty();

    if (this.req.body.first_see) {
        this.req.checkBody('sold_date', this.req.i18n.__(
            'The sold date does not seem a valid date')).isValidDate();
    }

    var errors = this.req.validationErrors();
    if (errors) {
        var i18n = this.formNames(editForm);
        i18n.title = title;

        this.res.render('product', {
            i18n: i18n,
            flash: { type: 'alert-danger', messages: errors},
            obj: this.req.body
        });
        return;
    }

    var args = {
        index: this.req.config.mainIndex,
        type: 'product',
        refresh: true,
    };

    var obj = this.toElasticsearchFormat(this.req.body);

    var that = this;  // workaround for the this visibility problem inside inner functions.
    var cb = function(err, resp, respcode) {
        if (!err) {
            // redirect does not take into account being in inside a router
            that.res.redirect(productsPath);
        }
        else {
            var messages;
            if (err instanceof esErrors.NoConnections)
                messages = [{msg: that.req.i18n.__('Database connection error')}];
            else
                messages = [{msg: that.req.i18n.__('Database error')}];
            console.error(err);

            var i18n = that.formNames(editForm);
            i18n.title = title;

            that.res.render('product', {
                i18n: i18n,
                flash: { type: 'alert-danger', messages: messages},
                obj: that.req.body
            });
        }
    };

    if (typeof this.req.params.id != 'undefined') {
        args.id = this.req.params.id;
        args.body = {doc: obj};
        client.update(args, cb);
    }
    else {
        args.body = obj;
        args.body.created_at = new Date().toISOString();
        client.index(args, cb);
    }
};

router.use(['/new', '*clone', '*edit'], function (req, res, next) {
    req.utils = new ProductUtils(req, res);
    next();
});

router.get('/new', function(req, res, next) {
    var i18n = req.utils.formNames(false);
    i18n.title = req.i18n.__('Add product');
    res.render('product', {
        i18n: i18n,
        obj: {
            sold_date: common.toLocalFormattedDate(req, moment()),
        }
    });
});

router.post('/new', function(req, res, next) {
    req.utils.handleForm(req.i18n.__('Add product'), false);
});

router.get('/:id/clone', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        var i18n = req.utils.formNames(false);
        i18n.title = req.i18n.__('Add product');
        res.render('product', {
            i18n: i18n,
            obj: {
                name: resp._source.name,
                brand: resp._source.brand
            },
            actionUrl: getProductsUrl(req, 'new')
        });
    });
});

router.get('/:id/edit', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        var i18n = req.utils.formNames(true);
        i18n.title = req.i18n.__('Edit product');

        res.render('product', {
            i18n: i18n,
            obj: req.utils.toViewFormat(resp._source)
        });
    });
});

router.post('/:id/edit', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        req.utils.handleForm(req.i18n.__('Edit product'), true);
    });
});

router.post('/:id/delete', function(req, res, next) {
    client.delete({
        index: req.config.mainIndex,
        type: 'product',
        refresh: true,
        id: req.params.id
    }, function(err, resp, respcode) {
        if (err) {
            console.log(err);
            res.status(400).end();
        }
        else {
            res.status(200).end();
        }
    });
});

/** The products router. */
module.exports.router = router;
/** The product routes base path. */
module.exports.path = productsPath;
<commit_msg>Use today as a default date when cloning a product.<commit_after>/**
 * Products module, contains all the views and code related to products.
 * @module
 */

var express = require('express');
var elasticsearch = require('elasticsearch');
var router = express.Router();
var common = require('../common');
var client = common.createClient();
var esErrors = elasticsearch.errors;
var productsPath = '/products';
var moment = require('moment');

router.use(common.isAuthenticated);
router.use(function (request, response, next) {
  // everything inside this file is under the active view 'products'
  response.locals.isProductsActive = true;
  next();
});

/**
 * Helper function to build the url for a single product based route.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {string} route the base route path.
 * @param {string} [productId] the product id, which will be extracted from the request if not provided.
 */
function getProductUrl(req, route, productId) {
    var pid = typeof productId == 'undefined' ? req.params.id : productId;
    return getProductsUrl(req, pid + '/' + route);
}

/**
 * Helper function to build the url for a product based route.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {string} route the base route path.
 */
function getProductsUrl(req, route) {
    return req.protocol + "://" + req.get('host') + productsPath + '/' + route;
}

/**
 * Parses the elasticsearch response and returns an array of objects that represent the products.
 * @function
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {object} resp the elasticsearch response.
 */
function processElasticsearchResults(req, resp) {

    function getField(hit, field, field_type) {
        if (hit.highlight && hit.highlight[field + '.' + field_type])
            return hit.highlight[field + '.' + field_type][0];

        return hit._source[field];
    }

    var hits = resp.hits.hits;
    var aggregations = resp.aggregations.prods.buckets;

    var lookup = {};
    for (var i = 0; i < hits.length; i++) {
        lookup[hits[i]._id] = hits[i];
    }

    var results = [];
    var obj;
    var j;
    var objects;

    for (i = 0; i < aggregations.length; i++) {
        objects = [];
        for (j = 0; j < aggregations[i].hits.hits.hits.length; j++) {
            obj = lookup[aggregations[i].hits.hits.hits[j]._id];

            objects[objects.length] = {
                urlDelete: getProductUrl(req, 'delete', obj._id),
                deleteText: req.i18n.__('Delete product'),
                urlEdit: getProductUrl(req, 'edit', obj._id),
                date: obj._source.sold_date ? common.toLocalFormattedDate(req, obj._source.sold_date) : '-',
                notes: obj._source.notes ? obj._source.notes : '-'
            };
        }

        obj = lookup[aggregations[i].hits.hits.hits[0]._id];
        results[results.length] = {
            urlClone: getProductUrl(req, 'clone', obj._id),
            cloneText: req.i18n.__('Add another'),
            name: getField(obj, 'name', 'autocomplete'),
            brand: getField(obj, 'brand', 'autocomplete'),
            count: aggregations[i].doc_count,
            headerDate: req.i18n.__('Sold date'),
            headerNotes: req.i18n.__('Notes'),
            objects: objects
        };
    }

    return results;
}

/**
 * The elasticsearch aggregation logic for products. To maintain good performance, it
 * contains the assumption that a product (name + brand) cannot be sold
 * more than 10000 times.
 * @var
 */
var aggregate_product = {
    prods: {
      terms: {
        field: 'complete_name'
      },
      aggs: {
        hits: {
          top_hits: {
            size: 10000,
            _source: {
              include: "_id"
            },
            sort: {
                created_at: { order: "asc" }
            }
          }
        }
      }
    }
};

router.get('/', common.exposeTemplates, function(req, res, next) {
    client.search({
        index: req.config.mainIndex,
        type: 'product',
        size: 50,
        body: {
            query: {
                match_all: {}
            },
            aggs: aggregate_product
        }
    }, function(err, resp, respcode) {
        res.render('products', {
            i18n: {
                title: req.i18n.__('Products'),
                addNewProduct: req.i18n.__('Add product'),
                search: req.i18n.__('Search...'),
                btnConfirm: req.i18n.__('Confirm'),
                btnCancel: req.i18n.__('Cancel'),
                deleteTitle: req.i18n.__('Delete the product?'),
                deleteMsg: req.i18n.__('The operation cannot be undone. Continue?')
            },
            productsData: {
                headerName: req.i18n.__('Name'),
                headerBrand: req.i18n.__('Brand'),
                headerCount: req.i18n.__('Sold'),
                emptyMsg: req.i18n.__('No products to display.'),
                products: processElasticsearchResults(req, resp)
            },
            urlNew: getProductsUrl(req, 'new'),
            urlSearch: getProductsUrl(req, 'search')
        });
    });
});

router.get('/search', function(req, res, next) {
    var queryBody;
    if (req.query.text.trim()) {
        queryBody = {
            query: {
                multi_match: {
                    query: req.query.text,
                    operator: 'and',
                    type: 'cross_fields',
                    fields: [
                        "name.autocomplete",
                        "brand.autocomplete"
                    ]
                }
            },
            highlight: {
                fields: {
                    '*': {
                        'pre_tags': ['<b>'],
                        'post_tags': ['</b>']
                    }
                }
            },
            aggs: aggregate_product
        };
    }
    else {
        queryBody = {
            query: {
                match_all: {}
            },
            aggs: aggregate_product
        };
    }

    client.search({
        index: req.config.mainIndex,
        type: 'product',
        size: 50,
        body: queryBody
    }, function(err, resp, respcode) {
        res.json({
            headerName: req.i18n.__('Name'),
            headerBrand: req.i18n.__('Brand'),
            headerCount: req.i18n.__('Sold'),
            emptyMsg: req.i18n.__('No products to display.'),
            products: processElasticsearchResults(req, resp)
        });
    });
});

/**
 * Creates a new ProductUtils object, which encapsulates some common utility
 * functions and properties to handle the Product form and the related documents
 * on elasticsearch.
 * @class ProductUtils
 *
 * @param {object} req the current {@link http://expressjs.com/4x/api.html#req|request object}.
 * @param {object} res the {@link http://expressjs.com/4x/api.html#res|response object}.
 */
var ProductUtils = function(req, res) {
    this.req = req;
    this.res = res;
};

/**
 * The fields of the Product form.
 * @var
 */
ProductUtils.formFields = ['name', 'brand', 'sold_date', 'notes'];

/**
 * Transforms a local formatted date to the iso format.
 * @method
 *
 * @param {string} localFormattedDate the local formatted date.
 */
ProductUtils.prototype.toISODate = function(localFormattedDate) {
    return common.toISODate(this.req, localFormattedDate);
};

/**
 * Transforms an iso format date to the local format defined in the configuration.
 * @method
 *
 * @param {string} ISODate the iso date.
 */
ProductUtils.prototype.toLocalFormattedDate = function(ISODate) {
    return common.toLocalFormattedDate(this.req, ISODate);
};

/**
 * Returns an object which maps the form fields and buttons of the Product form
 * with the related translated names.
 * @method
 *
 * @param {bool} editForm true if the form is for edit.
 */
ProductUtils.prototype.formNames = function(editForm) {
    return {
        name: this.req.i18n.__('Name'),
        brand: this.req.i18n.__('Brand'),
        sold_date: this.req.i18n.__('Sold date'),
        notes: this.req.i18n.__('Notes'),
        submit: editForm ? this.req.i18n.__('Edit product') : this.req.i18n.__('Add product')
    };
};

/**
 * Converts the source object to the format used to save the related document
 * on elasticsearch.
 * @method
 *
 * @param {object} sourceObj the source object originated from the Product form.
 */
ProductUtils.prototype.toElasticsearchFormat = function(sourceObj) {
    var obj = {};
    for (var i = 0; i < ProductUtils.formFields.length; i++) {
        var field = ProductUtils.formFields[i];
        if (sourceObj[field]) {
            if (field == 'sold_date')
                obj[field] = this.toISODate(sourceObj[field]);
            else
                obj[field] = sourceObj[field];
        }
    }

    obj.complete_name = obj.name;
    if (obj.brand) {
        obj.complete_name += obj.brand;
    }

    return obj;
};

/**
 * Converts the source object to the format used to present the data in the
 * Product form.
 * @method
 *
 * @param {object} sourceObj the source object originated from the elasticsearch response.
 */
ProductUtils.prototype.toViewFormat = function(sourceObj) {
    var obj = {};
    for (var i = 0; i < ProductUtils.formFields.length; i++) {
        var field = ProductUtils.formFields[i];
        if (sourceObj[field]) {
            if (field == 'sold_date')
                obj[field] = this.toLocalFormattedDate(sourceObj[field]);
            else
                obj[field] = sourceObj[field];
        }
    }
    return obj;
};

/**
 * Handles the Product form, creating / updating a new document if the form
 * content validation passes, or displaying the proper error messages if not.
 * @method
 *
 * @param {string} title the title of the form.
 * @param {bool} editForm true if the form is for edit.
 */
ProductUtils.prototype.handleForm = function(title, editForm) {
    // Trim all the fields that allow the user to write text
    for (var i = 0; i < ProductUtils.formFields.length; i++)
        this.req.sanitize(ProductUtils.formFields[i]).trim();

    this.req.checkBody('name', this.req.i18n.__('The name is mandatory')).notEmpty();

    if (this.req.body.first_see) {
        this.req.checkBody('sold_date', this.req.i18n.__(
            'The sold date does not seem a valid date')).isValidDate();
    }

    var errors = this.req.validationErrors();
    if (errors) {
        var i18n = this.formNames(editForm);
        i18n.title = title;

        this.res.render('product', {
            i18n: i18n,
            flash: { type: 'alert-danger', messages: errors},
            obj: this.req.body
        });
        return;
    }

    var args = {
        index: this.req.config.mainIndex,
        type: 'product',
        refresh: true,
    };

    var obj = this.toElasticsearchFormat(this.req.body);

    var that = this;  // workaround for the this visibility problem inside inner functions.
    var cb = function(err, resp, respcode) {
        if (!err) {
            // redirect does not take into account being in inside a router
            that.res.redirect(productsPath);
        }
        else {
            var messages;
            if (err instanceof esErrors.NoConnections)
                messages = [{msg: that.req.i18n.__('Database connection error')}];
            else
                messages = [{msg: that.req.i18n.__('Database error')}];
            console.error(err);

            var i18n = that.formNames(editForm);
            i18n.title = title;

            that.res.render('product', {
                i18n: i18n,
                flash: { type: 'alert-danger', messages: messages},
                obj: that.req.body
            });
        }
    };

    if (typeof this.req.params.id != 'undefined') {
        args.id = this.req.params.id;
        args.body = {doc: obj};
        client.update(args, cb);
    }
    else {
        args.body = obj;
        args.body.created_at = new Date().toISOString();
        client.index(args, cb);
    }
};

router.use(['/new', '*clone', '*edit'], function (req, res, next) {
    req.utils = new ProductUtils(req, res);
    next();
});

router.get('/new', function(req, res, next) {
    var i18n = req.utils.formNames(false);
    i18n.title = req.i18n.__('Add product');
    res.render('product', {
        i18n: i18n,
        obj: {
            sold_date: common.toLocalFormattedDate(req, moment()),
        }
    });
});

router.post('/new', function(req, res, next) {
    req.utils.handleForm(req.i18n.__('Add product'), false);
});

router.get('/:id/clone', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        var i18n = req.utils.formNames(false);
        i18n.title = req.i18n.__('Add product');
        res.render('product', {
            i18n: i18n,
            obj: {
                name: resp._source.name,
                brand: resp._source.brand,
                sold_date: common.toLocalFormattedDate(req, moment())
            },
            actionUrl: getProductsUrl(req, 'new')
        });
    });
});

router.get('/:id/edit', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        var i18n = req.utils.formNames(true);
        i18n.title = req.i18n.__('Edit product');

        res.render('product', {
            i18n: i18n,
            obj: req.utils.toViewFormat(resp._source)
        });
    });
});

router.post('/:id/edit', function(req, res, next) {
    client.get({
        index: req.config.mainIndex,
        type: 'product',
        id: req.params.id
    }, function(err, resp, respcode) {
        req.utils.handleForm(req.i18n.__('Edit product'), true);
    });
});

router.post('/:id/delete', function(req, res, next) {
    client.delete({
        index: req.config.mainIndex,
        type: 'product',
        refresh: true,
        id: req.params.id
    }, function(err, resp, respcode) {
        if (err) {
            console.log(err);
            res.status(400).end();
        }
        else {
            res.status(200).end();
        }
    });
});

/** The products router. */
module.exports.router = router;
/** The product routes base path. */
module.exports.path = productsPath;
<|endoftext|>


<commit_before>#!/usr/bin/env node

const fs = require( 'fs' );
const jpeg = require('jpeg-js');
const util = require('util');

const bresenham6d = require( '../../src/weak/Bresenham6d' );
const TriangularB6 = require( '../../src/weak/TriangularB6' );
const CanvasHack = require( '../common/CanvasHack' );

const TriangularB6Test = function() {
	const self = this;
	self.width = 1024;
	self.height = 1024;
	self.cache = {uid:33,points:{},midpoints:{},triangles:[]}

	self.random = function() {
		return Math.random();
		return 0.5;
	}

	self.main = function( args ) {
		let canvas = CanvasHack.create( self.width, self.height );
		let tb6 = new TriangularB6( canvas );
		tb6.clear();

		self.drawLameWater( tb6 );

		let q = 0.1;
		let u = 1;

		let f = 2.0;
		q *= f;
		u *= f;

		let point1 = self.vertex( -u, +q, +u,  0, 0, 1 );
		let point2 = self.vertex( +u, +q, +u,  1, 0, 0 );
		let point3 = self.vertex(  0, -q,  0,  0, 1, 0 );

		let texture = self.makeTexture( canvas );
		tb6.setTexture( texture );
		self.split( tb6, point1, point2, point3, 1 );

		// tried to draw water, without much luck...
		if ( false ) {
			tb6.setTexture( false );

			f = 2.0;
			q *= f;
			u *= f;

			point1 = self.vertex( -u, +q, -u,  0,0,255 );
			point2 = self.vertex( +u, +q, -u,  0,0,255 );
			point3 = self.vertex(  0, -q, 0,  0,0,255 );
			self.drawTriangle( tb6, point1, point2, point3 );
		}

		canvas.saveToJpg( jpeg, 'tb6.jpg' );
		console.log( 'ok' );
	};

	self.split = function( tb6, point1, point2, point3, level, theQ ) {
		let max = 1;
		max = 7;

		if ( level > max ) {
			self.drawTriangle( tb6, point1, point2, point3 );
		} else {
			if ( self.random() < -0.10 ) {
				self.triangleSplit1( tb6, point1, point2, point3, level, theQ );
			} else {
				self.triangleSplit2( tb6, point1, point2, point3, level, theQ );
			}
		}
	}

	/**
	 *
	 * this takes the midpoint on the triangle and moves it up or down
	 *
	 *       ^                     ^
	 *      / \                   /|\
	 *     /   \                 / | \
	 *    /     \      --->     /  |  \
	 *   /       \             /   x   \
	 *  /         \           / .'   `. \
	 * `___________'         `/_________\'
	 *
	 */
	self.triangleSplit1 = function( tb6, point1, point2, point3, level, theQ ) {
		let midpoint = {
			  x: ( point1.x + point2.x + point3.x ) / 3
			, y: ( point1.y + point2.y + point3.y ) / 3
			, z: ( point1.z + point2.z + point3.z ) / 3
			, r: ( point1.r + point2.r + point3.r ) / 3
			, g: ( point1.g + point2.g + point3.g ) / 3
			, b: ( point1.b + point2.b + point3.b ) / 3
		}

		let normal = self.xyzzy( point1, point2, point3 );
		self.log( 'at level', level, 'normal is', normal );

		let qzar = 0.1 + 0.2 * self.random();
		qzar = 0.3 + 0.7 * self.random();
		if ( self.random() < 0.33 ) {
			qzar = -qzar;
		}

		let q = theQ || qzar;

		q /= ( level * level ); // exponential decay

		let nu = {
			  x: midpoint.x + normal.x * q
			, y: midpoint.y + normal.y * q
			, z: midpoint.z + normal.z * q
		};

		self.split( tb6, point1, point2, nu, level + 1, theQ );
		self.split( tb6, point2, point3, nu, level + 1, theQ );
		self.split( tb6, point3, point1, nu, level + 1, theQ );
	}

	/**
	 *
	 * this takes the midpoints of the edges and moves them up or down
	 *
	 *         1                      1
	 *         ^                      ^
	 *        / \                    / \
	 *       /   \                  / a \
	 *      /     \      --->    2 x-----x 0      0:midpt1
	 *     /       \              / \ d / \       1:midpt2
	 *    /         \            / c \ / b \      2:midpt3
	 *   `___________'          `_____x_____'
	 *  3             2        3      1      2
	 *
	 */
	self.triangleSplit2 = function( tb6, point1, point2, point3, level, theQ ) {
		let q = 0.8 / ( level * level );
		let points = [ point1, point2, point3 ];
		let midpoints = [];

		let froms = [];

		for ( let i = 0 ; i < points.length ; i++ ) {
			let point = points[ i ];
			let next = points[ ( i + 1 ) % points.length ];

			let id1 = point.id;
			let id2 = next.id;

			if ( id2 < id1 ) {
				let tmp = id1;
				id1 = id2;
				id2 = tmp;
			}

			// moved just for debugging...
			let from = self.from( point, next );
			froms.push( from );

			let key = id1 + 'x' + id2;
			if ( !false ) {
				if ( key in self.cache.midpoints ) {
					// this cache caused a lot of problems...
					midpoints.push( self.cache.midpoints[ key ] );
					continue;
				}
			}

			let x = point.x + from.x * 0.5;
			let y = point.y + from.y * 0.5;
			let z = point.z + from.z * 0.5;
			let r = point.r + from.r * 0.5;
			let g = point.g + from.g * 0.5;
			let b = point.b + from.b * 0.5;

			let yi = q * self.random() - q * self.random();

			// doesn't help...
			//if ( key in theQ ) yi = theQ[ key ]; else theQ[ key ] = yi;

			y += yi;

			// doesn't help...
			//if ( key in theQ ) y = theQ[ key ]; else theQ[ key ] = y;

			let midpoint = self.vertex( x,y,z, r,g, b );
			self.cache.midpoints[ key ] = midpoint;
			midpoints.push( midpoint );
		}

		let midpt1 = midpoints[ 0 ];
		let midpt2 = midpoints[ 1 ];
		let midpt3 = midpoints[ 2 ];
		self.split( tb6, point1, midpt1, midpt3, level + 1 ); // a
		self.split( tb6, midpt1, point2, midpt2, level + 1 ); // b
		self.split( tb6, midpt2, point3, midpt3, level + 1 ); // c
		self.split( tb6, midpt1, midpt2, midpt3, level + 1 ); // d
	};

	self.drawTriangle = function( tb6, point1, point2, point3 ) {
		let normal = self.xyzzy( point1, point2, point3 );

		let screened1 = self.toScreen( point1 );
		let screened2 = self.toScreen( point2 );
		let screened3 = self.toScreen( point3 );

		let pixelsDrawn = tb6.triangleDraw( screened1, screened2, screened3, normal );
	};

	self.vertex = function( x, y, z, r, g, b ) {
		let v = { x:x, y:y, z:z, r:r, g:g, b:b, id:self.cache.uid++ };
		self.cache.points[ v.id ] = v;
		return v;
	};

	self.toScreen = function( point ) {
		let screened = {
			  x: self.into( point.x, 1024 )
			, y: self.into( point.y, 1024 )
			, z: self.into( point.z, 1024 )
			, r: self.into( point.r, 256  )
			, g: self.into( point.g, 256  )
			, b: self.into( point.b, 256  )
		}

		return screened;
	};

	self.into = function( r, i ) {
		return Math.floor( i * 0.5  + i * 0.5 * r );
	};

	self.xyzzy = function( point1, point2, point3 ) {
		let a = self.normallyFrom( point2, point1 );
		let b = self.normallyFrom( point2, point3 );
		return self.normalize(
			{
				  x: ( a.y * b.z ) - ( a.z * b.y ) // x:yzzy
				, y: ( a.z * b.x ) - ( a.x * b.z ) // y:zxxz
				, z: ( a.x * b.y ) - ( a.y * b.x ) // z:xyyx
			}
		);
	};

	self.normallyFrom = function( start, stop ) {
		return self.normalize( self.from( start, stop ) );
	};

	self.from = function( start, stop ) {
		let from = {};
		for ( let key in start ) {
			if ( key in stop ) {
				from[ key ] = stop[ key ] - start[ key ];
			}
		}
		return from;
	};

	self.normalize = function( point ) {
		let s = ( point.x * point.x ) + ( point.y * point.y ) + ( point.z * point.z );
		if ( 0 === s ) {
			s = 1;
		} else {
			s = Math.sqrt( s );
		}

		return self.vertex( point.x / s, point.y / s, point.z / s );
	};

	self.makeTexture = function( canvas ) {
		if ( false ) {
			let texture = canvas.makeImageData(2,2);
			let i = 0;
			texture.data[i++] = 255; texture.data[i++] =   0; texture.data[i++] =   0; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] =   0; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] = 255; texture.data[i++] = 255;
			return texture;
		}

		let n = 256;
		let texture = canvas.makeImageData( n, n );

		let index = 0;
		let color_increment = 255 / n;
		let r = 0;
		let b = 128
		for ( let y = 0 ; y < n ; y++, r += color_increment ) {
			let g = 0;
			for ( let x = 0 ; x < n ; x++, g += color_increment ) {
				texture.data[ index++ ] = r;
				texture.data[ index++ ] = g;
				texture.data[ index++ ] = b;
				texture.data[ index++ ] = 255;
			}
		}

		if ( false ) {
			let rawImageData = { data:texture.data, width:n, height:n }
			let jpegImageData = jpeg.encode( rawImageData, 50 );
			fs.writeFile( 'texture.jpg', jpegImageData.data, function(){} );
		}

		return texture;
	}

	self.log = function() {
		let s = '';
		let q = '';
		for ( let i in arguments ) {
			s += q + self.appendix( arguments[ i ] );
			q = ' ';
		}
		console.log( s );
	};

	self.appendix = function( lulz ) {
		let s = '';

		if ( Number.isInteger( lulz ) ) {
			lulz += 0.00000000001; // we all float down here
		}

		let type = typeof( lulz );
		if ( util.isArray( lulz ) ) {
			type = 'array';
		}

		switch ( type ) {
			case 'object':
				s = '{';
				let luld = false;
				for ( let i in lulz ) {
					s += ( luld ? ', ' : ' ' ) +  i + ':' + self.appendix( lulz[ i ] );
					luld = true;

				}
				if ( luld ) s +=  ' ';
				s += '}';
				break;
			case 'array':
				s = '[';
				for ( let i = 0 ; i < lulz.length ; i++ ) {
					s += ( i ? ', ' : ' ' ) + self.appendix( lulz[ i ] );
				}
				if ( lulz.length ) s += ' ';
				s += ']';
				break;
			case 'number':
				if ( Number.isInteger( lulz ) ) {
					if ( lulz < 0 ) {
						return lulz;
					} else {
						return '+' + lulz;
					}
				}
				let precision = 100000;
				lulz = Math.floor( precision * lulz ) / precision;
				if ( lulz < 0 ) {
					s = '' + lulz;
				} else {
					s = '+' + lulz;
				}

				if ( -1 == s.indexOf( '.' ) ) {
					s += '.';
				}
				while ( s.length < 8 ) s += '0';

				break;
			case 'string':
				s = lulz;
				break;
			default:
				//console.log( 'idk:' + type );
				return lulz;
		};
		return s;
	}

	self.drawLameWater = function( tb6 ) {
		let i = 0;
		for ( let y = 0 ; y < self.height ; y++ ) {
			for ( let x = 0 ; x < self.width ; x++ ) {
				tb6.imageData.data[ i++ ] = 0;
				tb6.imageData.data[ i++ ] = y < self.height / 2 ? 255 : 0;
				tb6.imageData.data[ i++ ] = 255;
				tb6.imageData.data[ i++ ] = 255;
			}
		}
	}
};

new TriangularB6Test().main( process.argv.slice( 2 ) );
<commit_msg>cleanup<commit_after>#!/usr/bin/env node

const fs = require( 'fs' );
const jpeg = require('jpeg-js');
const util = require('util');

const bresenham6d = require( '../../src/weak/Bresenham6d' );
const TriangularB6 = require( '../../src/weak/TriangularB6' );
const CanvasHack = require( '../common/CanvasHack' );

const TriangularB6Test = function() {
	const self = this;
	self.width = 1024;
	self.height = 1024;

	self.cache = { uid:33, points:{}, midpoints:{} };

	self.random = function() {
		return Math.random();
		return 0.5;
	}

	self.main = function( args ) {
		let canvas = CanvasHack.create( self.width, self.height );
		let tb6 = new TriangularB6( canvas );
		tb6.clear();

		self.drawLameWater( tb6 );

		let q = 0.1;
		let u = 1;

		let f = 2.0;
		q *= f;
		u *= f;

		let point1 = self.vertex( -u, +q, +u,  0, 0, 1 );
		let point2 = self.vertex( +u, +q, +u,  1, 0, 0 );
		let point3 = self.vertex(  0, -q,  0,  0, 1, 0 );

		let texture = self.makeTexture( canvas );
		tb6.setTexture( texture );
		self.split( tb6, point1, point2, point3, 1 );

		canvas.saveToJpg( jpeg, 'tb6.jpg' );
		console.log( 'ok' );
	};

	self.split = function( tb6, point1, point2, point3, level, theQ ) {
		let max = 1;
		max = 8;

		if ( level > max ) {
			self.drawTriangle( tb6, point1, point2, point3 );
		} else {
			if ( self.random() < -0.10 ) {
				self.triangleSplit1( tb6, point1, point2, point3, level, theQ );
			} else {
				self.triangleSplit2( tb6, point1, point2, point3, level, theQ );
			}
		}
	}

	/**
	 *
	 * this takes the midpoint on the triangle and moves it up or down
	 *
	 *       ^                     ^
	 *      / \                   /|\
	 *     /   \                 / | \
	 *    /     \      --->     /  |  \
	 *   /       \             /   x   \
	 *  /         \           / .'   `. \
	 * `___________'         `/_________\'
	 *
	 */
	self.triangleSplit1 = function( tb6, point1, point2, point3, level, theQ ) {
		let midpoint = {
			  x: ( point1.x + point2.x + point3.x ) / 3
			, y: ( point1.y + point2.y + point3.y ) / 3
			, z: ( point1.z + point2.z + point3.z ) / 3
			, r: ( point1.r + point2.r + point3.r ) / 3
			, g: ( point1.g + point2.g + point3.g ) / 3
			, b: ( point1.b + point2.b + point3.b ) / 3
		}

		let normal = self.xyzzy( point1, point2, point3 );
		self.log( 'at level', level, 'normal is', normal );

		let qzar = 0.1 + 0.2 * self.random();
		qzar = 0.3 + 0.7 * self.random();
		if ( self.random() < 0.33 ) {
			qzar = -qzar;
		}

		let q = theQ || qzar;

		q /= ( level * level ); // exponential decay

		let nu = {
			  x: midpoint.x + normal.x * q
			, y: midpoint.y + normal.y * q
			, z: midpoint.z + normal.z * q
		};

		self.split( tb6, point1, point2, nu, level + 1, theQ );
		self.split( tb6, point2, point3, nu, level + 1, theQ );
		self.split( tb6, point3, point1, nu, level + 1, theQ );
	}

	/**
	 *
	 * this takes the midpoints of the edges and moves them up or down
	 *
	 *         1                      1
	 *         ^                      ^
	 *        / \                    / \
	 *       /   \                  / a \
	 *      /     \      --->    2 x-----x 0      0:midpt1
	 *     /       \              / \ d / \       1:midpt2
	 *    /         \            / c \ / b \      2:midpt3
	 *   `___________'          `_____x_____'
	 *  3             2        3      1      2
	 *
	 */
	self.triangleSplit2 = function( tb6, point1, point2, point3, level, theQ ) {
		let q = 0.8 / ( level * level );
		let points = [ point1, point2, point3 ];
		let midpoints = [];

		for ( let i = 0 ; i < points.length ; i++ ) {
			let point = points[ i ];
			let next = points[ ( i + 1 ) % points.length ];

			let id1 = point.id;
			let id2 = next.id;

			if ( id2 < id1 ) {
				let tmp = id1;
				id1 = id2;
				id2 = tmp;
			}

			let key = id1 + 'x' + id2;
			if ( !false ) {
				if ( key in self.cache.midpoints ) {
					// this cache caused a lot of problems...
					midpoints.push( self.cache.midpoints[ key ] );
					continue;
				}
			}

			// moved just for debugging...
			let from = self.from( point, next );
			let tmp = {};
			for ( let k in point ) {
				tmp[ k ] = point[ k ] + from[ k ] * 0.5;
			}

			let yi = q * self.random() - q * self.random();
			tmp.y += yi;
			tmp.x += yi * 0.2;
			tmp.z += yi * 0.2;

			let midpoint = self.vertex( tmp.x, tmp.y, tmp.z, tmp.r, tmp.g, tmp.b );

			self.cache.midpoints[ key ] = midpoint;
			midpoints.push( midpoint );
		}

		let midpt1 = midpoints[ 0 ];
		let midpt2 = midpoints[ 1 ];
		let midpt3 = midpoints[ 2 ];
		self.split( tb6, point1, midpt1, midpt3, level + 1 ); // a
		self.split( tb6, midpt1, point2, midpt2, level + 1 ); // b
		self.split( tb6, midpt2, point3, midpt3, level + 1 ); // c
		self.split( tb6, midpt1, midpt2, midpt3, level + 1 ); // d
	};

	self.drawTriangle = function( tb6, point1, point2, point3 ) {
		let normal = self.xyzzy( point1, point2, point3 );

		let screened1 = self.toScreen( point1 );
		let screened2 = self.toScreen( point2 );
		let screened3 = self.toScreen( point3 );

		let pixelsDrawn = tb6.triangleDraw( screened1, screened2, screened3, normal );
	};

	self.vertex = function( x, y, z, r, g, b ) {
		let v = { x:x, y:y, z:z, r:r, g:g, b:b, id:self.cache.uid++ };
		self.cache.points[ v.id ] = v;
		return v;
	};

	self.toScreen = function( point ) {
		let screened = {
			  x: self.into( point.x, 1024 )
			, y: self.into( point.y, 1024 )
			, z: self.into( point.z, 1024 )
			, r: self.into( point.r, 256  )
			, g: self.into( point.g, 256  )
			, b: self.into( point.b, 256  )
		}

		return screened;
	};

	self.into = function( r, i ) {
		return Math.floor( i * 0.5  + i * 0.5 * r );
	};

	self.xyzzy = function( point1, point2, point3 ) {
		let a = self.normallyFrom( point2, point1 );
		let b = self.normallyFrom( point2, point3 );
		return self.normalize(
			{
				  x: ( a.y * b.z ) - ( a.z * b.y ) // x:yzzy
				, y: ( a.z * b.x ) - ( a.x * b.z ) // y:zxxz
				, z: ( a.x * b.y ) - ( a.y * b.x ) // z:xyyx
			}
		);
	};

	self.normallyFrom = function( start, stop ) {
		return self.normalize( self.from( start, stop ) );
	};

	self.from = function( start, stop ) {
		let from = {};
		for ( let key in start ) {
			if ( key in stop ) {
				from[ key ] = stop[ key ] - start[ key ];
			}
		}
		return from;
	};

	self.normalize = function( point ) {
		let s = ( point.x * point.x ) + ( point.y * point.y ) + ( point.z * point.z );
		if ( 0 === s ) {
			s = 1;
		} else {
			s = Math.sqrt( s );
		}

		return self.vertex( point.x / s, point.y / s, point.z / s );
	};

	self.makeTexture = function( canvas ) {
		if ( false ) {
			let texture = canvas.makeImageData(2,2);
			let i = 0;
			texture.data[i++] = 255; texture.data[i++] =   0; texture.data[i++] =   0; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] =   0; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] = 255;
			texture.data[i++] =   0; texture.data[i++] = 255; texture.data[i++] = 255; texture.data[i++] = 255;
			return texture;
		}

		let n = 256;
		let texture = canvas.makeImageData( n, n );

		let index = 0;
		let color_increment = 255 / n;
		let r = 0;
		let b = 128
		for ( let y = 0 ; y < n ; y++, r += color_increment ) {
			let g = 0;
			for ( let x = 0 ; x < n ; x++, g += color_increment ) {
				texture.data[ index++ ] = r;
				texture.data[ index++ ] = g;
				texture.data[ index++ ] = b;
				texture.data[ index++ ] = 255;
			}
		}

		if ( false ) {
			let rawImageData = { data:texture.data, width:n, height:n }
			let jpegImageData = jpeg.encode( rawImageData, 50 );
			fs.writeFile( 'texture.jpg', jpegImageData.data, function(){} );
		}

		return texture;
	}

	self.log = function() {
		let s = '';
		let q = '';
		for ( let i in arguments ) {
			s += q + self.appendix( arguments[ i ] );
			q = ' ';
		}
		console.log( s );
	};

	self.appendix = function( lulz ) {
		let s = '';

		if ( Number.isInteger( lulz ) ) {
			lulz += 0.00000000001; // we all float down here
		}

		let type = typeof( lulz );
		if ( util.isArray( lulz ) ) {
			type = 'array';
		}

		switch ( type ) {
			case 'object':
				s = '{';
				let luld = false;
				for ( let i in lulz ) {
					s += ( luld ? ', ' : ' ' ) +  i + ':' + self.appendix( lulz[ i ] );
					luld = true;

				}
				if ( luld ) s +=  ' ';
				s += '}';
				break;
			case 'array':
				s = '[';
				for ( let i = 0 ; i < lulz.length ; i++ ) {
					s += ( i ? ', ' : ' ' ) + self.appendix( lulz[ i ] );
				}
				if ( lulz.length ) s += ' ';
				s += ']';
				break;
			case 'number':
				if ( Number.isInteger( lulz ) ) {
					if ( lulz < 0 ) {
						return lulz;
					} else {
						return '+' + lulz;
					}
				}
				let precision = 100000;
				lulz = Math.floor( precision * lulz ) / precision;
				if ( lulz < 0 ) {
					s = '' + lulz;
				} else {
					s = '+' + lulz;
				}

				if ( -1 == s.indexOf( '.' ) ) {
					s += '.';
				}
				while ( s.length < 8 ) s += '0';

				break;
			case 'string':
				s = lulz;
				break;
			default:
				//console.log( 'idk:' + type );
				return lulz;
		};
		return s;
	}

	self.drawLameWater = function( tb6 ) {
		let i = 0;
		for ( let y = 0 ; y < self.height ; y++ ) {
			for ( let x = 0 ; x < self.width ; x++ ) {
				tb6.imageData.data[ i++ ] = 0;
				tb6.imageData.data[ i++ ] = y < self.height / 2 ? 255 : 0;
				tb6.imageData.data[ i++ ] = 255;
				tb6.imageData.data[ i++ ] = 255;
			}
		}
	}
};

new TriangularB6Test().main( process.argv.slice( 2 ) );
<|endoftext|>


<commit_before>/*global module, require*/
var _ = require('underscore'),
	layoutGeometry = require('./layout-geometry');

module.exports = function MultiRootLayout() {
	'use strict';
	var self = this,
		mergeNodes = function (storedLayout, offset) {
			_.each(storedLayout.rootLayout, function (node) {
				node.x = node.x + offset.x;
				node.y = node.y + offset.y;
				node.rootId = storedLayout.rootIdea.id;
			});
		},
		globalIdeaTopLeftPosition = function (idea) {
			var positionArray = (idea && idea.attr && idea.attr.position) || [0, 0, 0];
			return {
				x: positionArray[0],
				y: positionArray[1],
				priority: positionArray[2]
			};
		},
		toStoredLayout = function (rootLayout, rootIdea) {
			var dimensions = {
				minX: 0,
				maxX: 0,
				minY: 0,
				maxY: 0,
				rootIdea: rootIdea,
				rootNode: rootLayout[rootIdea.id],
				rootLayout: rootLayout
			};
			_.each(rootLayout, function (node) {
				dimensions.minX = Math.min(dimensions.minX, node.x);
				dimensions.maxX = Math.max(dimensions.maxX, node.x + node.width);
				dimensions.minY = Math.min(dimensions.minY, node.y);
				dimensions.maxY = Math.max(dimensions.maxY, node.y + node.width);
			});
			dimensions.height = dimensions.maxY - dimensions.minY;
			dimensions.width = dimensions.maxX - dimensions.minX;
			return dimensions;
		},
		isPositioned = function (rootIdea) {
			return globalIdeaTopLeftPosition(rootIdea).priority;
		},
		calcDesiredRootNodeCenter = function (storedLayout) {
			var rootPosition = globalIdeaTopLeftPosition(storedLayout.rootIdea);
			return {
				x: (rootPosition.x + storedLayout.rootNode.width / 2),
				y: (rootPosition.y + storedLayout.rootNode.height / 2)
			};
		},
		positionedLayouts = [],
		unpositionedLayouts = [];

	self.appendRootNodeLayout = function (rootLayout, rootIdea) {
		var storedLayout = toStoredLayout(rootLayout, rootIdea);
		if (isPositioned(rootIdea)) {
			positionedLayouts.push(storedLayout);
		} else {
			unpositionedLayouts.push(storedLayout);
		}
	};
	self.getCombinedLayout = function (margin) {
		var result = {},
			mostRecentlyPositioned = positionedLayouts.length && _.max(positionedLayouts, function (layout) {
				return globalIdeaTopLeftPosition(layout.rootIdea).priority;
			}),
			origin = mostRecentlyPositioned && calcDesiredRootNodeCenter(mostRecentlyPositioned),
			originVector = [origin.x, origin.y],
			rootDistance = function (storedLayout) {
				var rootCenter = calcDesiredRootNodeCenter(storedLayout);
				return Math.pow(rootCenter.x - origin.x, 2) + Math.pow(rootCenter.y - origin.y, 2);
			},
			sortedPositionedLayouts = _.sortBy(positionedLayouts, rootDistance),
			placedLayouts = [],
			placedLayoutPoly = [],
			layoutOffsetWithoutOverlap = function (storedLayoutPoly, vector, offset) {
				var intersectionPoint = layoutGeometry.furthestIntersectionPoint(storedLayoutPoly, placedLayoutPoly, originVector, vector),
					currentIntersection = layoutGeometry.firstProjectedPolyPointOnVector(storedLayoutPoly, originVector, vector),
					translation;
				offset = offset || {x: 0, y: 0};
				if (intersectionPoint) {
					translation =  {
						x: intersectionPoint[0] - currentIntersection[0],
						y: intersectionPoint[1] - currentIntersection[1]
					};
					offset = {
						x: offset.x + translation.x,
						y: offset.y + translation.y
					};
					return layoutOffsetWithoutOverlap(layoutGeometry.translatePoly(storedLayoutPoly, translation), vector, offset);
				}
				return offset;
			},
			positionLayout = function (storedLayout) {
				var placedRootCenter = calcDesiredRootNodeCenter(storedLayout),
					storedLayoutPoly = layoutGeometry.tolayoutPolygon(storedLayout),
					offset,
					vector = [placedRootCenter.x - origin.x, placedRootCenter.y - origin.y];
				if (!storedLayout || _.contains(placedLayouts, storedLayout)) {
					return;
				}
				storedLayoutPoly = layoutGeometry.translatePoly(storedLayoutPoly, vector);
				/* must not overlap previously positioned layouts*/
				offset = layoutOffsetWithoutOverlap(storedLayoutPoly, vector);
				mergeNodes(storedLayout, offset);
				placedLayoutPoly = placedLayoutPoly.concat(layoutGeometry.tolayoutPolygon(storedLayout));
			};
		if (!margin) {
			throw 'invalid-args';
		}

		positionLayout(mostRecentlyPositioned);
		sortedPositionedLayouts.forEach(positionLayout);
		unpositionedLayouts.forEach(positionLayout);
		return result;
	};
};
<commit_msg>handle first layout placed and default vector<commit_after>/*global module, require*/
var _ = require('underscore'),
	layoutGeometry = require('./layout-geometry');

module.exports = function MultiRootLayout() {
	'use strict';
	var self = this,
		mergeNodes = function (storedLayout, offset) {
			_.each(storedLayout.rootLayout, function (node) {
				node.x = node.x + offset.x;
				node.y = node.y + offset.y;
				node.rootId = storedLayout.rootIdea.id;
			});
		},
		globalIdeaTopLeftPosition = function (idea) {
			var positionArray = (idea && idea.attr && idea.attr.position) || [0, 0, 0];
			return {
				x: positionArray[0],
				y: positionArray[1],
				priority: positionArray[2]
			};
		},
		toStoredLayout = function (rootLayout, rootIdea) {
			var dimensions = {
				minX: 0,
				maxX: 0,
				minY: 0,
				maxY: 0,
				rootIdea: rootIdea,
				rootNode: rootLayout[rootIdea.id],
				rootLayout: rootLayout
			};
			_.each(rootLayout, function (node) {
				dimensions.minX = Math.min(dimensions.minX, node.x);
				dimensions.maxX = Math.max(dimensions.maxX, node.x + node.width);
				dimensions.minY = Math.min(dimensions.minY, node.y);
				dimensions.maxY = Math.max(dimensions.maxY, node.y + node.width);
			});
			dimensions.height = dimensions.maxY - dimensions.minY;
			dimensions.width = dimensions.maxX - dimensions.minX;
			return dimensions;
		},
		isPositioned = function (rootIdea) {
			return globalIdeaTopLeftPosition(rootIdea).priority;
		},
		calcDesiredRootNodeCenter = function (storedLayout) {
			var rootPosition = globalIdeaTopLeftPosition(storedLayout.rootIdea);
			return {
				x: (rootPosition.x + storedLayout.rootNode.width / 2),
				y: (rootPosition.y + storedLayout.rootNode.height / 2)
			};
		},
		positionedLayouts = [],
		unpositionedLayouts = [];

	self.appendRootNodeLayout = function (rootLayout, rootIdea) {
		var storedLayout = toStoredLayout(rootLayout, rootIdea);
		if (isPositioned(rootIdea)) {
			positionedLayouts.push(storedLayout);
		} else {
			unpositionedLayouts.push(storedLayout);
		}
	};
	self.getCombinedLayout = function (margin) {
		var result = {},
			mostRecentlyPositioned = positionedLayouts.length && _.max(positionedLayouts, function (layout) {
				return globalIdeaTopLeftPosition(layout.rootIdea).priority;
			}),
			origin = mostRecentlyPositioned && calcDesiredRootNodeCenter(mostRecentlyPositioned),
			originVector = [origin.x, origin.y],
			rootDistance = function (storedLayout) {
				var rootCenter = calcDesiredRootNodeCenter(storedLayout);
				return Math.pow(rootCenter.x - origin.x, 2) + Math.pow(rootCenter.y - origin.y, 2);
			},
			sortedPositionedLayouts = _.sortBy(positionedLayouts, rootDistance),
			placedLayouts = [],
			placedLayoutPoly = [],
			layoutOffsetWithoutOverlap = function (storedLayoutPoly, vector, offset) {
				var intersectionPoint = layoutGeometry.furthestIntersectionPoint(storedLayoutPoly, placedLayoutPoly, originVector, vector),
					currentIntersection = layoutGeometry.firstProjectedPolyPointOnVector(storedLayoutPoly, originVector, vector),
					translation;
				offset = offset || {x: 0, y: 0};
				if (intersectionPoint) {
					translation =  {
						x: intersectionPoint[0] - currentIntersection[0],
						y: intersectionPoint[1] - currentIntersection[1]
					};
					offset = {
						x: offset.x + translation.x,
						y: offset.y + translation.y
					};
					return layoutOffsetWithoutOverlap(layoutGeometry.translatePoly(storedLayoutPoly, translation), vector, offset);
				}
				return offset;
			},
			positionLayout = function (storedLayout) {
				var placedRootCenter = calcDesiredRootNodeCenter(storedLayout),
					storedLayoutPoly = layoutGeometry.tolayoutPolygon(storedLayout),
					offset,
					vector = [placedRootCenter.x - origin.x, placedRootCenter.y - origin.y],
					initialTranslation = [placedRootCenter.x, placedRootCenter.y];
				if (!storedLayout || _.contains(placedLayouts, storedLayout)) {
					return;
				}
				if (positionedLayouts.length) {
					if (vector[0] === 0 && vector[1] === 0) {
						vector = [1, 0];
					}
					storedLayoutPoly = layoutGeometry.translatePoly(storedLayoutPoly, initialTranslation);
					offset = layoutOffsetWithoutOverlap(storedLayoutPoly, vector);
				} else {
					offset = placedRootCenter;
				}
				mergeNodes(storedLayout, offset);

				placedLayouts.push(storedLayout);
				placedLayoutPoly = placedLayoutPoly.concat(layoutGeometry.tolayoutPolygon(storedLayout));
			};
		if (!margin) {
			throw 'invalid-args';
		}
		positionLayout(mostRecentlyPositioned);
		sortedPositionedLayouts.forEach(positionLayout);
		unpositionedLayouts.forEach(positionLayout);
		return result;
	};
};
<|endoftext|>


<commit_before>var gulp = require('gulp');
var browser = require('browser-sync');
var plumber = require('gulp-plumber');
var notify = require('gulp-notify');
var compass = require('gulp-compass');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var cssmin = require('gulp-cssmin');
var pleeease = require('gulp-pleeease');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
var ejs = require("gulp-ejs");
var critical = require('critical');
var styleguide = require('gulp-styledocco');

// browserSync
gulp.task("browser", function() {
	browser({
		// phpを使わない場合は下記を有効にする
		server: {
			baseDir: "./app/"
		}
		// phpを使う場合は、下記を有効にして、appディレクトリを
		// MAMPを使うなどしてサーバ化する
		// proxy: 'localhost:8888'
	});
});

//reload
gulp.task('reload', function () {
	browser.reload();
});

// sass(compass)
gulp.task('compass', function () {
	return gulp.src('./src/sass/**/*.scss')
		.pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
		.pipe(compass({
				config_file : './config.rb',
				comments : false,
				css : './app/assets/css/',
				sass: './src/sass/',
				image: './app/assets/images/'
		}))
		.pipe(pleeease({
			fallbacks: {
				autoprefixer: ['last 4 versions'] //ベンダープレフィックス
		    },
		    optimizers: {
				minifier: false //圧縮の有無
		    }
		}))
		//.pipe(gulp.dest('./app/assets/temp'))
		.pipe(browser.reload({stream:true}))
});

// js library min (concat & uglify)
gulp.task('libsmin', function() {
	gulp.src([
			// 圧縮したいライブラリを指定
			'./src/js/libs/jquery.js',
			'./src/js/libs/velocity.js',
			'./src/js/libs/velocity.ui.js'
		])
		.pipe(concat('libs.js'))
		.pipe(uglify())
		.pipe(gulp.dest('./app/assets/js/'))
		.pipe(browser.reload({stream: true}));
});

// js
gulp.task('js', function() {
	gulp.src('./src/js/*.js')
		.pipe(gulp.dest('./app/assets/js/'))
		.pipe(browser.reload({stream: true}));
});

// ejs
gulp.task("ejs", function() {
	gulp.src(
		["./src/ejs/**/*.ejs",'!' + "./src/ejs/**/_*.ejs"]
	)
	.pipe(ejs())
	.pipe(gulp.dest("./app"));
});

// critical-path css(inline)
gulp.task('critical', function () {
  critical.generateInline({
		base: './app/',
		src: 'index.html',
		//dest: 'assets/css/critical.css',
		htmlTarget: 'index.html',
		minify: true,
		width: 1000,
		height: 768
	});
});

// image min
gulp.task('imagemin', function () {
	gulp.src('./app/assets/images/**/*')
	.pipe(imagemin({
		progressive: true,
		svgoPlugins: [{removeViewBox: false}],
		use: [pngquant({
			quality: '60-80',
			speed: 1
		})]
	}))
	.pipe(gulp.dest('./app/assets/images/'));
});

// css min
gulp.task('cssmin', function () {
	gulp.src('./app/assets/css/**/*')
		.pipe(cssmin())
		.pipe(gulp.dest('./app/assets/css/'));
});

// js min
gulp.task('jsmin', function() {
	gulp.src('./src/js/*.js')
		.pipe(uglify())
		.pipe(gulp.dest('./app/assets/js/'))
		.pipe(browser.reload({stream: true}));
});

// styleGuide
gulp.task('styleguide', function () {
	gulp.src('./app/assets/css/*.css')
		.pipe(styleguide({
			out: './app/styleguide',
			name: 'styleguide'
	}));
});

// watch
gulp.task('watch', function() {
	gulp.watch('./app/**/*.html',['reload']);
	gulp.watch('./src/sass/**/*.scss', ['compass','reload'])
	gulp.watch("./src/ejs/**/*.ejs",['ejs','reload']);
	gulp.watch("./src/js/*.js",['js']);
});

// default
gulp.task("default",['browser','watch']);

// minify
gulp.task("min",['cssmin','imagemin','jsmin','libsmin']);<commit_msg>[fix]pleeeaseのminifierの修正など<commit_after>var gulp = require('gulp');
var browser = require('browser-sync');
var plumber = require('gulp-plumber');
var notify = require('gulp-notify');
var compass = require('gulp-compass');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');
var cssmin = require('gulp-cssmin');
var pleeease = require('gulp-pleeease');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
var ejs = require("gulp-ejs");
var critical = require('critical');
var styleguide = require('gulp-styledocco');

// browserSync
gulp.task("browser", function() {
	browser({
		// phpを使わない場合は下記を有効にする
		server: {
			baseDir: "./app/"
		}
		// phpを使う場合は、下記を有効にして、appディレクトリを
		// MAMPを使うなどしてサーバ化する
		// proxy: 'localhost:8888'
	});
});

//reload
gulp.task('reload', function () {
	browser.reload();
});

// sass(compass)
gulp.task('compass', function () {
	return gulp.src('./src/sass/**/*.scss')
		.pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
		.pipe(compass({
				config_file : './config.rb',
				comments : false,
				css : './app/assets/css/',
				sass: './src/sass/',
				image: './app/assets/images/'
		}))
		.pipe(pleeease({
			fallbacks: {
				autoprefixer: ['last 4 versions'] //ベンダープレフィックス
			},
			minifier: false //圧縮の有無
		}))
		.pipe(gulp.dest('./app/assets/css/'))
		.pipe(browser.reload({stream:true}));
});

// js library min (concat & uglify)
gulp.task('libsmin', function() {
	gulp.src([
			// 圧縮したいライブラリを指定
			'./src/js/libs/jquery.js',
			'./src/js/libs/velocity.js',
			'./src/js/libs/velocity.ui.js'
		])
		.pipe(concat('libs.js'))
		.pipe(uglify())
		.pipe(gulp.dest('./app/assets/js/'));
});

// js
gulp.task('js', function() {
	gulp.src('./src/js/*.js')
		.pipe(gulp.dest('./app/assets/js/'))
});

// ejs
gulp.task("ejs", function() {
	gulp.src(
		["./src/ejs/**/*.ejs",'!' + "./src/ejs/**/_*.ejs"]
	)
	.pipe(ejs())
	.pipe(gulp.dest("./app"));
});

// critical-path css(inline)
gulp.task('critical', function () {
  critical.generateInline({
		base: './app/',
		src: 'index.html',
		//dest: 'assets/css/critical.css',
		htmlTarget: 'index.html',
		minify: true,
		width: 1000,
		height: 768
	});
});

// image min
gulp.task('imagemin', function () {
	gulp.src('./app/assets/images/**/*')
	.pipe(imagemin({
		progressive: true,
		svgoPlugins: [{removeViewBox: false}],
		use: [pngquant({
			quality: '60-80',
			speed: 1
		})]
	}))
	.pipe(gulp.dest('./app/assets/images/'));
});

// css min
gulp.task('cssmin', function () {
	gulp.src('./app/assets/css/**/*')
		.pipe(cssmin())
		.pipe(gulp.dest('./app/assets/css/'));
});

// js min
gulp.task('jsmin', function() {
	gulp.src('./src/js/*.js')
		.pipe(uglify())
		.pipe(gulp.dest('./app/assets/js/'));
});

// styleGuide
gulp.task('styleguide', function () {
	gulp.src('./app/assets/css/*.css')
		.pipe(styleguide({
			out: './app/styleguide',
			name: 'styleguide'
	}));
});

// watch
gulp.task('watch', function() {
	gulp.watch('./app/**/*.html',['reload']);
	gulp.watch('./src/sass/**/*.scss', ['compass','reload'])
	gulp.watch("./src/ejs/**/*.ejs",['ejs','reload']);
	gulp.watch("./src/js/*.js",['js','reload']);
});

// default
gulp.task("default",['browser','watch']);

// minify
gulp.task("min",['cssmin','imagemin','jsmin','libsmin']);<|endoftext|>