<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Class = require('../../utils/Class');
var Components = require('./components');
var EventEmitter = require('eventemitter3');
var GetFastValue = require('../../utils/object/GetFastValue');
var HasValue = require('../../utils/object/HasValue');
var Vertices = require('./lib/geometry/Vertices');

/**
 * @classdesc
 * A wrapper around a Tile that provides access to a corresponding Matter body. A tile can only
 * have one Matter body associated with it. You can either pass in an existing Matter body for
 * the tile or allow the constructor to create the corresponding body for you. If the Tile has a
 * collision group (defined in Tiled), those shapes will be used to create the body. If not, the
 * tile's rectangle bounding box will be used.
 *
 * The corresponding body will be accessible on the Tile itself via Tile.physics.matterBody.
 *
 * Note: not all Tiled collision shapes are supported. See
 * Phaser.Physics.Matter.TileBody#setFromTileCollision for more information.
 *
 * @class TileBody
 * @memberof Phaser.Physics.Matter
 * @extends Phaser.Events.EventEmitter
 * @constructor
 * @since 3.0.0
 *
 * @extends Phaser.Physics.Matter.Components.Bounce
 * @extends Phaser.Physics.Matter.Components.Collision
 * @extends Phaser.Physics.Matter.Components.Friction
 * @extends Phaser.Physics.Matter.Components.Gravity
 * @extends Phaser.Physics.Matter.Components.Mass
 * @extends Phaser.Physics.Matter.Components.Sensor
 * @extends Phaser.Physics.Matter.Components.Sleep
 * @extends Phaser.Physics.Matter.Components.Static
 *
 * @param {Phaser.Physics.Matter.World} world - The Matter world instance this body belongs to.
 * @param {Phaser.Tilemaps.Tile} tile - The target tile that should have a Matter body.
 * @param {Phaser.Types.Physics.Matter.MatterTileOptions} [options] - Options to be used when creating the Matter body.
 */
var MatterTileBody = new Class({

    Extends: EventEmitter,

    Mixins: [
        Components.Bounce,
        Components.Collision,
        Components.Friction,
        Components.Gravity,
        Components.Mass,
        Components.Sensor,
        Components.Sleep,
        Components.Static
    ],

    initialize:

    function MatterTileBody (world, tile, options)
    {
        EventEmitter.call(this);

        /**
         * The tile object the body is associated with.
         *
         * @name Phaser.Physics.Matter.TileBody#tile
         * @type {Phaser.Tilemaps.Tile}
         * @since 3.0.0
         */
        this.tile = tile;

        /**
         * The Matter world the body exists within.
         *
         * @name Phaser.Physics.Matter.TileBody#world
         * @type {Phaser.Physics.Matter.World}
         * @since 3.0.0
         */
        this.world = world;

        // Install a reference to 'this' on the tile and ensure there can only be one matter body
        // associated with the tile
        if (tile.physics.matterBody)
        {
            tile.physics.matterBody.destroy();
        }

        tile.physics.matterBody = this;

        // Set the body either from an existing body (if provided), the shapes in the tileset
        // collision layer (if it exists) or a rectangle matching the tile.
        var body = GetFastValue(options, 'body', null);

        var addToWorld = GetFastValue(options, 'addToWorld', true);

        if (!body)
        {
            var collisionGroup = tile.getCollisionGroup();
            var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

            if (collisionObjects.length > 0)
            {
                this.setFromTileCollision(options);
            }
            else
            {
                this.setFromTileRectangle(options);
            }
        }
        else
        {
            this.setBody(body, addToWorld);
        }
    },

    /**
     * Sets the current body to a rectangle that matches the bounds of the tile.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileRectangle
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileRectangle: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var bounds = this.tile.getBounds();
        var cx = bounds.x + (bounds.width / 2);
        var cy = bounds.y + (bounds.height / 2);
        var body = Bodies.rectangle(cx, cy, bounds.width, bounds.height, options);

        this.setBody(body, options.addToWorld);

        return this;
    },

    /**
     * Sets the current body from the collision group associated with the Tile. This is typically
     * set up in Tiled's collision editor.
     *
     * Note: Matter doesn't support all shapes from Tiled. Rectangles and polygons are directly
     * supported. Ellipses are converted into circle bodies. Polylines are treated as if they are
     * closed polygons. If a tile has multiple shapes, a multi-part body will be created. Concave
     * shapes are supported if poly-decomp library is included. Decomposition is not guaranteed to
     * work for complex shapes (e.g. holes), so it's often best to manually decompose a concave
     * polygon into multiple convex polygons yourself.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileCollision
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileCollision: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var sx = this.tile.tilemapLayer.scaleX;
        var sy = this.tile.tilemapLayer.scaleY;
        var tileX = this.tile.getLeft();
        var tileY = this.tile.getTop();
        var collisionGroup = this.tile.getCollisionGroup();
        var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

        var parts = [];

        for (var i = 0; i < collisionObjects.length; i++)
        {
            var object = collisionObjects[i];
            var ox = tileX + (object.x * sx);
            var oy = tileY + (object.y * sy);
            var ow = object.width * sx;
            var oh = object.height * sy;
            var body = null;

            if (object.rectangle)
            {
                body = Bodies.rectangle(ox + ow / 2, oy + oh / 2, ow, oh, options);
            }
            else if (object.ellipse)
            {
                body = Bodies.circle(ox + ow / 2, oy + oh / 2, ow / 2, options);
            }
            else if (object.polygon || object.polyline)
            {
                // Polygons and polylines are both treated as closed polygons
                var originalPoints = object.polygon ? object.polygon : object.polyline;

                var points = originalPoints.map(function (p)
                {
                    return { x: p.x * sx, y: p.y * sy };
                });

                var vertices = Vertices.create(points);

                // Points are relative to the object's origin (first point placed in Tiled), but
                // matter expects points to be relative to the center of mass. This only applies to
                // convex shapes. When a concave shape is decomposed, multiple parts are created and
                // the individual parts are positioned relative to (ox, oy).
                //
                //  Update: 8th January 2019 - the latest version of Matter needs the Vertices adjusted,
                //  regardless if convex or concave.

                var center = Vertices.centre(vertices);

                ox += center.x;
                oy += center.y;

                body = Bodies.fromVertices(ox, oy, vertices, options);
            }

            if (body)
            {
                parts.push(body);
            }
        }

        if (parts.length === 1)
        {
            this.setBody(parts[0], options.addToWorld);
        }
        else if (parts.length > 1)
        {
            options.parts = parts;
            this.setBody(Body.create(options), options.addToWorld);
        }

        return this;
    },

    /**
     * Sets the current body to the given body. This will remove the previous body, if one already
     * exists.
     *
     * @method Phaser.Physics.Matter.TileBody#setBody
     * @since 3.0.0
     *
     * @param {MatterJS.BodyType} body - The new Matter body to use.
     * @param {boolean} [addToWorld=true] - Whether or not to add the body to the Matter world.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setBody: function (body, addToWorld)
    {
        if (addToWorld === undefined) { addToWorld = true; }

        if (this.body)
        {
            this.removeBody();
        }

        this.body = body;
        this.body.gameObject = this;

        if (addToWorld)
        {
            this.world.add(this.body);
        }

        return this;
    },

    /**
     * Removes the current body from the TileBody and from the Matter world
     *
     * @method Phaser.Physics.Matter.TileBody#removeBody
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    removeBody: function ()
    {
        if (this.body)
        {
            this.world.remove(this.body);
            this.body.gameObject = undefined;
            this.body = undefined;
        }

        return this;
    },

    /**
     * Removes the current body from the tile and the world.
     *
     * @method Phaser.Physics.Matter.TileBody#destroy
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    destroy: function ()
    {
        this.removeBody();
        this.tile.physics.matterBody = undefined;
    }

});

module.exports = MatterTileBody;
<commit_msg>Update MatterTileBody.js<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Bodies = require('./lib/factory/Bodies');
var Body = require('./lib/body/Body');
var Class = require('../../utils/Class');
var Components = require('./components');
var EventEmitter = require('eventemitter3');
var GetFastValue = require('../../utils/object/GetFastValue');
var HasValue = require('../../utils/object/HasValue');
var Vertices = require('./lib/geometry/Vertices');

/**
 * @classdesc
 * A wrapper around a Tile that provides access to a corresponding Matter body. A tile can only
 * have one Matter body associated with it. You can either pass in an existing Matter body for
 * the tile or allow the constructor to create the corresponding body for you. If the Tile has a
 * collision group (defined in Tiled), those shapes will be used to create the body. If not, the
 * tile's rectangle bounding box will be used.
 *
 * The corresponding body will be accessible on the Tile itself via Tile.physics.matterBody.
 *
 * Note: not all Tiled collision shapes are supported. See
 * Phaser.Physics.Matter.TileBody#setFromTileCollision for more information.
 *
 * @class TileBody
 * @memberof Phaser.Physics.Matter
 * @extends Phaser.Events.EventEmitter
 * @constructor
 * @since 3.0.0
 *
 * @extends Phaser.Physics.Matter.Components.Bounce
 * @extends Phaser.Physics.Matter.Components.Collision
 * @extends Phaser.Physics.Matter.Components.Friction
 * @extends Phaser.Physics.Matter.Components.Gravity
 * @extends Phaser.Physics.Matter.Components.Mass
 * @extends Phaser.Physics.Matter.Components.Sensor
 * @extends Phaser.Physics.Matter.Components.Sleep
 * @extends Phaser.Physics.Matter.Components.Static
 *
 * @param {Phaser.Physics.Matter.World} world - The Matter world instance this body belongs to.
 * @param {Phaser.Tilemaps.Tile} tile - The target tile that should have a Matter body.
 * @param {Phaser.Types.Physics.Matter.MatterTileOptions} [options] - Options to be used when creating the Matter body.
 */
var MatterTileBody = new Class({

    Extends: EventEmitter,

    Mixins: [
        Components.Bounce,
        Components.Collision,
        Components.Friction,
        Components.Gravity,
        Components.Mass,
        Components.Sensor,
        Components.Sleep,
        Components.Static
    ],

    initialize:

    function MatterTileBody (world, tile, options)
    {
        EventEmitter.call(this);

        /**
         * The tile object the body is associated with.
         *
         * @name Phaser.Physics.Matter.TileBody#tile
         * @type {Phaser.Tilemaps.Tile}
         * @since 3.0.0
         */
        this.tile = tile;

        /**
         * The Matter world the body exists within.
         *
         * @name Phaser.Physics.Matter.TileBody#world
         * @type {Phaser.Physics.Matter.World}
         * @since 3.0.0
         */
        this.world = world;

        // Install a reference to 'this' on the tile and ensure there can only be one matter body
        // associated with the tile
        if (tile.physics.matterBody)
        {
            tile.physics.matterBody.destroy();
        }

        tile.physics.matterBody = this;

        // Set the body either from an existing body (if provided), the shapes in the tileset
        // collision layer (if it exists) or a rectangle matching the tile.
        var body = GetFastValue(options, 'body', null);

        var addToWorld = GetFastValue(options, 'addToWorld', true);

        if (!body)
        {
            var collisionGroup = tile.getCollisionGroup();
            var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

            if (collisionObjects.length > 0)
            {
                this.setFromTileCollision(options);
            }
            else
            {
                this.setFromTileRectangle(options);
            }
        }
        else
        {
            this.setBody(body, addToWorld);
        }
    },

    /**
     * Sets the current body to a rectangle that matches the bounds of the tile.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileRectangle
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileRectangle: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var bounds = this.tile.getBounds();
        var cx = bounds.x + (bounds.width / 2);
        var cy = bounds.y + (bounds.height / 2);
        var body = Bodies.rectangle(cx, cy, bounds.width, bounds.height, options);

        this.setBody(body, options.addToWorld);

        return this;
    },

    /**
     * Sets the current body from the collision group associated with the Tile. This is typically
     * set up in Tiled's collision editor.
     *
     * Note: Matter doesn't support all shapes from Tiled. Rectangles and polygons are directly
     * supported. Ellipses are converted into circle bodies. Polylines are treated as if they are
     * closed polygons. If a tile has multiple shapes, a multi-part body will be created. Concave
     * shapes are supported if poly-decomp library is included. Decomposition is not guaranteed to
     * work for complex shapes (e.g. holes), so it's often best to manually decompose a concave
     * polygon into multiple convex polygons yourself.
     *
     * @method Phaser.Physics.Matter.TileBody#setFromTileCollision
     * @since 3.0.0
     *
     * @param {Phaser.Types.Physics.Matter.MatterBodyTileOptions} [options] - Options to be used when creating the Matter body. See MatterJS.Body for a list of what Matter accepts.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setFromTileCollision: function (options)
    {
        if (options === undefined) { options = {}; }
        if (!HasValue(options, 'isStatic')) { options.isStatic = true; }
        if (!HasValue(options, 'addToWorld')) { options.addToWorld = true; }

        var sx = this.tile.tilemapLayer.scaleX;
        var sy = this.tile.tilemapLayer.scaleY;
        var tileX = this.tile.getLeft();
        var tileY = this.tile.getTop();
        var collisionGroup = this.tile.getCollisionGroup();
        var collisionObjects = GetFastValue(collisionGroup, 'objects', []);

        var parts = [];

        for (var i = 0; i < collisionObjects.length; i++)
        {
            var object = collisionObjects[i];
            var ox = tileX + (object.x * sx);
            var oy = tileY + (object.y * sy);
            var ow = object.width * sx;
            var oh = object.height * sy;
            var body = null;

            if (object.rectangle)
            {
                body = Bodies.rectangle(ox + ow / 2, oy + oh / 2, ow, oh, options);
            }
            else if (object.ellipse)
            {
                body = Bodies.circle(ox + ow / 2, oy + oh / 2, ow / 2, options);
            }
            else if (object.polygon || object.polyline)
            {
                // Polygons and polylines are both treated as closed polygons
                var originalPoints = object.polygon ? object.polygon : object.polyline;

                var points = originalPoints.map(function (p)
                {
                    return { x: p.x * sx, y: p.y * sy };
                });

                var vertices = Vertices.create(points);

                // Points are relative to the object's origin (first point placed in Tiled), but
                // matter expects points to be relative to the center of mass. This only applies to
                // convex shapes. When a concave shape is decomposed, multiple parts are created and
                // the individual parts are positioned relative to (ox, oy).
                //
                //  Update: 8th January 2019 - the latest version of Matter needs the Vertices adjusted,
                //  regardless if convex or concave.

                var center = Vertices.centre(vertices);

                ox += center.x;
                oy += center.y;

                body = Bodies.fromVertices(ox, oy, vertices, options);
            }

            if (body)
            {
                parts.push(body);
            }
        }

        if (parts.length === 1)
        {
            this.setBody(parts[0], options.addToWorld);
        }
        else if (parts.length > 1)
        {
            options.parts = parts;
            this.setBody(Body.create(options), options.addToWorld);
        }

        return this;
    },

    /**
     * Sets the current body to the given body. This will remove the previous body, if one already
     * exists.
     *
     * @method Phaser.Physics.Matter.TileBody#setBody
     * @since 3.0.0
     *
     * @param {MatterJS.BodyType} body - The new Matter body to use.
     * @param {boolean} [addToWorld=true] - Whether or not to add the body to the Matter world.
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    setBody: function (body, addToWorld)
    {
        if (addToWorld === undefined) { addToWorld = true; }

        if (this.body)
        {
            this.removeBody();
        }

        this.body = body;
        this.body.gameObject = this;

        if (addToWorld)
        {
            this.world.add(this.body);
        }

        return this;
    },

    /**
     * Removes the current body from the TileBody and from the Matter world
     *
     * @method Phaser.Physics.Matter.TileBody#removeBody
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    removeBody: function ()
    {
        if (this.body)
        {
            this.world.remove(this.body);
            this.body.gameObject = undefined;
            this.body = undefined;
        }

        return this;
    },

    /**
     * Removes the current body from the tile and the world.
     *
     * @method Phaser.Physics.Matter.TileBody#destroy
     * @since 3.0.0
     *
     * @return {Phaser.Physics.Matter.TileBody} This TileBody object.
     */
    destroy: function ()
    {
        this.removeBody();
        this.tile.physics.matterBody = undefined;
        this.removeAllListeners();
    }

});

module.exports = MatterTileBody;
<|endoftext|>


<commit_before>const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const colors = require('ansi-colors');
const logger = require('fancy-log');
const watchify = require('watchify');
const browserify = require('browserify');
const uglify = require('gulp-uglify');
const size = require('gulp-size');
const tsify = require('tsify');
const concat = require('gulp-concat');
const exec = require('child_process').exec;
const fs = require('fs');

require('../gulp/cssProject.js')(__dirname);

const browserifyOpts = (entries, debug) => ({
  entries: entries,
  standalone: 'Lichess',
  debug: debug
});
const destinationPath = '../../public/compiled/';
const destination = () => gulp.dest(destinationPath);
const fileBaseName = 'lichess.site';

const abFile = process.env.LILA_AB_FILE;

const jqueryFill = () => gulp.src('src/jquery.fill.js')
  .pipe(buffer())
  .pipe(uglify())
  .pipe(gulp.dest('./dist'));

const ab = () => {
  if (abFile) return gulp.src(abFile)
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
  else {
    logger.info(colors.yellow('Building without AB file'));
    return gulp.src('.');
  }
};

const stockfishPexe = () => gulp.src([
  require.resolve('stockfish.pexe/stockfish.nmf'),
  require.resolve('stockfish.pexe/stockfish.pexe'),
  require.resolve('stockfish.pexe/stockfish.bc')
]).pipe(gulp.dest('../../public/vendor/stockfish.pexe'));

const stockfishJs = () => gulp.src([
  require.resolve('stockfish.js/stockfish.wasm.js'),
  require.resolve('stockfish.js/stockfish.wasm'),
  require.resolve('stockfish.js/stockfish.js')
]).pipe(gulp.dest('../../public/vendor/stockfish.js'));

const stockfishWasm = () => gulp.src([
  require.resolve('stockfish.wasm/stockfish.js'),
  require.resolve('stockfish.wasm/stockfish.js.mem'),
  require.resolve('stockfish.wasm/stockfish.wasm'),
  require.resolve('stockfish.wasm/stockfish.worker.js')
]).pipe(gulp.dest('../../public/vendor/stockfish.wasm/'));

const stockfishMvWasm = () => gulp.src([
  require.resolve('stockfish-mv.wasm/stockfish.js'),
  require.resolve('stockfish-mv.wasm/stockfish.js.mem'),
  require.resolve('stockfish-mv.wasm/stockfish.wasm'),
  require.resolve('stockfish-mv.wasm/pthread-main.js')
]).pipe(gulp.dest('../../public/vendor/stockfish-mv.wasm/'));

const prodSource = () => browserify(browserifyOpts('src/index.ts', false))
  .plugin(tsify)
  .bundle()
  .pipe(source(`${fileBaseName}.source.min.js`))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(gulp.dest('./dist'));

const devSource = () => browserify(browserifyOpts('src/index.ts', true))
  .plugin(tsify)
  .bundle()
  .pipe(source(`${fileBaseName}.js`))
  .pipe(destination());

function makeDependencies(filename) {
  return function bundleDeps() {
    return gulp.src([
  '../../public/javascripts/vendor/jquery.min.js',
  './dist/jquery.fill.js',
  './dep/powertip.min.js',
  './dep/howler.min.js',
  './dep/mousetrap.min.js',
  './dist/consolemsg.js',
  ...(abFile ? ['./dist/ab.js'] : []),
])
      .pipe(concat(filename))
      .pipe(destination());
  };
}

function makeBundle(filename) {
  return function bundleItAll() {
    return gulp.src([
      destinationPath + 'lichess.deps.js',
      './dist/' + filename,
    ])
      .pipe(concat(filename.replace('source.', '')))
      .pipe(destination());
  };
}

const gitSha = (cb) => exec("git rev-parse -q --short HEAD", function (err, stdout) {
  if (err) throw err;
  if (!fs.existsSync('./dist')) fs.mkdirSync('./dist');
  var date = new Date().toISOString().split('.')[0];
  fs.writeFileSync('./dist/consolemsg.js',
    'window.lichess=window.lichess||{};console.info("Lichess is open source! https://github.com/ornicar/lila");' +
    `lichess.info = "Assets built ${date} from sha ${stdout.trim()}";`);
  cb();
});

const standalonesJs = () => gulp.src([
  'util.js', 'trans.js', 'tv.js', 'puzzle.js', 'user.js', 'coordinate.js', 'captcha.js', 'embed-analyse.js'
].map(f => `src/standalones/${f}`))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(destination());

const userMod = () => browserify(browserifyOpts('./src/user-mod.js', false))
  .bundle()
  .pipe(source('user-mod.js'))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(destination());

const tasks = [gitSha, jqueryFill, ab, standalonesJs, userMod, stockfishWasm, stockfishMvWasm, stockfishPexe, stockfishJs];

const dev = gulp.series(tasks.concat([devSource]));

const deps = makeDependencies('lichess.deps.js');

gulp.task('prod', gulp.series(deps, prodSource, makeBundle(`${fileBaseName}.source.min.js`)));
gulp.task('dev', gulp.series(deps, dev));
gulp.task('default', gulp.series(dev, () => gulp.watch('src/**/*.js', dev)));

gulp.task('deps', makeDependencies('lichess.deps.js'));
<commit_msg>try to fix ui/site gulp<commit_after>const gulp = require('gulp');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const colors = require('ansi-colors');
const logger = require('fancy-log');
const watchify = require('watchify');
const browserify = require('browserify');
const uglify = require('gulp-uglify');
const size = require('gulp-size');
const tsify = require('tsify');
const concat = require('gulp-concat');
const exec = require('child_process').exec;
const fs = require('fs');

require('../gulp/cssProject.js')(__dirname);

const browserifyOpts = (entries, debug) => ({
  entries: entries,
  standalone: 'Lichess',
  debug: debug
});
const destinationPath = '../../public/compiled/';
const destination = () => gulp.dest(destinationPath);
const fileBaseName = 'lichess.site';

const abFile = process.env.LILA_AB_FILE;

const jqueryFill = () => gulp.src('src/jquery.fill.js')
  .pipe(buffer())
  .pipe(uglify())
  .pipe(gulp.dest('./dist'));

const ab = () => {
  if (abFile) return gulp.src(abFile)
    .pipe(buffer())
    .pipe(uglify())
    .pipe(gulp.dest('./dist'));
  else {
    logger.info(colors.yellow('Building without AB file'));
    return gulp.src('.');
  }
};

const stockfishPexe = () => gulp.src([
  require.resolve('stockfish.pexe/stockfish.nmf'),
  require.resolve('stockfish.pexe/stockfish.pexe'),
  require.resolve('stockfish.pexe/stockfish.bc')
]).pipe(gulp.dest('../../public/vendor/stockfish.pexe'));

const stockfishJs = () => gulp.src([
  require.resolve('stockfish.js/stockfish.wasm.js'),
  require.resolve('stockfish.js/stockfish.wasm'),
  require.resolve('stockfish.js/stockfish.js')
]).pipe(gulp.dest('../../public/vendor/stockfish.js'));

const stockfishWasm = () => gulp.src([
  require.resolve('stockfish.wasm/stockfish.js'),
  require.resolve('stockfish.wasm/stockfish.js.mem'),
  require.resolve('stockfish.wasm/stockfish.wasm'),
  require.resolve('stockfish.wasm/stockfish.worker.js')
]).pipe(gulp.dest('../../public/vendor/stockfish.wasm/'));

const stockfishMvWasm = () => gulp.src([
  require.resolve('stockfish-mv.wasm/stockfish.js'),
  require.resolve('stockfish-mv.wasm/stockfish.js.mem'),
  require.resolve('stockfish-mv.wasm/stockfish.wasm'),
  require.resolve('stockfish-mv.wasm/pthread-main.js')
]).pipe(gulp.dest('../../public/vendor/stockfish-mv.wasm/'));

const prodSource = () => browserify(browserifyOpts('src/index.ts', false))
  .plugin(tsify)
  .bundle()
  .pipe(source(`${fileBaseName}.source.min.js`))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(gulp.dest('./dist'));

const devSource = () => browserify(browserifyOpts('src/index.ts', true))
  .plugin(tsify)
  .bundle()
  .pipe(source(`${fileBaseName}.js`))
  .pipe(destination());

function makeDependencies(filename) {
  return function bundleDeps() {
    return gulp.src([
  '../../public/javascripts/vendor/jquery.min.js',
  './dist/jquery.fill.js',
  './dep/powertip.min.js',
  './dep/howler.min.js',
  './dep/mousetrap.min.js',
  './dist/consolemsg.js',
  ...(abFile ? ['./dist/ab.js'] : []),
])
      .pipe(concat(filename))
      .pipe(destination());
  };
}

function makeBundle(filename) {
  return function bundleItAll() {
    return gulp.src([
      destinationPath + 'lichess.deps.js',
      './dist/' + filename,
    ])
      .pipe(concat(filename.replace('source.', '')))
      .pipe(destination());
  };
}

const gitSha = (cb) => exec("git rev-parse -q --short HEAD", function (err, stdout) {
  if (err) throw err;
  if (!fs.existsSync('./dist')) fs.mkdirSync('./dist');
  var date = new Date().toISOString().split('.')[0];
  fs.writeFileSync('./dist/consolemsg.js',
    'window.lichess=window.lichess||{};console.info("Lichess is open source! https://github.com/ornicar/lila");' +
    `lichess.info = "Assets built ${date} from sha ${stdout.trim()}";`);
  cb();
});

const standalonesJs = () => gulp.src([
  'util.js', 'trans.js', 'tv.js', 'puzzle.js', 'user.js', 'coordinate.js', 'captcha.js', 'embed-analyse.js'
].map(f => `src/standalones/${f}`))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(destination());

const userMod = () => browserify(browserifyOpts('./src/user-mod.js', false))
  .bundle()
  .pipe(source('user-mod.js'))
  .pipe(buffer())
  .pipe(uglify())
  .pipe(destination());

const tasks = [gitSha, jqueryFill, ab, standalonesJs, userMod, stockfishWasm, stockfishMvWasm, stockfishPexe, stockfishJs];

const dev = gulp.series(tasks.concat([devSource]));

const deps = makeDependencies('lichess.deps.js');

gulp.task('prod', gulp.series(tasks, deps, prodSource, makeBundle(`${fileBaseName}.source.min.js`)));
gulp.task('dev', dev, deps);
gulp.task('default', gulp.series(dev, deps, () => gulp.watch('src/**/*.js', dev)));

gulp.task('deps', gulp.series(tasks, deps));
<|endoftext|>


<commit_before>/*
 */
'use strict';
const _cloneDeep = require('lodash/cloneDeep');
/*
 *
 */
Entry.FieldDropdownDynamic = function(content, blockView, index) {
    this._block = blockView.block;
    this._blockView = blockView;

    var box = new Entry.BoxModel();
    this.box = box;

    this.svgGroup = null;

    this._contents = content;

    if(content.needDeepCopy) {
        this._contents = _cloneDeep(content);
    }

    this._index = index;

    var arrowColor = content.arrowColor;
    if (
        this._block.deletable === Entry.Block.DELETABLE_FALSE_LIGHTEN ||
        this._block.emphasized
    ) {
        arrowColor = blockView._fillColor;
    }

    this._arrowColor = arrowColor;

    var menuName = this._contents.menuName;

    if (_.isFunction(menuName)) this._menuGenerator = menuName;
    else this._menuName = menuName;

    this._CONTENT_HEIGHT = this.getContentHeight(content.dropdownHeight);

    this._font_size = this.getFontSize(content.fontSize);

    this._ROUND = content.roundValue || 3;
    this.renderStart(blockView);
    if (
        blockView &&
        blockView.getBoard() &&
        blockView.getBoard().workspace &&
        blockView.getBoard().workspace.changeEvent
    ) {
        blockView
            .getBoard()
            .workspace.changeEvent.attach(this, this._updateValue);
    }

    this.optionChangeTriggeredEvent();
};

Entry.Utils.inherit(Entry.FieldDropdown, Entry.FieldDropdownDynamic);

(function(p) {
    p.constructor = Entry.FieldDropDownDynamic;

    p._updateValue = function() {
        var object = this._block.getCode().object;
        var options = [];
        if (Entry.container) {
            if (this._menuName)
                options = Entry.container.getDropdownList(
                    this._menuName,
                    object
                );
            else options = this._menuGenerator();
        }

        this._contents.options = options;
        var value = this.getValue();
        if (this._blockView.isInBlockMenu || !value || value == 'null')
            value = options.length !== 0 ? options[0][1] : null;

        this._updateOptions();
        this.setValue(value);
    };

    p.renderOptions = function() {
        var that = this;

        this._attachDisposeEvent(() => {
            that.destroyOption(undefined, true);
        });

        this.optionGroup = Entry.Dom('ul', {
            class: 'entry-widget-dropdown',
            parent: $('body'),
        });

        var options;
        if (this._menuName)
            options = Entry.container.getDropdownList(this._contents.menuName);
        else options = this._menuGenerator();

        this._contents.options = options;

        var OPTION_X_PADDING = 30;
        var maxWidth = 0;

        var CONTENT_HEIGHT = this._CONTENT_HEIGHT + 4;

        this.optionGroup.bind('mousedown touchstart', (e) =>
            e.stopPropagation()
        );

        this.optionGroup.on('mouseup', '.rect', function(e) {
            e.stopPropagation();
            that.applyValue(this._value);
            that.destroyOption(undefined, true);
            that._selectBlockView();
        });

        var fragment = document.createDocumentFragment();
        options.forEach((option) => {
            var text = (option[0] = this._convert(option[0], option[1]));
            var value = option[1];
            var element = Entry.Dom('li', {
                class: 'rect',
            });
            var elem = element[0];
            elem._value = value;

            var left = Entry.Dom('span', {
                class: 'left',
                parent: element,
            });

            if (this.getValue() == value) left.text('\u2713');

            Entry.Dom('span', {
                class: 'right',
                parent: element,
            }).text(text);

            fragment.appendChild(elem);
        });

        this.optionGroup[0].appendChild(fragment);
        this._position();

        this.optionDomCreated();
    };

    p.optionChangeTriggeredEvent = function() {
        const that = this;
        const targetIndex = this._contents.targetIndex;

        if(typeof targetIndex === "undefined") {
            return ;
        }

        $(this._blockView.contentSvgGroup).on('optionChanged', function(e, data) {
            if( that._block == data.block && targetIndex == data.index) {
                let options = that._menuGenerator(data.value);
                that._contents.options = options;
                that.applyValue(options[0][1]);
            }
        });

    };
})(Entry.FieldDropdownDynamic.prototype);
<commit_msg>[#9233]  dynamic dropdown  오프라인 로드시 값 불오는부분 수정.<commit_after>/*
 */
'use strict';
const _cloneDeep = require('lodash/cloneDeep');
/*
 *
 */
Entry.FieldDropdownDynamic = function(content, blockView, index) {
    this._block = blockView.block;
    this._blockView = blockView;

    var box = new Entry.BoxModel();
    this.box = box;

    this.svgGroup = null;

    this._contents = content;

    if(content.needDeepCopy) {
        this._contents = _cloneDeep(content);
    }

    this._index = index;

    var arrowColor = content.arrowColor;
    if (
        this._block.deletable === Entry.Block.DELETABLE_FALSE_LIGHTEN ||
        this._block.emphasized
    ) {
        arrowColor = blockView._fillColor;
    }

    this._arrowColor = arrowColor;

    var menuName = this._contents.menuName;

    if (_.isFunction(menuName)) this._menuGenerator = menuName;
    else this._menuName = menuName;

    this._CONTENT_HEIGHT = this.getContentHeight(content.dropdownHeight);

    this._font_size = this.getFontSize(content.fontSize);

    this._ROUND = content.roundValue || 3;
    this.renderStart(blockView);
    if (
        blockView &&
        blockView.getBoard() &&
        blockView.getBoard().workspace &&
        blockView.getBoard().workspace.changeEvent
    ) {
        blockView
            .getBoard()
            .workspace.changeEvent.attach(this, this._updateValue);
    }

    this.optionChangeTriggeredEvent();
};

Entry.Utils.inherit(Entry.FieldDropdown, Entry.FieldDropdownDynamic);

(function(p) {
    p.constructor = Entry.FieldDropDownDynamic;

    p.getIndexValue = function() {
        if(this._contents.targetIndex >= 0) {
            return this._block.data.params[this._contents.targetIndex];
        }
        return null;
    }

    p._updateValue = function() {
        var object = this._block.getCode().object;
        var options = [];
        if (Entry.container) {
            if (this._menuName)
                options = Entry.container.getDropdownList(
                    this._menuName,
                    object
                );
            else options = this._menuGenerator(this.getIndexValue());
        }

        this._contents.options = options;
        var value = this.getValue();
        if (this._blockView.isInBlockMenu || !value || value == 'null')
            value = options.length !== 0 ? options[0][1] : null;

        this._updateOptions();
        this.setValue(value);
    };

    p.renderOptions = function() {
        var that = this;

        this._attachDisposeEvent(() => {
            that.destroyOption(undefined, true);
        });

        this.optionGroup = Entry.Dom('ul', {
            class: 'entry-widget-dropdown',
            parent: $('body'),
        });

        var options;
        if (this._menuName)
            options = Entry.container.getDropdownList(this._contents.menuName);
        else options = this._menuGenerator();

        this._contents.options = options;

        var OPTION_X_PADDING = 30;
        var maxWidth = 0;

        var CONTENT_HEIGHT = this._CONTENT_HEIGHT + 4;

        this.optionGroup.bind('mousedown touchstart', (e) =>
            e.stopPropagation()
        );

        this.optionGroup.on('mouseup', '.rect', function(e) {
            e.stopPropagation();
            that.applyValue(this._value);
            that.destroyOption(undefined, true);
            that._selectBlockView();
            $(that._blockView.contentSvgGroup).trigger('optionChanged', {block:that._block, value:that.getValue(), index:that._index});
        });

        var fragment = document.createDocumentFragment();
        options.forEach((option) => {
            var text = (option[0] = this._convert(option[0], option[1]));
            var value = option[1];
            var element = Entry.Dom('li', {
                class: 'rect',
            });
            var elem = element[0];
            elem._value = value;

            var left = Entry.Dom('span', {
                class: 'left',
                parent: element,
            });

            if (this.getValue() == value) left.text('\u2713');

            Entry.Dom('span', {
                class: 'right',
                parent: element,
            }).text(text);

            fragment.appendChild(elem);
        });

        this.optionGroup[0].appendChild(fragment);
        this._position();

        this.optionDomCreated();
    };

    p.optionChangeTriggeredEvent = function() {
        const that = this;
        const targetIndex = this._contents.targetIndex;

        if(typeof targetIndex === "undefined") {
            return ;
        }

        $(this._blockView.contentSvgGroup).on('optionChanged', function(e, data) {
            if( that._block == data.block && targetIndex == data.index) {
                let options = that._menuGenerator(data.value);
                that._contents.options = options;
                that.applyValue(options[0][1]);
            }
        });

    };
})(Entry.FieldDropdownDynamic.prototype);
<|endoftext|>


<commit_before>var lichess = window.lichess = window.lichess || {};

lichess.engineName = 'Stockfish 8';

lichess.raf = (window.requestAnimationFrame || window.setTimeout).bind(window);
lichess.requestIdleCallback = (window.requestIdleCallback || window.setTimeout).bind(window);
lichess.storage = (function() {
  var withStorage = function(f) {
    // can throw an exception when storage is full
    try {
      return !!window.localStorage ? f(window.localStorage) : null;
    } catch (e) {}
  }
  return {
    get: function(k) {
      return withStorage(function(s) {
        return s.getItem(k);
      });
    },
    set: function(k, v) {
      // removing first may help http://stackoverflow.com/questions/2603682/is-anyone-else-receiving-a-quota-exceeded-err-on-their-ipad-when-accessing-local
      withStorage(function(s) {
    console.log('set', k, v);
        s.removeItem(k);
        s.setItem(k, v);
      });
    },
    remove: function(k) {
      withStorage(function(s) {
        s.removeItem(k);
      });
    },
    make: function(k) {
      return {
        get: function() {
          return lichess.storage.get(k);
        },
        set: function(v) {
          return lichess.storage.set(k, v);
        },
        remove: function() {
          return lichess.storage.remove(k);
        },
        listen: function(f) {
          window.addEventListener('storage', function(e) {
            if (e.key === k && e.newValue !== null) f(e);
          });
        }
      };
    }
  };
})();
lichess.reloadOtherTabs = (function() {
  var storage = lichess.storage.make('reload-other-tabs');
  storage.listen(function() {
    lichess.reload();
  });
  return function() {
    storage.set(1);
  }
})();
lichess.once = function(key, mod) {
  if (mod === 'always') return true;
  if (!lichess.storage.get(key)) {
    lichess.storage.set(key, 1);
    return true;
  }
  return false;
};
lichess.powertip = (function() {

  var elementIdContains = function(id, contained) {
    var el = document.getElementById(id);
    return el && el.contains(contained);
  };

  var onPowertipPreRender = function(id) {
    return function() {
      $.ajax({
        url: ($(this).data('href') || $(this).attr('href')).replace(/\?.+$/, '') + '/mini',
        success: function(html) {
          $('#' + id).html(html);
          lichess.pubsub.emit('content_loaded')();
        }
      });
    };
  };

  var userPowertip = function(el, pos) {
    if (!pos) {
      if (elementIdContains('site_header', el)) pos = 'e';
      else pos = el.getAttribute('data-pt-pos') || 'w';
    }
    $(el).removeClass('ulpt').powerTip({
      intentPollInterval: 200,
      placement: pos,
      mouseOnToPopup: true,
      closeDelay: 200
    }).on({
      powerTipPreRender: onPowertipPreRender('powerTip')
    }).data('powertip', lichess.spinnerHtml);
  };

  var gamePowertip = function(el) {
    $(el).removeClass('glpt').powerTip({
      intentPollInterval: 200,
      placement: 'w',
      smartPlacement: true,
      mouseOnToPopup: true,
      closeDelay: 200,
      popupId: 'miniGame'
    }).on({
      powerTipPreRender: onPowertipPreRender('miniGame')
    }).data('powertip', lichess.spinnerHtml);
  };

  var powerTipWith = function(el, ev, f) {
    f(el);
    $.powerTip.show(el, ev);
  };

  return {
    mouseover: function(e) {
      var t = e.target,
        cl = t.classList;
      if (cl.contains('ulpt')) powerTipWith(t, e, userPowertip);
      else if (cl.contains('glpt')) powerTipWith(t, e, gamePowertip);
    },
    manualGameIn: function(parent) {
      Array.prototype.forEach.call(parent.querySelectorAll('.glpt'), gamePowertip);
    },
    manualUserIn: function(parent) {
      Array.prototype.forEach.call(parent.querySelectorAll('.ulpt'), function(el) {
        userPowertip(el);
      });
    }
  };
})();
lichess.widget = function(name, prototype) {
  var constructor = $[name] = function(options, element) {
    var self = this;
    self.element = $(element);
    $.data(element, name, self);
    self.options = options;
    self._create();
  };
  constructor.prototype = prototype;
  $.fn[name] = function(method) {
    var returnValue = this;
    var args = Array.prototype.slice.call(arguments, 1);
    if (typeof method === 'string') this.each(function() {
      var instance = $.data(this, name);
      if (!instance) return;
      if (!$.isFunction(instance[method]) || method.charAt(0) === "_")
        return $.error("no such method '" + method + "' for " + name + " widget instance");
      returnValue = instance[method].apply(instance, args);
    });
    else this.each(function() {
      if ($.data(this, name)) return $.error("widget " + name + " already bound to " + this);
      $.data(this, name, new constructor(method, this));
    });
    return returnValue;
  };
};
lichess.isTrident = navigator.userAgent.indexOf('Trident/') > -1;
lichess.isChrome = navigator.userAgent.indexOf('Chrome/') > -1;
lichess.spinnerHtml = '
<svg viewBox="0 0 40 40"><circle cx=20 cy=20 r=18 fill="none"></circle></svg>
';
lichess.initiatingHtml = '
' + lichess.spinnerHtml + '
';
lichess.assetUrl = function(url, opts) {
  opts = opts || {};
  var baseUrl = opts.sameDomain ? '' : document.body.getAttribute('data-asset-url');
  var version = document.body.getAttribute('data-asset-version');
  return baseUrl + url + (opts.noVersion ? '' : '?v=' + version);
};
lichess.loadedCss = {};
lichess.loadCss = function(url) {
  if (lichess.loadedCss[url]) return;
  lichess.loadedCss[url] = true;
  $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', lichess.assetUrl(url)));
};
lichess.loadScript = function(url, opts) {
  return $.ajax({
    dataType: "script",
    cache: true,
    url: lichess.assetUrl(url, opts)
  });
};
lichess.hopscotch = function(f) {
  lichess.loadCss('/assets/vendor/hopscotch/dist/css/hopscotch.min.css');
  lichess.loadScript("/assets/vendor/hopscotch/dist/js/hopscotch.min.js", {noVersion:true}).done(f);
}
lichess.slider = function() {
  lichess.loadCss('/assets/stylesheets/jquery-ui.css');
  return lichess.loadScript('/assets/javascripts/vendor/jquery-ui.slider.min.js', {noVersion:true});
};
lichess.shepherd = function(f) {
  var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'default' : 'dark');
  lichess.loadCss('/assets/vendor/shepherd/dist/css/' + theme + '.css');
  lichess.loadCss('/assets/stylesheets/shepherd.css');
  lichess.loadScript("/assets/vendor/shepherd/dist/js/tether.js", {noVersion:true}).done(function() {
    lichess.loadScript("/assets/vendor/shepherd/dist/js/shepherd.min.js", {noVersion:true}).done(function() {
      f(theme);
    });
  });
};
lichess.makeChat = function(id, data, callback) {
  var isDev = document.body.getAttribute('data-dev');
  data.loadCss = lichess.loadCss;
  lichess.loadScript("/assets/compiled/lichess.chat" + (isDev ? '' : '.min') + '.js').done(function() {
    (callback || $.noop)(LichessChat.default(document.getElementById(id), data));
  });
};

lichess.desktopNotification = (function() {
  var notifications = [];
  function closeAll() {
    notifications.forEach(function(n) {
      n.close();
    });
    notifications = [];
  };
  window.addEventListener('focus', closeAll);
  var storage = lichess.storage.make('just-notified');
  function notify(msg) {
    var now = Date.now();
    if (document.hasFocus() || now - storage.get() < 1000) return;
    storage.set(now);
    if ($.isFunction(msg)) msg = msg();
    var notification = new Notification('lichess.org', {
      icon: '//lichess1.org/assets/images/logo.256.png',
      body: msg
    });
    notification.onclick = function() {
      window.focus();
    };
    notifications.push(notification);
  };
  return function(msg) {
    if (document.hasFocus() || !('Notification' in window)) return;
    if (Notification.permission === 'granted') {
      // increase chances that the first tab can put a local storage lock
      setTimeout(notify, 10 + Math.random() * 500, msg);
    } else if (Notification.permission !== 'denied') {
      Notification.requestPermission(function(p) {
        if (p === 'granted') notify(msg);
      });
    };
  };
})();

lichess.numberFormat = (function() {
  var formatter = false;
  return function(n) {
    if (formatter === false) formatter = (window.Intl && Intl.NumberFormat) ? new Intl.NumberFormat() : null;
    if (formatter === null) return n;
    return formatter.format(n);
  };
})();
lichess.idleTimer = function(delay, onIdle, onWakeUp) {
  var events = ['mousemove', 'touchstart'];
  var listening = false;
  var active = true;
  var lastSeenActive = Date.now();
  var onActivity = function() {
    if (!active) {
      // console.log('Wake up');
      onWakeUp();
    }
    active = true;
    lastSeenActive = Date.now();
    stopListening();
  };
  var startListening = function() {
    if (!listening) {
      events.forEach(function(e) {
        document.addEventListener(e, onActivity);
      });
      listening = true;
    }
  };
  var stopListening = function() {
    if (listening) {
      events.forEach(function(e) {
        document.removeEventListener(e, onActivity);
      });
      listening = false;
    }
  };
  setInterval(function() {
    if (active && Date.now() - lastSeenActive > delay) {
      // console.log('Idle mode');
      onIdle();
      active = false;
    }
    startListening();
  }, 10000);
};
lichess.pubsub = (function() {
  var subs = [];
  return {
    on: function(name, cb) {
      subs[name] = subs[name] || [];
      subs[name].push(cb);
    },
    off: function(name, cb) {
      if (!subs[name]) return;
      for (var i in subs[name]) {
        if (subs[name][i] === cb) {
          subs[name].splice(i);
          break;
        }
      }
    },
    emit: function(name) {
      return function() {
        if (!subs[name]) return;
        var args = Array.prototype.slice.call(arguments, 0);
        for (var i in subs[name]) subs[name][i].apply(null, args);
      }
    }
  };
})();
// wtf was I thinking
lichess.partial = function() {
  return arguments[0].bind.apply(arguments[0], [null].concat(Array.prototype.slice.call(arguments, 1)));
};
lichess.hasToReload = false;
lichess.redirectInProgress = false;
lichess.reload = function() {
  if (lichess.redirectInProgress) return;
  lichess.hasToReload = true;
  if (window.location.hash) location.reload();
  else location.href = location.href;
};
lichess.escapeHtml = function(html) {
  var div = document.createElement('div');
  div.appendChild(document.createTextNode(html));
  return div.innerHTML;
};
lichess.toYouTubeEmbedUrl = function(url) {
  var m = url.match(/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch)?(?:\?v=)?([^"&?\/ ]{11})(?:\?|&|)(\S*)/i);
  if (!m) return;
  var start = 1;
  m[2].split('&').forEach(function(p) {
    var s = p.split('=');
    if (s[0] === 't' || s[0] === 'start') {
      if (s[1].match(/^\d+$/)) start = parseInt(s[1]);
      else {
        var n = s[1].match(/(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?/);
        start = (parseInt(n[1]) || 0) * 3600 + (parseInt(n[2]) || 0) * 60 + (parseInt(n[3]) || 0);
      }
    }
  });
  var params = 'modestbranding=1&rel=0&controls=2&iv_load_policy=3&start=' + start;
  return 'https://www.youtube.com/embed/' + m[1] + '?' + params;
};
$.spreadNumber = function(el, nbSteps, getDuration, previous) {
  var previous = previous,
    displayed;
  var display = function(prev, cur, it) {
    var val = lichess.numberFormat(Math.round(((prev * (nbSteps - 1 - it)) + (cur * (it + 1))) / nbSteps));
    if (val !== displayed) {
      el.textContent = val;
      displayed = val;
    }
  };
  var timeouts = [];
  return function(nb, overrideNbSteps) {
    if (!el || (!nb && nb !== 0)) return;
    if (overrideNbSteps) nbSteps = Math.abs(overrideNbSteps);
    timeouts.forEach(clearTimeout);
    timeouts = [];
    var prev = previous === 0 ? 0 : (previous || nb);
    previous = nb;
    var interv = Math.abs(getDuration() / nbSteps);
    for (var i = 0; i < nbSteps; i++)
      timeouts.push(setTimeout(display.bind(null, prev, nb, i), Math.round(i * interv)));
  };
};
$.fn.scrollTo = function(target, offsetTop) {
  return this.each(function() {
    if (typeof target === "number") this.scrollTop  = target;
    else {
      var $t = $(target);
      if (!$t.length) return;
      this.scrollTop = $t.offset().top + this.scrollTop - (offsetTop || 0);
    }
  });
};
$.modal = function(html) {
  if (!html.clone) html = $('
' + html + '
');
  var $wrap = $('
').html(html.clone().removeClass('none').show()).prepend('');
  var $overlay = $('
').html($wrap);
  $overlay.add($wrap.find('.close')).one('click', $.modal.close);
  $wrap.click(function(e) {
    e.stopPropagation();
  });
  $('body').prepend($overlay);
  return $wrap;
};
$.modal.close = function() {
  $('#modal-overlay').remove();
};

// polyfills

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    value: function(predicate) {
      for (var i in this) if (predicate(this[i])) return this[i];
    }
  });
}
<commit_msg>remove debug<commit_after>var lichess = window.lichess = window.lichess || {};

lichess.engineName = 'Stockfish 8';

lichess.raf = (window.requestAnimationFrame || window.setTimeout).bind(window);
lichess.requestIdleCallback = (window.requestIdleCallback || window.setTimeout).bind(window);
lichess.storage = (function() {
  var withStorage = function(f) {
    // can throw an exception when storage is full
    try {
      return !!window.localStorage ? f(window.localStorage) : null;
    } catch (e) {}
  }
  return {
    get: function(k) {
      return withStorage(function(s) {
        return s.getItem(k);
      });
    },
    set: function(k, v) {
      // removing first may help http://stackoverflow.com/questions/2603682/is-anyone-else-receiving-a-quota-exceeded-err-on-their-ipad-when-accessing-local
      withStorage(function(s) {
        s.removeItem(k);
        s.setItem(k, v);
      });
    },
    remove: function(k) {
      withStorage(function(s) {
        s.removeItem(k);
      });
    },
    make: function(k) {
      return {
        get: function() {
          return lichess.storage.get(k);
        },
        set: function(v) {
          return lichess.storage.set(k, v);
        },
        remove: function() {
          return lichess.storage.remove(k);
        },
        listen: function(f) {
          window.addEventListener('storage', function(e) {
            if (e.key === k && e.newValue !== null) f(e);
          });
        }
      };
    }
  };
})();
lichess.reloadOtherTabs = (function() {
  var storage = lichess.storage.make('reload-other-tabs');
  storage.listen(function() {
    lichess.reload();
  });
  return function() {
    storage.set(1);
  }
})();
lichess.once = function(key, mod) {
  if (mod === 'always') return true;
  if (!lichess.storage.get(key)) {
    lichess.storage.set(key, 1);
    return true;
  }
  return false;
};
lichess.powertip = (function() {

  var elementIdContains = function(id, contained) {
    var el = document.getElementById(id);
    return el && el.contains(contained);
  };

  var onPowertipPreRender = function(id) {
    return function() {
      $.ajax({
        url: ($(this).data('href') || $(this).attr('href')).replace(/\?.+$/, '') + '/mini',
        success: function(html) {
          $('#' + id).html(html);
          lichess.pubsub.emit('content_loaded')();
        }
      });
    };
  };

  var userPowertip = function(el, pos) {
    if (!pos) {
      if (elementIdContains('site_header', el)) pos = 'e';
      else pos = el.getAttribute('data-pt-pos') || 'w';
    }
    $(el).removeClass('ulpt').powerTip({
      intentPollInterval: 200,
      placement: pos,
      mouseOnToPopup: true,
      closeDelay: 200
    }).on({
      powerTipPreRender: onPowertipPreRender('powerTip')
    }).data('powertip', lichess.spinnerHtml);
  };

  var gamePowertip = function(el) {
    $(el).removeClass('glpt').powerTip({
      intentPollInterval: 200,
      placement: 'w',
      smartPlacement: true,
      mouseOnToPopup: true,
      closeDelay: 200,
      popupId: 'miniGame'
    }).on({
      powerTipPreRender: onPowertipPreRender('miniGame')
    }).data('powertip', lichess.spinnerHtml);
  };

  var powerTipWith = function(el, ev, f) {
    f(el);
    $.powerTip.show(el, ev);
  };

  return {
    mouseover: function(e) {
      var t = e.target,
        cl = t.classList;
      if (cl.contains('ulpt')) powerTipWith(t, e, userPowertip);
      else if (cl.contains('glpt')) powerTipWith(t, e, gamePowertip);
    },
    manualGameIn: function(parent) {
      Array.prototype.forEach.call(parent.querySelectorAll('.glpt'), gamePowertip);
    },
    manualUserIn: function(parent) {
      Array.prototype.forEach.call(parent.querySelectorAll('.ulpt'), function(el) {
        userPowertip(el);
      });
    }
  };
})();
lichess.widget = function(name, prototype) {
  var constructor = $[name] = function(options, element) {
    var self = this;
    self.element = $(element);
    $.data(element, name, self);
    self.options = options;
    self._create();
  };
  constructor.prototype = prototype;
  $.fn[name] = function(method) {
    var returnValue = this;
    var args = Array.prototype.slice.call(arguments, 1);
    if (typeof method === 'string') this.each(function() {
      var instance = $.data(this, name);
      if (!instance) return;
      if (!$.isFunction(instance[method]) || method.charAt(0) === "_")
        return $.error("no such method '" + method + "' for " + name + " widget instance");
      returnValue = instance[method].apply(instance, args);
    });
    else this.each(function() {
      if ($.data(this, name)) return $.error("widget " + name + " already bound to " + this);
      $.data(this, name, new constructor(method, this));
    });
    return returnValue;
  };
};
lichess.isTrident = navigator.userAgent.indexOf('Trident/') > -1;
lichess.isChrome = navigator.userAgent.indexOf('Chrome/') > -1;
lichess.spinnerHtml = '
<svg viewBox="0 0 40 40"><circle cx=20 cy=20 r=18 fill="none"></circle></svg>
';
lichess.initiatingHtml = '
' + lichess.spinnerHtml + '
';
lichess.assetUrl = function(url, opts) {
  opts = opts || {};
  var baseUrl = opts.sameDomain ? '' : document.body.getAttribute('data-asset-url');
  var version = document.body.getAttribute('data-asset-version');
  return baseUrl + url + (opts.noVersion ? '' : '?v=' + version);
};
lichess.loadedCss = {};
lichess.loadCss = function(url) {
  if (lichess.loadedCss[url]) return;
  lichess.loadedCss[url] = true;
  $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', lichess.assetUrl(url)));
};
lichess.loadScript = function(url, opts) {
  return $.ajax({
    dataType: "script",
    cache: true,
    url: lichess.assetUrl(url, opts)
  });
};
lichess.hopscotch = function(f) {
  lichess.loadCss('/assets/vendor/hopscotch/dist/css/hopscotch.min.css');
  lichess.loadScript("/assets/vendor/hopscotch/dist/js/hopscotch.min.js", {noVersion:true}).done(f);
}
lichess.slider = function() {
  lichess.loadCss('/assets/stylesheets/jquery-ui.css');
  return lichess.loadScript('/assets/javascripts/vendor/jquery-ui.slider.min.js', {noVersion:true});
};
lichess.shepherd = function(f) {
  var theme = 'shepherd-theme-' + ($('body').hasClass('dark') ? 'default' : 'dark');
  lichess.loadCss('/assets/vendor/shepherd/dist/css/' + theme + '.css');
  lichess.loadCss('/assets/stylesheets/shepherd.css');
  lichess.loadScript("/assets/vendor/shepherd/dist/js/tether.js", {noVersion:true}).done(function() {
    lichess.loadScript("/assets/vendor/shepherd/dist/js/shepherd.min.js", {noVersion:true}).done(function() {
      f(theme);
    });
  });
};
lichess.makeChat = function(id, data, callback) {
  var isDev = document.body.getAttribute('data-dev');
  data.loadCss = lichess.loadCss;
  lichess.loadScript("/assets/compiled/lichess.chat" + (isDev ? '' : '.min') + '.js').done(function() {
    (callback || $.noop)(LichessChat.default(document.getElementById(id), data));
  });
};

lichess.desktopNotification = (function() {
  var notifications = [];
  function closeAll() {
    notifications.forEach(function(n) {
      n.close();
    });
    notifications = [];
  };
  window.addEventListener('focus', closeAll);
  var storage = lichess.storage.make('just-notified');
  function notify(msg) {
    var now = Date.now();
    if (document.hasFocus() || now - storage.get() < 1000) return;
    storage.set(now);
    if ($.isFunction(msg)) msg = msg();
    var notification = new Notification('lichess.org', {
      icon: '//lichess1.org/assets/images/logo.256.png',
      body: msg
    });
    notification.onclick = function() {
      window.focus();
    };
    notifications.push(notification);
  };
  return function(msg) {
    if (document.hasFocus() || !('Notification' in window)) return;
    if (Notification.permission === 'granted') {
      // increase chances that the first tab can put a local storage lock
      setTimeout(notify, 10 + Math.random() * 500, msg);
    } else if (Notification.permission !== 'denied') {
      Notification.requestPermission(function(p) {
        if (p === 'granted') notify(msg);
      });
    };
  };
})();

lichess.numberFormat = (function() {
  var formatter = false;
  return function(n) {
    if (formatter === false) formatter = (window.Intl && Intl.NumberFormat) ? new Intl.NumberFormat() : null;
    if (formatter === null) return n;
    return formatter.format(n);
  };
})();
lichess.idleTimer = function(delay, onIdle, onWakeUp) {
  var events = ['mousemove', 'touchstart'];
  var listening = false;
  var active = true;
  var lastSeenActive = Date.now();
  var onActivity = function() {
    if (!active) {
      // console.log('Wake up');
      onWakeUp();
    }
    active = true;
    lastSeenActive = Date.now();
    stopListening();
  };
  var startListening = function() {
    if (!listening) {
      events.forEach(function(e) {
        document.addEventListener(e, onActivity);
      });
      listening = true;
    }
  };
  var stopListening = function() {
    if (listening) {
      events.forEach(function(e) {
        document.removeEventListener(e, onActivity);
      });
      listening = false;
    }
  };
  setInterval(function() {
    if (active && Date.now() - lastSeenActive > delay) {
      // console.log('Idle mode');
      onIdle();
      active = false;
    }
    startListening();
  }, 10000);
};
lichess.pubsub = (function() {
  var subs = [];
  return {
    on: function(name, cb) {
      subs[name] = subs[name] || [];
      subs[name].push(cb);
    },
    off: function(name, cb) {
      if (!subs[name]) return;
      for (var i in subs[name]) {
        if (subs[name][i] === cb) {
          subs[name].splice(i);
          break;
        }
      }
    },
    emit: function(name) {
      return function() {
        if (!subs[name]) return;
        var args = Array.prototype.slice.call(arguments, 0);
        for (var i in subs[name]) subs[name][i].apply(null, args);
      }
    }
  };
})();
// wtf was I thinking
lichess.partial = function() {
  return arguments[0].bind.apply(arguments[0], [null].concat(Array.prototype.slice.call(arguments, 1)));
};
lichess.hasToReload = false;
lichess.redirectInProgress = false;
lichess.reload = function() {
  if (lichess.redirectInProgress) return;
  lichess.hasToReload = true;
  if (window.location.hash) location.reload();
  else location.href = location.href;
};
lichess.escapeHtml = function(html) {
  var div = document.createElement('div');
  div.appendChild(document.createTextNode(html));
  return div.innerHTML;
};
lichess.toYouTubeEmbedUrl = function(url) {
  var m = url.match(/(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch)?(?:\?v=)?([^"&?\/ ]{11})(?:\?|&|)(\S*)/i);
  if (!m) return;
  var start = 1;
  m[2].split('&').forEach(function(p) {
    var s = p.split('=');
    if (s[0] === 't' || s[0] === 'start') {
      if (s[1].match(/^\d+$/)) start = parseInt(s[1]);
      else {
        var n = s[1].match(/(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?/);
        start = (parseInt(n[1]) || 0) * 3600 + (parseInt(n[2]) || 0) * 60 + (parseInt(n[3]) || 0);
      }
    }
  });
  var params = 'modestbranding=1&rel=0&controls=2&iv_load_policy=3&start=' + start;
  return 'https://www.youtube.com/embed/' + m[1] + '?' + params;
};
$.spreadNumber = function(el, nbSteps, getDuration, previous) {
  var previous = previous,
    displayed;
  var display = function(prev, cur, it) {
    var val = lichess.numberFormat(Math.round(((prev * (nbSteps - 1 - it)) + (cur * (it + 1))) / nbSteps));
    if (val !== displayed) {
      el.textContent = val;
      displayed = val;
    }
  };
  var timeouts = [];
  return function(nb, overrideNbSteps) {
    if (!el || (!nb && nb !== 0)) return;
    if (overrideNbSteps) nbSteps = Math.abs(overrideNbSteps);
    timeouts.forEach(clearTimeout);
    timeouts = [];
    var prev = previous === 0 ? 0 : (previous || nb);
    previous = nb;
    var interv = Math.abs(getDuration() / nbSteps);
    for (var i = 0; i < nbSteps; i++)
      timeouts.push(setTimeout(display.bind(null, prev, nb, i), Math.round(i * interv)));
  };
};
$.fn.scrollTo = function(target, offsetTop) {
  return this.each(function() {
    if (typeof target === "number") this.scrollTop  = target;
    else {
      var $t = $(target);
      if (!$t.length) return;
      this.scrollTop = $t.offset().top + this.scrollTop - (offsetTop || 0);
    }
  });
};
$.modal = function(html) {
  if (!html.clone) html = $('
' + html + '
');
  var $wrap = $('
').html(html.clone().removeClass('none').show()).prepend('');
  var $overlay = $('
').html($wrap);
  $overlay.add($wrap.find('.close')).one('click', $.modal.close);
  $wrap.click(function(e) {
    e.stopPropagation();
  });
  $('body').prepend($overlay);
  return $wrap;
};
$.modal.close = function() {
  $('#modal-overlay').remove();
};

// polyfills

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    value: function(predicate) {
      for (var i in this) if (predicate(this[i])) return this[i];
    }
  });
}
<|endoftext|>


<commit_before>/***
|''Requires''|TiddlySpaceConfig ImageMacroPlugin|
!Code
***/
//{{{
(function($) {

if(!config.macros.image) {
	throw "Missing dependency: ImageMacroPlugin";
}

// hijack text viewer to add public/private icon
var _view = config.macros.view.views.text;
config.macros.view.views.text = function(value, place, params, wikifier,
		paramString, tiddler) {
	if(params[0] == "title") {
		var type = "private";
		if(store.tiddlerExists(tiddler.title) || store.isShadowTiddler(tiddler.title)) {
			var ns = config.extensions.tiddlyspace;
			var space = ns.determineSpace(tiddler, true);
			type = space && space.name == ns.currentSpace.name ? space.type : "external";
		}

		var tidEl = story.findContainingTiddler(place);
		$(tidEl).removeClass("private public external").addClass(type);

		if(type != "external") {
			invokeMacro(place, "image", "%0Icon alt:%0".format([type]), null, tiddler); // XXX: should call macro's function directly
		}
		else {
			var tiddlyweb = config.extensions.tiddlyweb;
			tiddlyweb.getStatus(function(status) {
				var server_host = status.server_host;
				var tsHost = server_host.scheme +"://" + server_host.host;
				if(server_host.port && server_host.port != '80' &&
					server_host.port != '443') {
					tsHost += ":" + server_host.port;
				}
				var spaceName = "tiddlyspace";
				if(space.name) {
					spaceName = "%0_public".format([space.name]);
				}
				var imageSrc = "%0/bags/%1/tiddlers/SiteIcon".format([tsHost,spaceName]);
				$("<img />").attr("src",imageSrc).attr("alt",spaceName).
				prependTo(place);
			});
		}
		$(place).attr("title", "private");
	}
	_view.apply(this, arguments);
};

})(jQuery);
//}}}
<commit_msg>code cleanup<commit_after>/***
|''Requires''|TiddlySpaceConfig ImageMacroPlugin|
!Code
***/
//{{{
(function($) {

if(!config.macros.image) {
	throw "Missing dependency: ImageMacroPlugin";
}

// hijack text viewer to add public/private icon
var _view = config.macros.view.views.text;
config.macros.view.views.text = function(value, place, params, wikifier,
		paramString, tiddler) {
	if(params[0] == "title") {
		var type = "private";
		if(store.tiddlerExists(tiddler.title) || store.isShadowTiddler(tiddler.title)) {
			var ns = config.extensions.tiddlyspace;
			var space = ns.determineSpace(tiddler, true);
			type = space && space.name == ns.currentSpace.name ? space.type : "external";
		}

		var tidEl = story.findContainingTiddler(place);
		$(tidEl).removeClass("private public external").addClass(type);

		if(type != "external") {
			invokeMacro(place, "image", "%0Icon alt:%0".format([type]), null, tiddler); // XXX: should call macro's function directly
		} else {
			var tiddlyweb = config.extensions.tiddlyweb;
			tiddlyweb.getStatus(function(status) {
				var server_host = status.server_host;
				var tsHost = server_host.scheme + "://" + server_host.host;
				if(server_host.port && server_host.port != "80" &&
					server_host.port != "443") {
					tsHost += ":" + server_host.port;
				}
				var spaceName = "tiddlyspace";
				if(space.name) {
					spaceName = "%0_public".format([space.name]);
				}
				var imageSrc = "%0/bags/%1/tiddlers/SiteIcon".format([tsHost,spaceName]);
				$("<img />").attr("src",imageSrc).attr("alt",spaceName).
				prependTo(place);
			});
		}
		$(place).attr("title", "private");
	}
	_view.apply(this, arguments);
};

})(jQuery);
//}}}
<|endoftext|>


<commit_before>/* Copyright (c) 2013-2015 Richard Rodger, MIT License */
/* jslint node:true, asi:true */
"use strict";

var fs      = require('fs');
var path    = require('path');
var _       = require('lodash');
var uuid    = require('node-uuid');
var levelup = require('level');
var levelQuery      = require('level-queryengine');
var jsonqueryEngine = require('jsonquery-engine');
var util = require('util');

var name    = "level-store";

module.exports = function(opts) {

    var desc;
    var dbmap = {};
    var seneca = this;

    opts = this.util.deepextend({
        folder: '.',
        valueEncoding: 'json',
        sync: false,
        fillCache: true
    }, opts);

    function fixquery(qent, q) {
        return null === q ? {} : _.isString(q) ? {id: q} : _.isString(q.id) ? q : q;
    }

    function filterdata(q, data) {
        for(var p in q) {
            if( !~p.indexOf('$') && q[p] != data[p] ) {
                return null;
            }
        }
        if( q.fields$ ) {
          data = _.omit(data, q.field$)
        }
        return data;
    }

    /**
     * sort$, skip$, limit$ and fields$ support.
     */
    function fixlist(q, list) {

        // sort first
        if (q.sort$) {
          for ( var sf in q.sort$ ) break;
          var sd = q.sort$[sf] < 0 ? -1 : 1

          list = _.sortBy(list, sf);
          if (sd == -1) {
              list.reverse()
          }

        }
        if (q.skip$) {
          list = list.slice(q.skip$)
        }

        if (q.limit$) {
          list = list.slice(0, q.limit$)
        }

        if (q.fields$) {
            list = _.map(list, function (item) {
                return _.pick(item, q.fields$);
            });
        }

        return list;
    }

    /**
    * Return if the folder exists, create otherwise.
    */
    function ensurefolder(folder, cb){
        fs.exists(folder, function(exists) {
          if( exists ) {
              return cb();
          }
          fs.mkdir(folder, function(err) {
            cb(err);
          });
        });
    }

    function makefolderpath(ent) {
        var canon = ent.canon$({object: true});

        var base  = canon.base;
        var name  = canon.name;

        var entfolder = (base ? base + '_' : '') + name;
        var folderpath = path.join(opts.folder, entfolder);
        return folderpath;
    }

    function error (done, win){
        return function(err, out){
          if (err)  {
              return done(err);
          }
          if (win) {
              return win(out);
          }
        }
    }

    /**
     * Get the levelup reference
     */
    var get_db = function get_db(ent, done) {
        var folder = makefolderpath(ent)
        var db = dbmap[folder];
        if (!db) {
          ensurefolder(folder, error(done, function(){
            db = dbmap[folder];
            if (db) {
                return done(null, db);
            }
            try {
                db = dbmap[folder] = levelQuery(levelup(folder, opts));
                db.query.use(jsonqueryEngine());
            } catch(e) {
                db = dbmap[folder];
                if (db) {
                    return done(null, db);
                }
                return done(e);
            }
            return done(null, db)
          }));
      } else {
          return done(null, db);
      }
    }

    var store = {
        name: name,

        close: function(done) {
            // Closes all the dbs
            seneca.util.recurse(_.values(dbmap), function(db, next) {
                db.close(next);
            }, done);
        },

        save: function(args, done) {

            var ent = args.ent
            var update = !!ent.id

            if (!update) {
                ent.id = void 0 != ent.id$ ? ent.id$ : uuid();
                delete(ent.id$)
            }

            get_db(ent, error(done, function(db) {
                    db.put(ent.id, ent.data$(false), function(err) {
                        seneca.log.debug('save/'+(update? 'update' : 'insert'), ent, desc);
                            done(null, ent);
                    });
                })
            );
        },

        load: function(args, done) {

            var qent = args.qent;
            var q    = args.q;
            var qq = fixquery(qent, q);

            if (qq.id) {
                get_db(qent, error(done, function(db) {
                    db.get( qq.id, function(err, data) {
                        if (err) {
                            if (err.notFound) {
                                return done(null, null);
                            }
                            return done(err);
                        }
                        var fent = qent.make$(data);
                        seneca.log.debug('load', q, fent, desc);
                        done(null, fent);
                    });
                }));
            } else {
              store.list(args, error(done, function(list){
                  done(null, list[0])
              }))
          }
        },

        remove: function(args, done) {

            var qent = args.qent;
            var q    = args.q;

            // all that match the query, of course
            var all  = q.all$; // default false
            var load  = _.isUndefined(q.load$) ? true : q.load$; // default true
            var qq = fixquery(qent,q);

            get_db(qent, error(done, function(db) {
                if( !_.isUndefined(qq.id) && load && !all ) {
                  db.get(qq.id, {fillCache:opts.fillCache}, function(err, data){
                      if (err) {
                          if (err.notFound) {
                              return done(null, null);
                          }
                          return done(err);
                      }
                      var fent = qent.make$(data);
                      do_remove(db, fent);
                  })
                } else {
                do_remove(db);
                }
            }));

            function do_remove(db, maybefent) {
                if ( _.isUndefined(qq.id) ) {
                    if (all) {
                        store.list(args, error(done, function(list) {
                            seneca.util.recurse(
                                list,
                                function(fent, next) {
                                  db.del(fent.id, {sync:opts.sync}, next);
                                },
                                function(err){
                                  seneca.log.debug('remove/all', q, desc);
                                  done(err);
                                }
                            );
                        }));
                    } else {
                        store.load(args, error(done, function(item){
                            if (item) {
                                db.del( item.id, {sync:opts.sync}, function(err){
                                    if( err ) return done(err);
                                    seneca.log.debug('remove/one', q, item, desc);
                                    done(null,item);
                                });
                            } else {
                                return done();
                            }
                        }));
                    }
            } else {
              db.del(qq.id, {sync:opts.sync}, function(err) {
                if (err) {
                    return done(err);
                }
                seneca.log.debug('remove/one', q, maybefent, desc);
                done(null, maybefent);
              })
            }
          }
        },

        list: function(args, done) {
            var qent = args.qent;
            var q    = args.q;

            get_db(qent, error(done, function(db){
                //var mq = metaquery(qent, q);
                var n = 1;
                var list = [];

                // saves the sorting / limit / skip / fields params
                var additionaParams = {
                    sort$: q.sort$,
                    limit$: q.limit$,
                    skip$: q.skip$,
                    fields$: q.fields$
                }
                // Remove the optional params from the query
                q = _.omit(q, ['sort$', 'limit$', 'skip$', 'fields$']);
                if (q.all$) { // useless
                    q = {};
                }

                db.query(q)
                    .on('data', function (data) {
                        if (filterdata( q, data ) ) {
                          list.push( qent.make$(data) )
                        }
                    })
                    .on('error', function (err) {
                        if(--n) throw new Error('errored:'+ n)
                        done(err)
                      })
                    .on('close', function () {
                    // do nothing, I guess
                    })
                    .on('end', function () {
                        if(--n) throw new Error('ended:'+ n);
                        // Order / skip / fields mngmt
                        list = fixlist(additionaParams, list)
                        seneca.log.debug('list', q, list.length, list[0],desc);
                        done(null,list);
                    });
            }))
        },

        native: function(args,done) {
            get_db(args.ent, function(err, db) {
                done(err, db);
            });
        }
    };

    var storedesc = seneca.store.init(seneca, opts, store);

    return {name:store.name, tag:storedesc.tag};
}
<commit_msg>close had incorrect signature<commit_after>/* Copyright (c) 2013-2015 Richard Rodger, MIT License */
/* jslint node:true, asi:true */
"use strict";

var fs      = require('fs');
var path    = require('path');
var _       = require('lodash');
var uuid    = require('node-uuid');
var levelup = require('level');
var levelQuery      = require('level-queryengine');
var jsonqueryEngine = require('jsonquery-engine');
var util = require('util');

var name    = "level-store";

module.exports = function(opts) {

    var desc;
    var dbmap = {};
    var seneca = this;

    opts = this.util.deepextend({
        folder: '.',
        valueEncoding: 'json',
        sync: false,
        fillCache: true
    }, opts);

    function fixquery(qent, q) {
        return null === q ? {} : _.isString(q) ? {id: q} : _.isString(q.id) ? q : q;
    }

    function filterdata(q, data) {
        for(var p in q) {
            if( !~p.indexOf('$') && q[p] != data[p] ) {
                return null;
            }
        }
        if( q.fields$ ) {
          data = _.omit(data, q.field$)
        }
        return data;
    }

    /**
     * sort$, skip$, limit$ and fields$ support.
     */
    function fixlist(q, list) {

        // sort first
        if (q.sort$) {
          for ( var sf in q.sort$ ) break;
          var sd = q.sort$[sf] < 0 ? -1 : 1

          list = _.sortBy(list, sf);
          if (sd == -1) {
              list.reverse()
          }

        }
        if (q.skip$) {
          list = list.slice(q.skip$)
        }

        if (q.limit$) {
          list = list.slice(0, q.limit$)
        }

        if (q.fields$) {
            list = _.map(list, function (item) {
                return _.pick(item, q.fields$);
            });
        }

        return list;
    }

    /**
    * Return if the folder exists, create otherwise.
    */
    function ensurefolder(folder, cb){
        fs.exists(folder, function(exists) {
          if( exists ) {
              return cb();
          }
          fs.mkdir(folder, function(err) {
            cb(err);
          });
        });
    }

    function makefolderpath(ent) {
        var canon = ent.canon$({object: true});

        var base  = canon.base;
        var name  = canon.name;

        var entfolder = (base ? base + '_' : '') + name;
        var folderpath = path.join(opts.folder, entfolder);
        return folderpath;
    }

    function error (done, win){
        return function(err, out){
          if (err)  {
              return done(err);
          }
          if (win) {
              return win(out);
          }
        }
    }

    /**
     * Get the levelup reference
     */
    var get_db = function get_db(ent, done) {
        var folder = makefolderpath(ent)
        var db = dbmap[folder];
        if (!db) {
          ensurefolder(folder, error(done, function(){
            db = dbmap[folder];
            if (db) {
                return done(null, db);
            }
            try {
                db = dbmap[folder] = levelQuery(levelup(folder, opts));
                db.query.use(jsonqueryEngine());
            } catch(e) {
                db = dbmap[folder];
                if (db) {
                    return done(null, db);
                }
                return done(e);
            }
            return done(null, db)
          }));
      } else {
          return done(null, db);
      }
    }

    var store = {
        name: name,

        close: function(args, done) {
            // Closes all the dbs
            seneca.util.recurse(_.values(dbmap), function(db, next) {
                db.close(next);
            }, done);
        },

        save: function(args, done) {

            var ent = args.ent
            var update = !!ent.id

            if (!update) {
                ent.id = void 0 != ent.id$ ? ent.id$ : uuid();
                delete(ent.id$)
            }

            get_db(ent, error(done, function(db) {
                    db.put(ent.id, ent.data$(false), function(err) {
                        seneca.log.debug('save/'+(update? 'update' : 'insert'), ent, desc);
                            done(null, ent);
                    });
                })
            );
        },

        load: function(args, done) {

            var qent = args.qent;
            var q    = args.q;
            var qq = fixquery(qent, q);

            if (qq.id) {
                get_db(qent, error(done, function(db) {
                    db.get( qq.id, function(err, data) {
                        if (err) {
                            if (err.notFound) {
                                return done(null, null);
                            }
                            return done(err);
                        }
                        var fent = qent.make$(data);
                        seneca.log.debug('load', q, fent, desc);
                        done(null, fent);
                    });
                }));
            } else {
              store.list(args, error(done, function(list){
                  done(null, list[0])
              }))
          }
        },

        remove: function(args, done) {

            var qent = args.qent;
            var q    = args.q;

            // all that match the query, of course
            var all  = q.all$; // default false
            var load  = _.isUndefined(q.load$) ? true : q.load$; // default true
            var qq = fixquery(qent,q);

            get_db(qent, error(done, function(db) {
                if( !_.isUndefined(qq.id) && load && !all ) {
                  db.get(qq.id, {fillCache:opts.fillCache}, function(err, data){
                      if (err) {
                          if (err.notFound) {
                              return done(null, null);
                          }
                          return done(err);
                      }
                      var fent = qent.make$(data);
                      do_remove(db, fent);
                  })
                } else {
                do_remove(db);
                }
            }));

            function do_remove(db, maybefent) {
                if ( _.isUndefined(qq.id) ) {
                    if (all) {
                        store.list(args, error(done, function(list) {
                            seneca.util.recurse(
                                list,
                                function(fent, next) {
                                  db.del(fent.id, {sync:opts.sync}, next);
                                },
                                function(err){
                                  seneca.log.debug('remove/all', q, desc);
                                  done(err);
                                }
                            );
                        }));
                    } else {
                        store.load(args, error(done, function(item){
                            if (item) {
                                db.del( item.id, {sync:opts.sync}, function(err){
                                    if( err ) return done(err);
                                    seneca.log.debug('remove/one', q, item, desc);
                                    done(null,item);
                                });
                            } else {
                                return done();
                            }
                        }));
                    }
            } else {
              db.del(qq.id, {sync:opts.sync}, function(err) {
                if (err) {
                    return done(err);
                }
                seneca.log.debug('remove/one', q, maybefent, desc);
                done(null, maybefent);
              })
            }
          }
        },

        list: function(args, done) {
            var qent = args.qent;
            var q    = args.q;

            get_db(qent, error(done, function(db){
                //var mq = metaquery(qent, q);
                var n = 1;
                var list = [];

                // saves the sorting / limit / skip / fields params
                var additionaParams = {
                    sort$: q.sort$,
                    limit$: q.limit$,
                    skip$: q.skip$,
                    fields$: q.fields$
                }
                // Remove the optional params from the query
                q = _.omit(q, ['sort$', 'limit$', 'skip$', 'fields$']);
                if (q.all$) { // useless
                    q = {};
                }

                db.query(q)
                    .on('data', function (data) {
                        if (filterdata( q, data ) ) {
                          list.push( qent.make$(data) )
                        }
                    })
                    .on('error', function (err) {
                        if(--n) throw new Error('errored:'+ n)
                        done(err)
                      })
                    .on('close', function () {
                    // do nothing, I guess
                    })
                    .on('end', function () {
                        if(--n) throw new Error('ended:'+ n);
                        // Order / skip / fields mngmt
                        list = fixlist(additionaParams, list)
                        seneca.log.debug('list', q, list.length, list[0],desc);
                        done(null,list);
                    });
            }))
        },

        native: function(args,done) {
            get_db(args.ent, function(err, db) {
                done(err, db);
            });
        }
    };

    var storedesc = seneca.store.init(seneca, opts, store);

    return {name:store.name, tag:storedesc.tag};
}
<|endoftext|>