<commit_before>import React from 'react'
import injectSheet from 'react-jss'
import { Link } from 'react-router-dom'

import { Phone, Desktop } from '../components/breakpoints'
import Spacer from '../components/spacer'
import LogoBetaBlack from '../components/icons/icon-components/logo-beta-black'
import HamburgerMenu from '../components/icons/icon-components/hamburger-menu'

const LogoAndNavigation = ({ classes }) => (

		<Phone>

					<Link to="/mini-app">
						<LogoBetaBlack height={25} width={160} />
					</Link>
					<Link to="/menu" className={classes.hamburgerMenuPhone}>
						<HamburgerMenu />
					</Link>

		</Phone>

		<Desktop>

					<Link to="/mini-app">
						<LogoBetaBlack height={33} width={208} />
					</Link>

						<Link to="/about" className={classes.navigationLinkDesktop}>
							About
						</Link>
						<Spacer width="32px" />
						<Link to="/feedback" className={classes.navigationLinkDesktop}>
							Feedback
						</Link>

		</Desktop>

)

const styles = {
	logoPhone: {
		height: '55px',
		padding: '0 5% 0 5%',
		'background-color': '#FFFFFF',
		'min-width': '90vw',
		display: 'flex',
		'align-items': 'center',
		'justify-content': 'space-between',
		'border-bottom': '1px solid #3D65F9',
		position: 'fixed',
		top: '0vh',
	},
	logoDesktop: {
		height: '100px',
		padding: '0px 134px 0px 42px',
		'background-color': '#FFFFFF',
		'min-width': '90vw',
		display: 'flex',
		'align-items': 'center',
		'justify-content': 'space-between',
		position: 'fixed',
		top: '0vh',
		'border-bottom': '4px solid #3D65F9',
	},
	hamburgerMenuPhone: {
		cursor: 'pointer',
	},
	linksDesktop: {
		display: 'flex',
	},
	navigationLinkDesktop: {
		'font-family': 'hah-regular',
		'font-size': '18px',
		color: '#000000',
		'text-decoration': 'none',
		cursor: 'pointer',
	},
}

export default injectSheet(styles)(LogoAndNavigation)
<commit_msg>Reorder properties<commit_after>import React from 'react'
import injectSheet from 'react-jss'
import { Link } from 'react-router-dom'

import { Phone, Desktop } from '../components/breakpoints'
import Spacer from '../components/spacer'
import LogoBetaBlack from '../components/icons/icon-components/logo-beta-black'
import HamburgerMenu from '../components/icons/icon-components/hamburger-menu'

const LogoAndNavigation = ({ classes }) => (

		<Phone>

					<Link to="/mini-app">
						<LogoBetaBlack height={25} width={160} />
					</Link>
					<Link to="/menu" className={classes.hamburgerMenuPhone}>
						<HamburgerMenu />
					</Link>

		</Phone>

		<Desktop>

					<Link to="/mini-app">
						<LogoBetaBlack height={33} width={208} />
					</Link>

						<Link to="/about" className={classes.navigationLinkDesktop}>
							About
						</Link>
						<Spacer width="32px" />
						<Link to="/feedback" className={classes.navigationLinkDesktop}>
							Feedback
						</Link>

		</Desktop>

)

const styles = {
	logoPhone: {
		position: 'fixed',
		top: '0vh',
		height: '55px',
		padding: '0 5% 0 5%',
		display: 'flex',
		'align-items': 'center',
		'justify-content': 'space-between',
		'background-color': '#FFFFFF',
		'min-width': '90vw',
		'border-bottom': '1px solid #3D65F9',
	},
	logoDesktop: {
		position: 'fixed',
		top: '0vh',
		height: '100px',
		padding: '0px 134px 0px 42px',
		display: 'flex',
		'align-items': 'center',
		'justify-content': 'space-between',
		'background-color': '#FFFFFF',
		'min-width': '90vw',
		'border-bottom': '4px solid #3D65F9',
	},
	hamburgerMenuPhone: {
		cursor: 'pointer',
	},
	linksDesktop: {
		display: 'flex',
	},
	navigationLinkDesktop: {
		'font-family': 'hah-regular',
		'font-size': '18px',
		color: '#000000',
		'text-decoration': 'none',
		cursor: 'pointer',
	},
}

export default injectSheet(styles)(LogoAndNavigation)
<|endoftext|>


<commit_before>define(['jquery', 'underscore', 'lacuna', 'template'], function($, _, Lacuna, Template) {

    Template.load(['mapStars']);

    function MapStars() {
        // Use scope to reduce confusion about this.
        var scope = this;

        // A 'tile' is a block of the starmap 100 units wide by 30 units high
        // Each of these tiles can be populated by a single call to get_star_map
        // Nine of these tiles arranged in a 3x3 grid form the starmap
        // the 'view-port' is a window onto this grid
        // As the map is dragged, some tiles will go out of scope and tiles on
        // the other side will be created (by calls to get_star_map).
        // Since the view-port is generally smaller than a tile (especially at high zoom)
        // then tiles will generally be created outside of the viewport and it will
        // give the appearance of continuous smooth scrolling.

        var defaults = {
            parentContainer     : '#starmap',   // The parent div to contain the starmap
            zoomLevel           : 2,            // Default zoom level
            viewX               : 0,            // The start X unit in the starmap
            viewY               : 0,            // The start Y unit in the starmap
            boundLeft           : -1500,        // Bounds of the starmap
            boundRight          : 1499,         // +1500 has no bodies, so we can ignore it
            boundTop            : 1499,         // Likewise on the Y axis
            boundBottom         : -1500,        // Lower bound
        };
        var options;
        // tiles are an array of 9 tiles arranged by index
        //
        //  6  7  8
        //  3  4  5
        //  0  1  2
        //
        scope.tiles = new Array();
        scope.oldCentreTile = {};

        // convert a zoom level into pixels per starmap 'unit'
        var zoomToPixels = {
            6   : 150,
            5   : 100,
            4   : 75,
            3   : 50,
            2   : 35,
            1   : 20
        };

        // When we move from the central tile (4) to any other tile
        // we can avoid reloading some tiles, we just juggle them
        // about. These (and their order) are the tiles we can move.
        // TODO There is an edge case, e.g. moving from tile 7 to
        // tile 1 (2 tiles jump) which is slightly inefficent (it
        // calculates all tiles). But it will do for now.
        //
        // TODO It is possible to scroll off the edge of the map. We
        // can probably stop this by putting bounds on the draggable.
        //
        // TODO Minor irritation. When dragging, new tile pops up with
        // what looks like 'old' data which is then overwritten by the
        // call to get_star_map. Clear it out first.
        var juggleTiles = {
            0   : [4,3,1,0],
            1   : [5,4,3,2,1,0],
            2   : [5,4,2,1],
            3   : [7,6,4,3,1,0],
            4   : [],
            5   : [1,2,4,5,7,8],
            6   : [3,4,6,7],
            7   : [3,4,5,6,7,8],
            8   : [4,5,7,8]
        };

        // We should only need to 'renderStars' when we first display
        // the starmap, when we zoom in/out, or make a big change to
        // our x,y position such that all current tiles go out of range.
        //
        // TODO Create a separate function to initialize the data
        //
        scope.renderStars = function(o) {

            // TODO change this to cater for options already defined
            // so we can call it multiple times, but retain old options
            if (typeof o == 'object') {
                options = $.extend(defaults, o);
            }
            else {
                options = defaults;
            }
            // First determine where the centre tile is positioned in the starmap units
            var centreLeft      = Math.floor((options.viewX - options.boundLeft) / 100) * 100 + options.boundLeft;
            var centreBottom    = Math.floor((options.viewY - options.boundBottom) / 30) * 30 + options.boundBottom;
            scope.oldCentreTile = {
                html    : '',
                top     : centreBottom + 29,
                bottom  : centreBottom,
                left    : centreLeft,
                right   : centreLeft + 100
            }

            // The starsParent is the draggable object, it's children (the tiles) can be dragged
            // with it. Let's make it as big as the expanse (in pixels)
            var expanseWidthPx = scope.unitWidthPx() * (options.boundRight - options.boundLeft);
            var expanseHeightPx = scope.unitHeightPx() * (options.boundTop - options.boundBottom);
            var $starsParent = $("#starsParent");
            $starsParent.draggable({
                stop : function(event, ui) {
                    scope.processDragStop()
                }
            });
            $starsParent.html('').width(expanseWidthPx).height(expanseHeightPx);

            // First set the position the nine tiles, they will be empty until
            // they are rendered by calls to 'get_star_map'.
            for (var x=0; x<9; x++) {
                var tile    = scope.getTileBounds(x);
                var tileAbs = scope.getTileAbsPosition(x);

                var tileHtml = Template.read.mapStar_tile({
                    absLeft     : tileAbs.left,
                    absTop      : tileAbs.top,
                    x           : tile.left,
                    y           : tile.top,
                    tileId      : x,
                    widthPx     : 100 * scope.unitWidthPx(),
                    heightPx    : 30 * scope.unitHeightPx(),
                });
                $starsParent.append(tileHtml);
                scope.tiles[x].html = tileHtml;
            }

            // Render all 9 tiles
            // TODO: We should render all tiles that are visible first
            // but for now render the centre tile first
            scope.renderTile(4);
            for (var x=0; x < 9; x++) {
                if (x != 4) {
                    scope.renderTile(x);
                }
            }
            // Get the size of the viewport so we can position the target in the centre of the screen
            var $starsViewport = $("#starsViewport");
            var viewWidth = $starsViewport.width();
            var viewHeight = $starsViewport.height();
            var left = viewWidth / 2 - (options.viewX - options.boundLeft) * scope.unitWidthPx();
            var top = viewHeight / 2 - (options.boundTop - options.viewY) * scope.unitHeightPx();
            $starsParent.css('left', left);
            $starsParent.css('top', top);
            $starsParent = null; // avoid potential memory leak
        };

        // After a drag-drop, we need to recalculate the tiles
        scope.processDragStop= function() {
            //alert('get here');
            var $starsParent    = $("#starsParent");
            var $starsViewport  = $("#starsViewport");

            var parentLeft      = parseInt($starsParent.css('left'));
            var parentTop       = parseInt($starsParent.css('top'));
            var viewWidth       = parseInt($starsViewport.width());
            var viewHeight      = parseInt($starsViewport.height());
            var unitX           = Math.round((viewWidth / 2 - parentLeft) / scope.unitWidthPx() + options.boundLeft);
            var unitY           = Math.round(options.boundTop - (viewHeight / 2 - parentTop) / scope.unitHeightPx());

            // Given, the star unit X,Y we need to see if it falls within any of the 9 currently rendered tiles
            var moveToTile = scope.getTileToMoveTo(unitX,unitY);
            if (moveToTile == -1) {
                // moved totally outside the current 9 tiles, recalculate everything
                // TODO it is possible that we have moved the centre tile outside of
                // any existing tiles, but some edge tiles might still be reusable.
                // Look at this later.
                scope.renderStars({
                    viewX : unitX,
                    viewY : unitY
                });
            }
            else if (moveToTile != 4) {
                // tile 4 means 'no movement', so omit it
                var delta = 4 - moveToTile;
                var tiles = _.clone(juggleTiles[moveToTile]);
                var tilesToDo = [0,1,2,3,4,5,6,7,8];
                while(tiles.length) {
                    var tile = tiles.shift();
                    scope.moveTile(tile,tile+delta);
                    // remove this tile from tilesToDo (native indexOf not supported in IE 8 or below)
                    tilesToDo.splice(_.indexOf(tilesToDo, tile+delta), 1);
                }
                scope.oldCentreTile = _.clone(scope.tiles[4]);
                // render the remaining tiles.
                while(tilesToDo.length) {
                    var tile = tilesToDo.shift();
                    scope.renderTile(tile);
                }
                // Now adjust the position of all of the tiles
                for (var x=0; x<9; x++) {
                    var tileAbs = scope.getTileAbsPosition(x);
                    $("#starmap_tile"+x).css("top",tileAbs.top).css("left",tileAbs.left);
                    $("#starmap_tile_title"+x).css("top",tileAbs.top).css("left",tileAbs.left);
                }
            }
        };
        // Get the pixel location of the tile on the draggable background
        scope.getTileAbsPosition = function(tileId) {
            var tile    = scope.tiles[tileId];
            return {
                left    : (tile.left - options.boundLeft) * scope.unitWidthPx(),
                top     : (options.boundTop - tile.top) * scope.unitHeightPx()
            };
        };
        // Move a tile and adjust the html for that tile
        scope.moveTile = function(from, to) {
            scope.tiles[to] = _.clone(scope.tiles[from]);
            $("#starmap_tile"+to).html($("#starmap_tile"+from).html());
            $("#starmap_tile"+from).html('');
            // the following is only temporary, for debug purposes.
            $("#starmap_tile_title"+to).html("   tile "+to+", "+scope.tiles[to].left+"|"+scope.tiles[to].top);
        };

        // The expanse is tiled in fixed size tiles 100 units wide by 30 units high.
        // We can break the starmap into 30 tiles wide (ignoring x=1500 since it
        // does not contain any bodies) and 100 tiles high (again ignoring y=1500)
        // Taking advantage of this makes the code a bit easier. This may change in the
        // future when we have the basic code working.
        //
        // We can position the tile in a parent div with an area equivalent to the size of the expanse
        // (in pixels, which depends upon zoom level) and then position the stars and planets
        // absolutely within the tile.
        //
        scope.unitWidthPx = function() {
            return zoomToPixels[options.zoomLevel];
        };
        scope.unitHeightPx = function() {
            return zoomToPixels[options.zoomLevel];
        };

        // Given a star unit X,Y location, work out which of the existing tiles have we moved to
        // (or return -1 if we are out of range of all existing tiles)
        scope.getTileToMoveTo = function(unitX, unitY) {
            var xDelta  = (Math.floor((unitX - options.boundLeft)/100) * 100 + options.boundLeft - scope.oldCentreTile.left) / 100;
            var yDelta  = (Math.floor((unitY - options.boundBottom)/30) * 30 + options.boundBottom + 29 - scope.oldCentreTile.top) / 30;
            //alert("xDelta="+xDelta+", yDelta="+yDelta);
            if (Math.abs(xDelta) < 2 && Math.abs(yDelta) < 2) {
                return 4 + yDelta * 3 + xDelta;
            }
            return -1;      // Out of range of all tiles
        };

        // tileId is the tile who's position we want to find
        // by referring to the central tile
        //
        scope.getTileBounds = function(tileId) {
            scope.tiles[tileId] = {};
            // xDelta and yDelta are the tile offsets to the centre tile
            var yDelta  = Math.floor((8 - tileId)/3) - 1;
            var xDelta  = tileId % 3 - 1;
            // Convert to position for the specified tileId (in starmap units)
            var left    = scope.oldCentreTile.left + xDelta * 100;
            var top     = scope.oldCentreTile.top - yDelta * 30;
            scope.tiles[tileId] = {
                html    : '',
                left    : left,
                top     : top,
                right   : left + 99,
                bottom  : top - 29
            };
            return scope.tiles[tileId];
        };

        // Render a single tile given it's ID (0-9)
        scope.renderTile = function(tileId) {
            scope.getTileBounds(tileId);
            var tile = scope.tiles[tileId];

            if (    tile.left   >= options.boundLeft
                &&  tile.right  <= options.boundRight
                &&  tile.bottom >= options.boundBottom
                &&  tile.top    <= options.boundTop) {
                // Then we are within the bounds of the starmap
                Lacuna.send({
                    module: '/map',
                    method: 'get_star_map',
                    params: [{
                        session_id  : Lacuna.getSession(),
                        left        : tile.left,
                        top         : tile.top,
                        right       : tile.right,
                        bottom      : tile.bottom
                    }],
                    success : function(o) {
                        var stars = o.result.stars;
                        var tileHtml = '';

                        // Map each star onto the tile
                        for (var i = 0; i < stars.length; i++) {
                            var star = stars[i];
                            var star_div = Template.read.mapStar_star({
                                assetsUrl   : window.assetsUrl,
                                id          : star.id,
                                x           : star.x,
                                y           : star.y,
                                name        : star.name,
                                tile_width  : scope.unitWidthPx() * 3,
                                tile_height : scope.unitHeightPx() * 3,
                                tile_left   : (star.x - tile.left - 1) * scope.unitWidthPx(),
                                tile_top    : (tile.top - star.y - 1) * scope.unitHeightPx(),
                                star_color  : star.color,
                                star_width  : scope.unitWidthPx() * 3,
                                star_height : scope.unitHeightPx() * 3,
                                margin_top  : 5,
                                star_seized : 0
                            });
                            tileHtml += star_div;
                            // Map each planet of this star onto the tile
                            var bodies = star.bodies;
                            for (var b=0; b<bodies.length; b++) {
                                var body = bodies[b];
                                var body_div = Template.read.mapStar_body({
                                    assetsUrl   : window.assetsUrl,
                                    id          : body.id,
                                    x           : body.x,
                                    y           : body.y,
                                    name        : body.name,
                                    tile_width  : scope.unitWidthPx(),
                                    tile_height : scope.unitHeightPx(),
                                    tile_left   : (body.x - tile.left) * scope.unitWidthPx(),
                                    tile_top    : (tile.top - body.y) * scope.unitHeightPx(),
                                    body_image  : body.image,
                                    body_orbit  : body.orbit,
                                    body_width  : scope.unitWidthPx(),
                                    body_height : scope.unitWidthPx(),
                                    planet_occupied : 0,            // for now
                                    margin_top  : 5
                                });
                                tileHtml += body_div;
                            }
                        }
                        scope.tiles[tileId].html = tileHtml;
                        $("#starmap_tile"+tileId).html(tileHtml);
                        // The following is only temporary for debug purposes
                        $("#starmap_tile_title"+tileId).html("   tile "+tileId+", "+scope.tiles[tileId].left+"|"+scope.tiles[tileId].top);
                        // Now populate the tile with the ships
                    }
                });
            }
            else {
                // Then we are outside the bounds, just render a starfield
                // (we may not need to render anything if we have a tiled background image)
            }
        };
    };

    return new MapStars();
});

<commit_msg>A more sensible sizing method for planets<commit_after>define(['jquery', 'underscore', 'lacuna', 'template'], function($, _, Lacuna, Template) {

    Template.load(['mapStars']);

    function MapStars() {
        // Use scope to reduce confusion about this.
        var scope = this;

        // A 'tile' is a block of the starmap 100 units wide by 30 units high
        // Each of these tiles can be populated by a single call to get_star_map
        // Nine of these tiles arranged in a 3x3 grid form the starmap
        // the 'view-port' is a window onto this grid
        // As the map is dragged, some tiles will go out of scope and tiles on
        // the other side will be created (by calls to get_star_map).
        // Since the view-port is generally smaller than a tile (especially at high zoom)
        // then tiles will generally be created outside of the viewport and it will
        // give the appearance of continuous smooth scrolling.

        var defaults = {
            parentContainer     : '#starmap',   // The parent div to contain the starmap
            zoomLevel           : 2,            // Default zoom level
            viewX               : 0,            // The start X unit in the starmap
            viewY               : 0,            // The start Y unit in the starmap
            boundLeft           : -1500,        // Bounds of the starmap
            boundRight          : 1499,         // +1500 has no bodies, so we can ignore it
            boundTop            : 1499,         // Likewise on the Y axis
            boundBottom         : -1500,        // Lower bound
        };
        var options;
        // tiles are an array of 9 tiles arranged by index
        //
        //  6  7  8
        //  3  4  5
        //  0  1  2
        //
        scope.tiles = new Array();
        scope.oldCentreTile = {};

        // convert a zoom level into pixels per starmap 'unit'
        var zoomToPixels = {
            6   : 150,
            5   : 100,
            4   : 75,
            3   : 50,
            2   : 35,
            1   : 20
        };

        // When we move from the central tile (4) to any other tile
        // we can avoid reloading some tiles, we just juggle them
        // about. These (and their order) are the tiles we can move.
        // TODO There is an edge case, e.g. moving from tile 7 to
        // tile 1 (2 tiles jump) which is slightly inefficent (it
        // calculates all tiles). But it will do for now.
        //
        // TODO It is possible to scroll off the edge of the map. We
        // can probably stop this by putting bounds on the draggable.
        //
        // TODO Minor irritation. When dragging, new tile pops up with
        // what looks like 'old' data which is then overwritten by the
        // call to get_star_map. Clear it out first.
        var juggleTiles = {
            0   : [4,3,1,0],
            1   : [5,4,3,2,1,0],
            2   : [5,4,2,1],
            3   : [7,6,4,3,1,0],
            4   : [],
            5   : [1,2,4,5,7,8],
            6   : [3,4,6,7],
            7   : [3,4,5,6,7,8],
            8   : [4,5,7,8]
        };

        // We should only need to 'renderStars' when we first display
        // the starmap, when we zoom in/out, or make a big change to
        // our x,y position such that all current tiles go out of range.
        //
        // TODO Create a separate function to initialize the data
        //
        scope.renderStars = function(o) {

            // TODO change this to cater for options already defined
            // so we can call it multiple times, but retain old options
            if (typeof o == 'object') {
                options = $.extend(defaults, o);
            }
            else {
                options = defaults;
            }
            // First determine where the centre tile is positioned in the starmap units
            var centreLeft      = Math.floor((options.viewX - options.boundLeft) / 100) * 100 + options.boundLeft;
            var centreBottom    = Math.floor((options.viewY - options.boundBottom) / 30) * 30 + options.boundBottom;
            scope.oldCentreTile = {
                html    : '',
                top     : centreBottom + 29,
                bottom  : centreBottom,
                left    : centreLeft,
                right   : centreLeft + 100
            }

            // The starsParent is the draggable object, it's children (the tiles) can be dragged
            // with it. Let's make it as big as the expanse (in pixels)
            var expanseWidthPx = scope.unitWidthPx() * (options.boundRight - options.boundLeft);
            var expanseHeightPx = scope.unitHeightPx() * (options.boundTop - options.boundBottom);
            var $starsParent = $("#starsParent");
            $starsParent.draggable({
                stop : function(event, ui) {
                    scope.processDragStop()
                }
            });
            $starsParent.html('').width(expanseWidthPx).height(expanseHeightPx);

            // First set the position the nine tiles, they will be empty until
            // they are rendered by calls to 'get_star_map'.
            for (var x=0; x<9; x++) {
                var tile    = scope.getTileBounds(x);
                var tileAbs = scope.getTileAbsPosition(x);

                var tileHtml = Template.read.mapStar_tile({
                    absLeft     : tileAbs.left,
                    absTop      : tileAbs.top,
                    x           : tile.left,
                    y           : tile.top,
                    tileId      : x,
                    widthPx     : 100 * scope.unitWidthPx(),
                    heightPx    : 30 * scope.unitHeightPx(),
                });
                $starsParent.append(tileHtml);
                scope.tiles[x].html = tileHtml;
            }

            // Render all 9 tiles
            // TODO: We should render all tiles that are visible first
            // but for now render the centre tile first
            scope.renderTile(4);
            for (var x=0; x < 9; x++) {
                if (x != 4) {
                    scope.renderTile(x);
                }
            }
            // Get the size of the viewport so we can position the target in the centre of the screen
            var $starsViewport = $("#starsViewport");
            var viewWidth = $starsViewport.width();
            var viewHeight = $starsViewport.height();
            var left = viewWidth / 2 - (options.viewX - options.boundLeft) * scope.unitWidthPx();
            var top = viewHeight / 2 - (options.boundTop - options.viewY) * scope.unitHeightPx();
            $starsParent.css('left', left);
            $starsParent.css('top', top);
            $starsParent = null; // avoid potential memory leak
        };

        // After a drag-drop, we need to recalculate the tiles
        scope.processDragStop= function() {
            //alert('get here');
            var $starsParent    = $("#starsParent");
            var $starsViewport  = $("#starsViewport");

            var parentLeft      = parseInt($starsParent.css('left'));
            var parentTop       = parseInt($starsParent.css('top'));
            var viewWidth       = parseInt($starsViewport.width());
            var viewHeight      = parseInt($starsViewport.height());
            var unitX           = Math.round((viewWidth / 2 - parentLeft) / scope.unitWidthPx() + options.boundLeft);
            var unitY           = Math.round(options.boundTop - (viewHeight / 2 - parentTop) / scope.unitHeightPx());

            // Given, the star unit X,Y we need to see if it falls within any of the 9 currently rendered tiles
            var moveToTile = scope.getTileToMoveTo(unitX,unitY);
            if (moveToTile == -1) {
                // moved totally outside the current 9 tiles, recalculate everything
                // TODO it is possible that we have moved the centre tile outside of
                // any existing tiles, but some edge tiles might still be reusable.
                // Look at this later.
                scope.renderStars({
                    viewX : unitX,
                    viewY : unitY
                });
            }
            else if (moveToTile != 4) {
                // tile 4 means 'no movement', so omit it
                var delta = 4 - moveToTile;
                var tiles = _.clone(juggleTiles[moveToTile]);
                var tilesToDo = [0,1,2,3,4,5,6,7,8];
                while(tiles.length) {
                    var tile = tiles.shift();
                    scope.moveTile(tile,tile+delta);
                    // remove this tile from tilesToDo (native indexOf not supported in IE 8 or below)
                    tilesToDo.splice(_.indexOf(tilesToDo, tile+delta), 1);
                }
                scope.oldCentreTile = _.clone(scope.tiles[4]);
                // render the remaining tiles.
                while(tilesToDo.length) {
                    var tile = tilesToDo.shift();
                    scope.renderTile(tile);
                }
                // Now adjust the position of all of the tiles
                for (var x=0; x<9; x++) {
                    var tileAbs = scope.getTileAbsPosition(x);
                    $("#starmap_tile"+x).css("top",tileAbs.top).css("left",tileAbs.left);
                    $("#starmap_tile_title"+x).css("top",tileAbs.top).css("left",tileAbs.left);
                }
            }
        };
        // Get the pixel location of the tile on the draggable background
        scope.getTileAbsPosition = function(tileId) {
            var tile    = scope.tiles[tileId];
            return {
                left    : (tile.left - options.boundLeft) * scope.unitWidthPx(),
                top     : (options.boundTop - tile.top) * scope.unitHeightPx()
            };
        };
        // Move a tile and adjust the html for that tile
        scope.moveTile = function(from, to) {
            scope.tiles[to] = _.clone(scope.tiles[from]);
            $("#starmap_tile"+to).html($("#starmap_tile"+from).html());
            $("#starmap_tile"+from).html('');
            // the following is only temporary, for debug purposes.
            $("#starmap_tile_title"+to).html("   tile "+to+", "+scope.tiles[to].left+"|"+scope.tiles[to].top);
        };

        // The expanse is tiled in fixed size tiles 100 units wide by 30 units high.
        // We can break the starmap into 30 tiles wide (ignoring x=1500 since it
        // does not contain any bodies) and 100 tiles high (again ignoring y=1500)
        // Taking advantage of this makes the code a bit easier. This may change in the
        // future when we have the basic code working.
        //
        // We can position the tile in a parent div with an area equivalent to the size of the expanse
        // (in pixels, which depends upon zoom level) and then position the stars and planets
        // absolutely within the tile.
        //
        scope.unitWidthPx = function() {
            return zoomToPixels[options.zoomLevel];
        };
        scope.unitHeightPx = function() {
            return zoomToPixels[options.zoomLevel];
        };

        // Given a star unit X,Y location, work out which of the existing tiles have we moved to
        // (or return -1 if we are out of range of all existing tiles)
        scope.getTileToMoveTo = function(unitX, unitY) {
            var xDelta  = (Math.floor((unitX - options.boundLeft)/100) * 100 + options.boundLeft - scope.oldCentreTile.left) / 100;
            var yDelta  = (Math.floor((unitY - options.boundBottom)/30) * 30 + options.boundBottom + 29 - scope.oldCentreTile.top) / 30;
            //alert("xDelta="+xDelta+", yDelta="+yDelta);
            if (Math.abs(xDelta) < 2 && Math.abs(yDelta) < 2) {
                return 4 + yDelta * 3 + xDelta;
            }
            return -1;      // Out of range of all tiles
        };

        // tileId is the tile who's position we want to find
        // by referring to the central tile
        //
        scope.getTileBounds = function(tileId) {
            scope.tiles[tileId] = {};
            // xDelta and yDelta are the tile offsets to the centre tile
            var yDelta  = Math.floor((8 - tileId)/3) - 1;
            var xDelta  = tileId % 3 - 1;
            // Convert to position for the specified tileId (in starmap units)
            var left    = scope.oldCentreTile.left + xDelta * 100;
            var top     = scope.oldCentreTile.top - yDelta * 30;
            scope.tiles[tileId] = {
                html    : '',
                left    : left,
                top     : top,
                right   : left + 99,
                bottom  : top - 29
            };
            return scope.tiles[tileId];
        };

        // Render a single tile given it's ID (0-9)
        scope.renderTile = function(tileId) {
            scope.getTileBounds(tileId);
            var tile = scope.tiles[tileId];

            if (    tile.left   >= options.boundLeft
                &&  tile.right  <= options.boundRight
                &&  tile.bottom >= options.boundBottom
                &&  tile.top    <= options.boundTop) {
                // Then we are within the bounds of the starmap
                Lacuna.send({
                    module: '/map',
                    method: 'get_star_map',
                    params: [{
                        session_id  : Lacuna.getSession(),
                        left        : tile.left,
                        top         : tile.top,
                        right       : tile.right,
                        bottom      : tile.bottom
                    }],
                    success : function(o) {
                        var stars = o.result.stars;
                        var tileHtml = '';

                        // Map each star onto the tile
                        for (var i = 0; i < stars.length; i++) {
                            var star = stars[i];
                            var star_div = Template.read.mapStar_star({
                                assetsUrl   : window.assetsUrl,
                                id          : star.id,
                                x           : star.x,
                                y           : star.y,
                                name        : star.name,
                                tile_width  : scope.unitWidthPx() * 3,
                                tile_height : scope.unitHeightPx() * 3,
                                tile_left   : (star.x - tile.left - 1) * scope.unitWidthPx(),
                                tile_top    : (tile.top - star.y - 1) * scope.unitHeightPx(),
                                star_color  : star.color,
                                star_width  : scope.unitWidthPx() * 3,
                                star_height : scope.unitHeightPx() * 3,
                                margin_top  : 5,
                                star_seized : 0
                            });
                            tileHtml += star_div;
                            // Map each planet of this star onto the tile
                            var bodies = star.bodies;
                            for (var b=0; b<bodies.length; b++) {
                                var body        = bodies[b];
                                // An attempt to get sensible sizes for planets/asteroids
                                var image_size  = (body.size < 30 ? 50 : (body.size - 30) / 2 + 75) * scope.unitWidthPx() / 100;

                                var body_div = Template.read.mapStar_body({
                                    assetsUrl   : window.assetsUrl,
                                    id          : body.id,
                                    x           : body.x,
                                    y           : body.y,
                                    name        : body.name,
                                    tile_width  : scope.unitWidthPx(),
                                    tile_height : scope.unitHeightPx(),
                                    tile_left   : (body.x - tile.left) * scope.unitWidthPx(),
                                    tile_top    : (tile.top - body.y) * scope.unitHeightPx(),
                                    body_image  : body.image,
                                    body_orbit  : body.orbit,
                                    body_width  : image_size,
                                    body_height : image_size,
                                    planet_occupied : 0,            // for now
                                    margin_top  : 5
                                });
                                tileHtml += body_div;
                            }
                        }
                        scope.tiles[tileId].html = tileHtml;
                        $("#starmap_tile"+tileId).html(tileHtml);
                        // The following is only temporary for debug purposes
                        $("#starmap_tile_title"+tileId).html("   tile "+tileId+", "+scope.tiles[tileId].left+"|"+scope.tiles[tileId].top);
                        // Now populate the tile with the ships
                    }
                });
            }
            else {
                // Then we are outside the bounds, just render a starfield
                // (we may not need to render anything if we have a tiled background image)
            }
        };
    };

    return new MapStars();
});

<|endoftext|>


<commit_before>import chalk from 'chalk'
import { Base } from '../../yo-yo'
import { upperFirst, camelCase, kebabCase } from 'lodash'

import path, { cliBase, packagesPath } from '../../path'
const templatesPath = path.join(__dirname, 'templates')

const params = {
	duck: 'Ducks',
	style: 'Styles → $',
	args: 'Args',
	props: 'Props',
	lang: 'Lang → l()',
	isfwl: 'Staff For Watch Lifecycle',
	saga: 'Sagas',
}
const generalParamsChoices = [
	{ name: params.duck },
	{ name: params.style, checked: true },
	{ name: params.lang, checked: true },
	{ name: params.args },
	{ name: params.props },
]
const additionalParamsChoices = [
	{ name: params.saga, checked: true },
	{ name: params.isfwl },
]
const mapComponentProps = [
	{ f: 'style', t: '$' },
	{ f: 'lang', t: 'l' },
	{ f: 'duck', t: 'state, actions' },
	{ f: 'args', t: 'args' },
	{ f: 'props', t: 'props' },
]

export default class extends Base {

	constructor(args, opts) {

		super(args, opts)

		this.author = `${this.user.git.name()} <${this.user.git.email()}>`

		this.argument('packageName', { type: String, required: false })

		this.sourceRoot(templatesPath)
	}

	prompting () {

		const prompts = [
			{
				type: 'input',
				name: 'name',
				message: `How do you want to name the ${chalk.yellow('component')}?`,
				default: this.options.packageName,
				validate: (input) => (input !== ''),
				filter: (name) => upperFirst(camelCase(name))
			},
			{
				type: 'list',
				name: 'folder',
				message: ({ name }) => (`In which folder do you want to put the ${chalk.yellow(name)} component?`),
				choices: this.getConfig('components')
			},
			{
				type: 'checkbox',
				name: 'generalParams',
				message: ({ name, folder }) => {
					const p = chalk.yellow(path.join(folder, name))
					return `What do you want to include to the ${p} component?`
				},
				choices: generalParamsChoices
			},
			{
				type: 'checkbox',
				name: 'additionalParams',
				message: ({ name, folder }) => `Quack! Quack! What else?`,
				choices: additionalParamsChoices,
				when: ({ generalParams }) => (!!~generalParams.indexOf('Ducks'))
			},
			{
				type: 'confirm',
				name: 'doInject',
				message: ({ name, folder }) => {
					const p = chalk.yellow(path.join(folder, name))
					return `Do you want to inject ${p} component into ${chalk.magenta('Storybull')}?`
				}
			}
		]

		return this
			.prompt(prompts)
			.then((answers) => { this.answers = answers })
	}

	configuring () {

		const { name, folder, generalParams, additionalParams } = this.answers
		const finalParams = generalParams.concat(additionalParams || [])
		const componentPath = path.join(folder, name)

		const context = {
			componentPath,
			componentName: name,
			componentNameCamel: camelCase(name),
			componentNameKebab: kebabCase(name),
			componentType: folder,
			componentDescription: `${name} component created as part of ${upperFirst(folder)}`,
			author: this.author,
		}

		for (var param in params) context[param] = !!~finalParams.indexOf(params[param])

		context.componentProps = mapComponentProps
			.filter((p) => (context[p.f]))
			.map((p) => (p.t))
			.join(', ')

		this.context = context
	}

	writing () {

		const { componentPath, style, duck, saga } = this.context

		// Чтобы копировать всё в папку с новым компонентом
		// потому что copy() работает от дестРута
		this.destinationRoot(path.join(packagesPath, componentPath))

		this.copy('package.json')
		this.copy('index.jsx.js')
		this.copy('stories/index.js')

		style && this.copy('styles/index.js')
		duck && this.copy('ducks/index.js')
		saga && this.copy('sagas/index.js')
	}

	end () {

		const { doInject } = this.answers
		const { componentPath } = this.context

		if (doInject) {

			var nextLoaded
			var prevLoaded = this.getConfig('loaded')

			if (Array.isArray(prevLoaded)) {

				if (!~prevLoaded.indexOf(componentPath)) {

					nextLoaded = [ ...prevLoaded, componentPath ]
				}
			} else {
				nextLoaded = [ componentPath ]
			}

			nextLoaded && this.setConfig('loaded', nextLoaded)
		}

		this.log(chalk.green(`The ${chalk.yellow(componentPath)} component created!`))
	}

}

<commit_msg>fix(create): duck as params.duck<commit_after>import chalk from 'chalk'
import { Base } from '../../yo-yo'
import { upperFirst, camelCase, kebabCase } from 'lodash'

import path, { cliBase, packagesPath } from '../../path'
const templatesPath = path.join(__dirname, 'templates')

const params = {
	duck: 'Ducks',
	style: 'Styles → $',
	args: 'Args',
	props: 'Props',
	lang: 'Lang → l()',
	isfwl: 'Staff For Watch Lifecycle',
	saga: 'Sagas',
}
const generalParamsChoices = [
	{ name: params.duck },
	{ name: params.style, checked: true },
	{ name: params.lang, checked: true },
	{ name: params.args },
	{ name: params.props },
]
const additionalParamsChoices = [
	{ name: params.saga, checked: true },
	{ name: params.isfwl },
]
const mapComponentProps = [
	{ f: 'style', t: '$' },
	{ f: 'lang', t: 'l' },
	{ f: 'duck', t: 'state, actions' },
	{ f: 'args', t: 'args' },
	{ f: 'props', t: 'props' },
]

export default class extends Base {

	constructor (args, opts) {

		super(args, opts)

		this.author = `${this.user.git.name()} <${this.user.git.email()}>`

		this.argument('packageName', { type: String, required: false })

		this.sourceRoot(templatesPath)
	}

	prompting () {

		const prompts = [
			{
				type: 'input',
				name: 'name',
				message: `How do you want to name the ${chalk.yellow('component')}?`,
				default: this.options.packageName,
				validate: (input) => (input !== ''),
				filter: (name) => upperFirst(camelCase(name))
			},
			{
				type: 'list',
				name: 'folder',
				message: ({ name }) => (`In which folder do you want to put the ${chalk.yellow(name)} component?`),
				choices: this.getConfig('components')
			},
			{
				type: 'checkbox',
				name: 'generalParams',
				message: ({ name, folder }) => {
					const p = chalk.yellow(path.join(folder, name))
					return `What do you want to include to the ${p} component?`
				},
				choices: generalParamsChoices
			},
			{
				type: 'checkbox',
				name: 'additionalParams',
				message: ({ name, folder }) => `Quack! Quack! What else?`,
				choices: additionalParamsChoices,
				when: ({ generalParams }) => (!!~generalParams.indexOf(params.duck))
			},
			{
				type: 'confirm',
				name: 'doInject',
				message: ({ name, folder }) => {
					const p = chalk.yellow(path.join(folder, name))
					return `Do you want to inject ${p} component into ${chalk.magenta('Storybull')}?`
				}
			}
		]

		return this
			.prompt(prompts)
			.then((answers) => { this.answers = answers })
	}

	configuring () {

		const { name, folder, generalParams, additionalParams } = this.answers
		const finalParams = generalParams.concat(additionalParams || [])
		const componentPath = path.join(folder, name)

		const context = {
			componentPath,
			componentName: name,
			componentNameCamel: camelCase(name),
			componentNameKebab: kebabCase(name),
			componentType: folder,
			componentDescription: `${name} component created as part of ${upperFirst(folder)}`,
			author: this.author,
		}

		for (var param in params) context[param] = !!~finalParams.indexOf(params[param])

		context.componentProps = mapComponentProps
			.filter((p) => (context[p.f]))
			.map((p) => (p.t))
			.join(', ')

		this.context = context
	}

	writing () {

		const { componentPath, style, duck, saga } = this.context

		// Чтобы копировать всё в папку с новым компонентом
		// потому что copy() работает от дестРута
		this.destinationRoot(path.join(packagesPath, componentPath))

		this.copy('package.json')
		this.copy('index.jsx.js')
		this.copy('stories/index.js')

		style && this.copy('styles/index.js')
		duck && this.copy('ducks/index.js')
		saga && this.copy('sagas/index.js')
	}

	end () {

		const { doInject } = this.answers
		const { componentPath } = this.context

		if (doInject) {

			var nextLoaded
			var prevLoaded = this.getConfig('loaded')

			if (Array.isArray(prevLoaded)) {

				if (!~prevLoaded.indexOf(componentPath)) {

					nextLoaded = [ ...prevLoaded, componentPath ]
				}
			} else {
				nextLoaded = [ componentPath ]
			}

			nextLoaded && this.setConfig('loaded', nextLoaded)
		}

		this.log(chalk.green(`The ${chalk.yellow(componentPath)} component created!`))
	}

}

<|endoftext|>


<commit_before>// Runs server that listens for post requests at /summarize with text
// in the http request body associated with the 'str' key, then runs the text
// through the text-backdrop npm module and returns the results
var backdrop = require('text-backdrop');
var express = require('express');
var bodyparser = require('body-parser');
var morgan = require('morgan');
var app = express();

app.use(bodyparser.urlencoded({extended: false}));
app.use(morgan('combined'));

app.post('/summarize', function (req, res) {
    res.set({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
    });
    var text = req.body['str'];
    backdrop(text).then(function(data) {
        res.end(JSON.stringify(data));
    }).catch(function(err) {
        res.status(500).send(err);
    });
});

var server = app.listen(3000, function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Well-Versed backend listening at http://%s:%s', host, port);
});
<commit_msg>Moved server to port 80<commit_after>// Runs server that listens for post requests at /summarize with text
// in the http request body associated with the 'str' key, then runs the text
// through the text-backdrop npm module and returns the results
var backdrop = require('text-backdrop');
var express = require('express');
var bodyparser = require('body-parser');
var morgan = require('morgan');
var app = express();

app.use(bodyparser.urlencoded({extended: false}));
app.use(morgan('combined'));

app.post('/summarize', function (req, res) {
    res.set({
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
    });
    var text = req.body['str'];
    backdrop(text).then(function(data) {
        res.end(JSON.stringify(data));
    }).catch(function(err) {
        res.status(500).send(err);
    });
});

var server = app.listen(80, function () {
    var host = server.address().address;
    var port = server.address().port;

    console.log('Well-Versed backend listening at http://%s:%s', host, port);
});
<|endoftext|>


<commit_before>/*
Project Name: Fancy Forms
URL: https://github.com/keobrien/Fancy-Forms
Author: Kevin O'Brien
Company: Clockwork Acive Media Systems
Company Site: clockwork.net
License: MIT
Copyright (C) 2012 Clockwork Active Media Systems
Version: 0.2
**************************************/

(function ($) {

	"use strict";

	// These locally scoped variables help minification by aliasing strings
	var data_key               =  'CW_fancy_forms',
		event_suffix           =  '.CW_fancy_forms',
		class_prefix           =  'fancy-',
	// Custom events
		component_initialized  =  'component_initialized' + event_suffix,
		$elements              =  $(), // Used to filter propHooks to plugin elements only
		$radios                =  $(); // Used to make the radio updater faster

	var methods  =  {

		_init_el  :  function (settings) {

			if($(this).hasClass(class_prefix + 'active')) { return this; }

			var $el  =  $(this),
				type =  $el.prop("tagName").toLowerCase(),
				type =  type === 'select' ? type : $el.attr('type'),
				options  =  $.extend({
					// Settings for selects, checkboxes and radios
					selected_class  :  class_prefix + type + '-checked',
					wrap_class      :  class_prefix + type +'-wrap',
					wrap_id         :  $el.attr('id') ? class_prefix + type +'-' + $el.attr('id') : '',
					wrap_tag        :  'div',
					type            :  type
				}, settings);
			options.wrap_html       =  '<' + options.wrap_tag + ' class="' + options.wrap_class + '" id="' + options.wrap_id + '"></' + options.wrap_tag + '>';
			$elements = $elements.add(this);

			if(type === 'select') {
				// Settings specific to selects
				options  =  $.extend({
					selected        :  $el.find('option:selected'),
					visible_class   :  class_prefix + type +'-visible',
					fallback        :  '

',
					visible_tag     :  'div'
				}, options);
				options.visible_html   =  '<' + options.visible_tag + ' class="' + options.visible_class + '">' + options.selected.html() + '</' + options.visible_tag + '>';
			}else {
				// Makes sure change event is fired when jQuery change is called on element
				$.propHooks.checked = {
					set: function(el, value) {
						if ($elements.filter(el).length !== 0) {
							el.checked = value;
							$(el).trigger('change');
						}
					}
				};
				if(type === 'radio') {
					$radios = $radios.add(this);
				}
			}

			$el.data(data_key, options);
			methods._make.apply(this);

			return this;
		},

		_make  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key);

			options.$wrap = $el.wrap(options.wrap_html).parent()
				.css({
					'width'          :  $el.outerWidth() + 'px',
					'height'          :  $el.outerHeight() + 'px'
				})
				.addClass(data_key + '-' + $el.attr('class'))
				.attr('id', data_key + '-' + $el.attr('id' ));

			if(options.type === 'select') {
				options.$visible  =  $(options.visible_html).appendTo(options.$wrap);
				if ($.browser.msie  && parseInt($.browser.version, 10) < 8) {
					$(options.fallback).appendTo(options.$wrap);
				}
			}else if(options.type === 'radio') {
				// Need to check all since checking one in a group doesn't fire change on the element that looses checked
				$el.change(function(){ $radios.each(function(){ methods.update.apply(this) }); });
			}

			$el
				.change(methods.update)
				.each(methods.update)
				.trigger(component_initialized, options.$wrap, options)
				.addClass(class_prefix +'active');

			return this;
		},

		/* usage: jQuery( element ).cw_fancy_forms("update") */
		update  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key),
				checked  =  $el.prop('checked');

			if((options.type === 'select') && options.$visible) {
				options.selected  =  options.$wrap.find('option:selected');
				options.$visible.html(options.selected.html());
			}else {
				checked === true ? $el.parent().addClass(options.selected_class) : $el.parent().removeClass(options.selected_class);
			}

		}
	};

	$.fn.cw_fancy_forms  =  function (method) {

		if (methods[method] && method.charAt(0) !== '_') {
			return $(this).map(function(i, val) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); });
		} else if (typeof method === 'object' || !method) {
			var args  =  arguments;
			return $(this).map(function(i, val) { return methods._init_el.apply(this, args); });
		}
	};

}(jQuery));<commit_msg>added class to disabled elements<commit_after>/*
Project Name: Fancy Forms
URL: https://github.com/keobrien/Fancy-Forms
Author: Kevin O'Brien
Company: Clockwork Acive Media Systems
Company Site: clockwork.net
License: MIT
Copyright (C) 2012 Clockwork Active Media Systems
Version: 0.2
**************************************/

(function ($) {

	"use strict";

	// These locally scoped variables help minification by aliasing strings
	var data_key               =  'CW_fancy_forms',
		event_suffix           =  '.CW_fancy_forms',
		class_prefix           =  'fancy-',
	// Custom events
		component_initialized  =  'component_initialized' + event_suffix,
		$elements              =  $(), // Used to filter propHooks to plugin elements only
		$radios                =  $(); // Used to make the radio updater faster

	var methods  =  {

		_init_el  :  function (settings) {

			if($(this).hasClass(class_prefix + 'active')) { return this; }

			var $el  =  $(this),
				type =  $el.prop("tagName").toLowerCase(),
				type =  type === 'select' ? type : $el.attr('type'),
				options  =  $.extend({
					// Settings for selects, checkboxes and radios
					selected_class  :  class_prefix + type + '-checked',
					disabled_class  :  class_prefix + type + '-disabled',
					wrap_class      :  class_prefix + type +'-wrap',
					wrap_id         :  $el.attr('id') ? class_prefix + type +'-' + $el.attr('id') : '',
					wrap_tag        :  'div',
					type            :  type
				}, settings);
			options.wrap_html       =  '<' + options.wrap_tag + ' class="' + options.wrap_class + '" id="' + options.wrap_id + '"></' + options.wrap_tag + '>';
			$elements = $elements.add(this);

			if(type === 'select') {
				// Settings specific to selects
				options  =  $.extend({
					selected        :  $el.find('option:selected'),
					visible_class   :  class_prefix + type +'-visible',
					fallback        :  '

',
					visible_tag     :  'div'
				}, options);
				options.visible_html   =  '<' + options.visible_tag + ' class="' + options.visible_class + '">' + options.selected.html() + '</' + options.visible_tag + '>';
			}else {
				// Makes sure change event is fired when jQuery change is called on element
				$.propHooks.checked = {
					set: function(el, value) {
						if ($elements.filter(el).length !== 0) {
							el.checked = value;
							$(el).trigger('change');
						}
					}
				};
				if(type === 'radio') {
					$radios = $radios.add(this);
				}
			}

			$el.data(data_key, options);
			methods._make.apply(this);

			return this;
		},

		_make  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key);

			options.$wrap = $el.wrap(options.wrap_html).parent()
				.css({
					'width'          :  $el.outerWidth() + 'px',
					'height'          :  $el.outerHeight() + 'px'
				})
				.addClass(data_key + '-' + $el.attr('class'))
				.attr('id', data_key + '-' + $el.attr('id' ));

			if(options.type === 'select') {
				options.$visible  =  $(options.visible_html).appendTo(options.$wrap);
				if ($.browser.msie  && parseInt($.browser.version, 10) < 8) {
					$(options.fallback).appendTo(options.$wrap);
				}
			}else if(options.type === 'radio') {
				// Need to check all since checking one in a group doesn't fire change on the element that looses checked
				$el.change(function(){ $radios.each(function(){ methods.update.apply(this) }); });
			}

			$el
				.change(methods.update)
				.each(methods.update)
				.trigger(component_initialized, options.$wrap, options)
				.addClass(class_prefix +'active');

			return this;
		},

		/* usage: jQuery( element ).cw_fancy_forms("update") */
		update  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key),
				checked  =  $el.prop('checked'),
				disabled =  $el.prop('disabled');

			if((options.type === 'select') && options.$visible) {
				options.selected  =  options.$wrap.find('option:selected');
				options.$visible.html(options.selected.html());
			}else {
				checked === true ? $el.parent().addClass(options.selected_class) : $el.parent().removeClass(options.selected_class);
			}
			if(disabled) {
				$el.parent().addClass(options.disabled_class);
			}

		}
	};

	$.fn.cw_fancy_forms  =  function (method) {

		if (methods[method] && method.charAt(0) !== '_') {
			return $(this).map(function(i, val) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); });
		} else if (typeof method === 'object' || !method) {
			var args  =  arguments;
			return $(this).map(function(i, val) { return methods._init_el.apply(this, args); });
		}
	};

}(jQuery));<|endoftext|>


<commit_before>App.Project.reopen({

    isFundable: Em.computed.equal('status', 6),

    isStatusPlan: Em.computed.equal('status.id', 5),
    isStatusCampaign: Em.computed.equal('status.id', 6),
    isStatusCompleted: Em.computed.equal('status.id', 8)

});<commit_msg>reopen MyProject model and override required / friendly field properties<commit_after>App.Project.reopen({
    url: "projects",
    isFundable: Em.computed.equal('status', 6),

    isStatusPlan: Em.computed.equal('status.id', 5),
    isStatusCampaign: Em.computed.equal('status.id', 6),
    isStatusCompleted: Em.computed.equal('status.id', 8)

});

App.MyProject.reopen({
    image: DS.attr('image'),
    video: DS.attr('string'),

    requiredStoryFields: ['description', 'goal', 'destination_impact'],
    requiredPitchFields: ['title', 'pitch', 'image', 'theme', 'tags.length', 'deadline', 'country', 'latitude', 'longitude'],

    friendlyFieldNames: {
        'title' : 'Title',
        'pitch': 'Description',
        'image' : 'Image',
        'theme' : 'Theme',
        'tags.length': 'Tags',
        'deadline' : 'Deadline',
        'country' : 'Country',
        'description': 'Why, what and how',
        'goal' : 'Goal',
        'destination_impact' : 'Destination impact'
    }

});<|endoftext|>


<commit_before>// Inspired from https://github.com/zgreen/lighthouse-circleci-example/blob/master/lighthouse.test.js
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher')

const TIME_FOR_LIGHTHOUSE_FULL_TEST = 60000
jest.setTimeout(TIME_FOR_LIGHTHOUSE_FULL_TEST)

const launchChromeAndRunLighthouse = async url => {
  const chrome = await chromeLauncher.launch({ chromeFlags: [] })
  const opts = { chromeFlags: [], port: chrome.port }
  const configJSON = null
  const results = await lighthouse(url, opts, configJSON)
  await chrome.kill()
  return results
}

const buildLighthouseReport = results => {
  const { categories } = results.lhr
  let rows = ''
  const scores = Object.values(categories).map(({ score }) => score * 100)
  const lighthouseScore = scores.reduce((a, b) => a + b, 0) / scores.length

  Object.values(categories).forEach(cat => {
    rows += `| ${cat.title} | ${cat.score * 100} |\n`
  })
  console.log('Total Lighthouse score is : ', lighthouseScore)
  console.log(rows)

  return { lighthouseScore, categories }
}

test('Article 85 : Meaningful first paint score', () =>
  launchChromeAndRunLighthouse('https://french-test.recontact.me/articles/85')
    .then(buildLighthouseReport)
    .then(report => {
      const { categories, lighthouseScore } = report

      expect(lighthouseScore).toBeGreaterThanOrEqual(75) // 78
      expect(categories.performance.score * 100).toBeGreaterThanOrEqual(60) // 34, 64, 65
      expect(categories.pwa.score * 100).toBeGreaterThanOrEqual(56) // 56
      expect(categories.accessibility.score * 100).toBeGreaterThanOrEqual(84) // 84
      expect(categories['best-practices'].score * 100).toBeGreaterThanOrEqual(86) // 77, 86
      expect(categories.seo.score * 100).toBeGreaterThanOrEqual(100) // 100
    }))

test('HomePage : Meaningful first paint score', () =>
  launchChromeAndRunLighthouse('https://french-test.recontact.me')
    .then(buildLighthouseReport)
    .then(report => {
      const { categories, lighthouseScore } = report

      expect(lighthouseScore).toBeGreaterThanOrEqual(83) // 86
      expect(categories.performance.score * 100).toBeGreaterThanOrEqual(85) // 93, 95, 90
      expect(categories.pwa.score * 100).toBeGreaterThanOrEqual(59) // 59
      expect(categories.accessibility.score * 100).toBeGreaterThanOrEqual(96) // 96
      expect(categories['best-practices'].score * 100).toBeGreaterThanOrEqual(86) // 92, 86
      expect(categories.seo.score * 100).toBeGreaterThanOrEqual(100) // 100
    }))

test('Static Page : Meaningful first paint score', () =>
  launchChromeAndRunLighthouse('https://french-test.recontact.me/browserconfig.xml')
    .then(buildLighthouseReport)
    .then(report => {
      const { categories, lighthouseScore } = report

      expect(lighthouseScore).toBeGreaterThanOrEqual(47) // 47
      expect(categories.performance.score * 100).toBeGreaterThanOrEqual(100) // 100
      expect(categories.pwa.score * 100).toBeGreaterThanOrEqual(44) // 44
      expect(categories.accessibility.score * 100).toBeGreaterThanOrEqual(0) // 0
      expect(categories['best-practices'].score * 100).toBeGreaterThanOrEqual(92) // 92, 93
      expect(categories.seo.score * 100).toBeGreaterThanOrEqual(0) // 0
    }))
<commit_msg>chore(ci): improve lighthouse efficiency<commit_after>// Inspired from https://github.com/zgreen/lighthouse-circleci-example/blob/master/lighthouse.test.js
const lighthouse = require('lighthouse')
const chromeLauncher = require('chrome-launcher')

const TIME_FOR_LIGHTHOUSE_FULL_TEST = 60000
jest.setTimeout(TIME_FOR_LIGHTHOUSE_FULL_TEST)

const launchChromeAndRunLighthouse = async url => {
  const chrome = await chromeLauncher.launch({ chromeFlags: [] })
  const opts = { chromeFlags: [], port: chrome.port }
  const configJSON = null
  const results = await lighthouse(url, opts, configJSON)
  await chrome.kill()
  return results
}

const buildLighthouseReport = results => {
  const { categories } = results.lhr
  let rows = ''
  const scores = Object.values(categories).map(({ score }) => score * 100)
  const lighthouseScore = scores.reduce((a, b) => a + b, 0) / scores.length

  Object.values(categories).forEach(cat => {
    rows += `| ${cat.title} | ${cat.score * 100} |\n`
  })
  console.log('Total Lighthouse score is : ', lighthouseScore)
  console.log(rows)

  return { lighthouseScore, categories }
}

test('Article 85 : Meaningful first paint score', () =>
  launchChromeAndRunLighthouse('https://french-test.recontact.me/articles/85')
    .then(buildLighthouseReport)
    .then(report => {
      const { categories, lighthouseScore } = report

      expect(lighthouseScore).toBeGreaterThanOrEqual(75) // 78
      expect(categories.performance.score * 100).toBeGreaterThanOrEqual(60) // 34, 64, 65, 64 (-4 just in case)
      expect(categories.accessibility.score * 100).toBeGreaterThanOrEqual(84) // 84
      expect(categories['best-practices'].score * 100).toBeGreaterThanOrEqual(93) // 93
      expect(categories.seo.score * 100).toBeGreaterThanOrEqual(100) // 100
      expect(categories.pwa.score * 100).toBeGreaterThanOrEqual(56) // 56
    }))

test('HomePage : Meaningful first paint score', () =>
  launchChromeAndRunLighthouse('https://english-test.recontact.me')
    .then(buildLighthouseReport)
    .then(report => {
      const { categories, lighthouseScore } = report

      expect(lighthouseScore).toBeGreaterThanOrEqual(83) // 86
      expect(categories.performance.score * 100).toBeGreaterThanOrEqual(85) // 94, 93, 95, 90 (-10 just in case)
      expect(categories.accessibility.score * 100).toBeGreaterThanOrEqual(96) // 96
      expect(categories['best-practices'].score * 100).toBeGreaterThanOrEqual(93) // 93
      expect(categories.seo.score * 100).toBeGreaterThanOrEqual(100) // 100
      expect(categories.pwa.score * 100).toBeGreaterThanOrEqual(59) // 59
    }))

<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';
import isAsync from 'ember-localforage-adapter/utils/is-async';
import isObject from 'ember-localforage-adapter/utils/is-object';

export default DS.Adapter.extend(Ember.Evented, {

  defaultSerializer: 'localforage',
  queue: LFQueue.create(),
  cache: LFCache.create(),
  caching: 'model',
  coalesceFindRequests: true,

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /**
   * This is the main entry point into finding records. The first parameter to
   * this method is the model's name as a string.
   *
   * @method findRecord
   * @param store
   * @param {DS.Model} type
   * @param {Object|String|Integer|null} id
   */
  findRecord(store, type, id) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const record = namespaceData.records[id];

      if (!record) {
        return Ember.RSVP.reject();
      }

      return record;
    });
  },

  findAll(store, type) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let id in namespaceData.records) {
        records.push(namespaceData.records[id]);
      }

      return records;
    });
  },

  findMany(store, type, ids) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let i = 0; i < ids.length; i++) {
        const record = namespaceData.records[ids[i]];

        if (record) {
          records.push(record);
        }
      }

      return records;
    });
  },

  queryRecord(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        const record = _this._query(namespaceData.records, query, true);

        if (!record) {
          reject(new Error(`Record of type ${modelName} with id '${record.id}' is not fulfilling specified query`));
        }

        _this._completeLoadRecord(store, type, record, proj).then(function(completeRecord) {
          resolve(completeRecord);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  /**
   *  Supports queries that look like this:
   *   {
   *     <property to query>: <value or regex (for strings) to match>,
   *     ...
   *   }
   *
   * Every property added to the query is an "AND" query, not "OR"
   *
   * Example:
   * match records with "complete: true" and the name "foo" or "bar"
   *  { complete: true, name: /foo|bar/ }
   */
  query(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        var recordArray = _this._query(namespaceData.records, query);
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch(function(reason) {
          reject(reason);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  _query(records, query, singleMatch) {
    const results = singleMatch ? null : [];

    for (let id in records) {
      const record = records[id];
      let isMatching = false;

      for (let property in query) {
        const queryValue = query[property];

        if (queryValue instanceof RegExp) {
          isMatching = queryValue.test(record[property]);
        } else {
          isMatching = record[property] === queryValue;
        }

        if (!isMatching) {
          break; // all criteria should pass
        }
      }

      if (isMatching || Ember.$.isEmptyObject(query)) {
        if (singleMatch) {
          return record;
        }

        results.push(record);
      }
    }

    return results;
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord(store, type, snapshot) {
    return this.queue.attach((resolve) => {
      this._getNamespaceData(type).then((namespaceData) => {
        delete namespaceData.records[snapshot.id];

        this._setNamespaceData(type, namespaceData).then(() => {
          resolve();
        });
      });
    });
  },

  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {subclass of DS.Model} typeClass Model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {subclass of DS.Store} store Store to use for complete loading record.
   * @param {subclass of DS.Model} type Model type.
   * @param {Object} record Main record loaded by adapter.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(store, type, record, projection) {
    let promises = Ember.A();
    let attributes = projection.attributes;
    for (var attrName in attributes) {
      if (attributes.hasOwnProperty(attrName)) {
        this._replaceIdToHash(store, type,  record, attributes, attrName, promises);
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        }
        else {
          let hasUnloadedObjects = false;
          for (let j = 0; j < record[relationshipName].length; j++) {
            if (!isObject(record[relationshipName][j])) {
              hasUnloadedObjects = true;
            }
          }

          if (hasUnloadedObjects) {
            record[relationshipName] = [];
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj) {
    let relatedRecord = store.peekRecord(proj.modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let options = {
        id: id,
        projection: proj
      };
      return this.queryRecord(store, type, options);
    }
    else {
      let relatedRecordObject = relatedRecord.serialize({includeId: true});
      return this._completeLoadRecord(store, type, relatedRecordObject, proj);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes[attrName];
      let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(attr.modelName) : null;
      switch (attr.kind) {
        case 'attr':
          break;
        case 'belongsTo':
          if (!isAsync(type, attrName)) {
            // let primaryKeyName = this.serializer.get('primaryKey');
            let id = record[attrName];
            if (!Ember.isNone(id)) {
              promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                delete record[attrName];
                record[attrName] = relatedRecord;
              }));
            }
          }

          break;
        case 'hasMany':
          if (!isAsync(type, attrName)) {
            if (Ember.isArray(record[attrName])) {
              let ids = Ember.copy(record[attrName]);
              delete record[attrName];
              record[attrName] = [];
              for (var i = 0; i < ids.length; i++) {
                let id = ids[i];
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                  record[attrName].push(relatedRecord);
                }));
              }
            }
            else {
              record[attrName] = [];
            }
          }

          break;
        default:
          throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
      }
  },

  _setNamespaceData(type, namespaceData) {
    const modelNamespace = this._modelNamespace(type);

    return this._loadData().then((storage) => {
      if (this.caching !== 'none') {
        this.cache.set(modelNamespace, namespaceData);
      }

      storage[modelNamespace] = namespaceData;

      return window.localforage.setItem(this._adapterNamespace(), storage);
    });
  },

  _getNamespaceData(type) {
    const modelNamespace = this._modelNamespace(type);

    if (this.caching !== 'none') {
      const cache = this.cache.get(modelNamespace);

      if (cache) {
        return Ember.RSVP.resolve(cache);
      }
    }

    return this._loadData().then((storage) => {
      const namespaceData = storage && storage[modelNamespace] || { records: {} };

      if (this.caching === 'model') {
        this.cache.set(modelNamespace, namespaceData);
      } else if (this.caching === 'all') {
        if (storage) {
          this.cache.replace(storage);
        }
      }

      return namespaceData;
    });
  },

  _loadData() {
    return window.localforage.getItem(this._adapterNamespace()).then((storage) => {
      return storage ? storage : {};
    });
  },

  _modelNamespace(type) {
    return type.url || type.modelName;
  },

  _adapterNamespace() {
    return this.get('namespace') || 'DS.LFAdapter';
  }
});

function updateOrCreate(store, type, snapshot) {
  return this.queue.attach((resolve) => {
    this._getNamespaceData(type).then((namespaceData) => {
      const serializer = store.serializerFor(type.modelName);
      const recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      const id = snapshot.id || recordHash.id;

      namespaceData.records[id] = recordHash;

      this._setNamespaceData(type, namespaceData).then(() => {
        resolve();
      });
    });
  });
}
<commit_msg>Fix removing relationships that not included into projection<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import LFQueue from 'ember-localforage-adapter/utils/queue';
import LFCache from 'ember-localforage-adapter/utils/cache';
import isAsync from 'ember-localforage-adapter/utils/is-async';
import isObject from 'ember-localforage-adapter/utils/is-object';

export default DS.Adapter.extend(Ember.Evented, {

  defaultSerializer: 'localforage',
  queue: LFQueue.create(),
  cache: LFCache.create(),
  caching: 'model',
  coalesceFindRequests: true,

  shouldBackgroundReloadRecord() {
    return false;
  },

  shouldReloadAll() {
    return true;
  },

  /**
   * This is the main entry point into finding records. The first parameter to
   * this method is the model's name as a string.
   *
   * @method findRecord
   * @param store
   * @param {DS.Model} type
   * @param {Object|String|Integer|null} id
   */
  findRecord(store, type, id) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const record = namespaceData.records[id];

      if (!record) {
        return Ember.RSVP.reject();
      }

      return record;
    });
  },

  findAll(store, type) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let id in namespaceData.records) {
        records.push(namespaceData.records[id]);
      }

      return records;
    });
  },

  findMany(store, type, ids) {
    return this._getNamespaceData(type).then((namespaceData) => {
      const records = [];

      for (let i = 0; i < ids.length; i++) {
        const record = namespaceData.records[ids[i]];

        if (record) {
          records.push(record);
        }
      }

      return records;
    });
  },

  queryRecord(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        const record = _this._query(namespaceData.records, query, true);

        if (!record) {
          reject(new Error(`Record of type ${modelName} with id '${record.id}' is not fulfilling specified query`));
        }

        _this._completeLoadRecord(store, type, record, proj).then(function(completeRecord) {
          resolve(completeRecord);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  /**
   *  Supports queries that look like this:
   *   {
   *     <property to query>: <value or regex (for strings) to match>,
   *     ...
   *   }
   *
   * Every property added to the query is an "AND" query, not "OR"
   *
   * Example:
   * match records with "complete: true" and the name "foo" or "bar"
   *  { complete: true, name: /foo|bar/ }
   */
  query(store, type, query) {
    var modelName = type.modelName;
    var proj = this._extractProjectionFromQuery(modelName, type, query);
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      _this._getNamespaceData(type).then((namespaceData) => {
        var recordArray = _this._query(namespaceData.records, query);
        let promises = Ember.A();
        for (let i = 0; i < recordArray.length; i++) {
          let record = recordArray[i];
          promises.pushObject(_this._completeLoadRecord(store, type, record, proj));
        }

        Ember.RSVP.all(promises).then(() => {
          resolve(recordArray);
        }).catch(function(reason) {
          reject(reason);
        });
      }).catch(function(reason) {
        reject(reason);
      });
    });
  },

  _query(records, query, singleMatch) {
    const results = singleMatch ? null : [];

    for (let id in records) {
      const record = records[id];
      let isMatching = false;

      for (let property in query) {
        const queryValue = query[property];

        if (queryValue instanceof RegExp) {
          isMatching = queryValue.test(record[property]);
        } else {
          isMatching = record[property] === queryValue;
        }

        if (!isMatching) {
          break; // all criteria should pass
        }
      }

      if (isMatching || Ember.$.isEmptyObject(query)) {
        if (singleMatch) {
          return record;
        }

        results.push(record);
      }
    }

    return results;
  },

  createRecord: updateOrCreate,

  updateRecord: updateOrCreate,

  deleteRecord(store, type, snapshot) {
    return this.queue.attach((resolve) => {
      this._getNamespaceData(type).then((namespaceData) => {
        delete namespaceData.records[snapshot.id];

        this._setNamespaceData(type, namespaceData).then(() => {
          resolve();
        });
      });
    });
  },

  generateIdForRecord() {
    return Math.random().toString(32).slice(2).substr(0, 5);
  },

  // private

  /**
   * Retrieves projection from query and returns it.
   * Retrieved projection removes from the query.
   *
   * @method _extractProjectionFromQuery
   * @private
   *
   * @param {String} modelName The name of the model type.
   * @param {subclass of DS.Model} typeClass Model type.
   * @param {Object} [query] Query parameters.
   * @param {String} query.projection Projection name.
   * @return {Object} Extracted projection from query or null
   *                  if projection is not set in query.
   */
  _extractProjectionFromQuery: function(modelName, typeClass, query) {
    if (query && query.projection) {
      let proj = query.projection;
      if (typeof query.projection === 'string') {
        let projName = query.projection;
        proj = typeClass.projections.get(projName);
      }

      delete query.projection;
      return proj;
    }

    return null;
  },

  /**
   * Completes loading record for given projection.
   *
   * @method _completeLoadingRecord
   * @private
   *
   * @param {subclass of DS.Store} store Store to use for complete loading record.
   * @param {subclass of DS.Model} type Model type.
   * @param {Object} record Main record loaded by adapter.
   * @param {Object} projection Projection for complete loading of record.
   * @return {Object} Completely loaded record with all properties
   *                  include relationships corresponds to given projection
   */
  _completeLoadRecord: function(store, type, record, projection) {
    let promises = Ember.A();
    let attributes = projection.attributes;
    for (var attrName in attributes) {
      if (attributes.hasOwnProperty(attrName)) {
        this._replaceIdToHash(store, type,  record, attributes, attrName, promises);
      }
    }

    return Ember.RSVP.all(promises).then(() => {
      let relationshipNames = Ember.get(type, 'relationshipNames');
      let belongsTo = relationshipNames.belongsTo;
      for (let i = 0; i < belongsTo.length; i++) {
        let relationshipName = belongsTo[i];
        if (!isAsync(type, relationshipName) && !isObject(record[relationshipName])) {
          record[relationshipName] = null;
        }
      }

      let hasMany = relationshipNames.hasMany;
      for (let i = 0; i < hasMany.length; i++) {
        let relationshipName = hasMany[i];
        if (!Ember.isArray(record[relationshipName])) {
          record[relationshipName] = [];
        }
        else {
          if (!isAsync(type, relationshipName)) {
            let hasUnloadedObjects = false;
            for (let j = 0; j < record[relationshipName].length; j++) {
              if (!isObject(record[relationshipName][j])) {
                hasUnloadedObjects = true;
              }
            }

            if (hasUnloadedObjects) {
              record[relationshipName] = [];
            }
          }
        }
      }

      return record;
    });
  },

  _loadRelatedRecord(store, type, id, proj) {
    let relatedRecord = store.peekRecord(proj.modelName, id);
    if (Ember.isNone(relatedRecord)) {
      let options = {
        id: id,
        projection: proj
      };
      return this.queryRecord(store, type, options);
    }
    else {
      let relatedRecordObject = relatedRecord.serialize({includeId: true});
      return this._completeLoadRecord(store, type, relatedRecordObject, proj);
    }
  },

  _replaceIdToHash(store, type,  record, attributes, attrName, promises) {
    let attr = attributes[attrName];
      let relatedModelType = (attr.kind === 'belongsTo' || attr.kind === 'hasMany') ? store.modelFor(attr.modelName) : null;
      switch (attr.kind) {
        case 'attr':
          break;
        case 'belongsTo':
          if (!isAsync(type, attrName)) {
            // let primaryKeyName = this.serializer.get('primaryKey');
            let id = record[attrName];
            if (!Ember.isNone(id)) {
              promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                delete record[attrName];
                record[attrName] = relatedRecord;
              }));
            }
          }

          break;
        case 'hasMany':
          if (!isAsync(type, attrName)) {
            if (Ember.isArray(record[attrName])) {
              let ids = Ember.copy(record[attrName]);
              delete record[attrName];
              record[attrName] = [];
              for (var i = 0; i < ids.length; i++) {
                let id = ids[i];
                promises.pushObject(this._loadRelatedRecord(store, relatedModelType, id, attr).then((relatedRecord) => {
                  record[attrName].push(relatedRecord);
                }));
              }
            }
            else {
              record[attrName] = [];
            }
          }

          break;
        default:
          throw new Error(`Unknown kind of projection attribute: ${attr.kind}`);
      }
  },

  _setNamespaceData(type, namespaceData) {
    const modelNamespace = this._modelNamespace(type);

    return this._loadData().then((storage) => {
      if (this.caching !== 'none') {
        this.cache.set(modelNamespace, namespaceData);
      }

      storage[modelNamespace] = namespaceData;

      return window.localforage.setItem(this._adapterNamespace(), storage);
    });
  },

  _getNamespaceData(type) {
    const modelNamespace = this._modelNamespace(type);

    if (this.caching !== 'none') {
      const cache = this.cache.get(modelNamespace);

      if (cache) {
        return Ember.RSVP.resolve(cache);
      }
    }

    return this._loadData().then((storage) => {
      const namespaceData = storage && storage[modelNamespace] || { records: {} };

      if (this.caching === 'model') {
        this.cache.set(modelNamespace, namespaceData);
      } else if (this.caching === 'all') {
        if (storage) {
          this.cache.replace(storage);
        }
      }

      return namespaceData;
    });
  },

  _loadData() {
    return window.localforage.getItem(this._adapterNamespace()).then((storage) => {
      return storage ? storage : {};
    });
  },

  _modelNamespace(type) {
    return type.url || type.modelName;
  },

  _adapterNamespace() {
    return this.get('namespace') || 'DS.LFAdapter';
  }
});

function updateOrCreate(store, type, snapshot) {
  return this.queue.attach((resolve) => {
    this._getNamespaceData(type).then((namespaceData) => {
      const serializer = store.serializerFor(type.modelName);
      const recordHash = serializer.serialize(snapshot, {includeId: true});
      // update(id comes from snapshot) or create(id comes from serialization)
      const id = snapshot.id || recordHash.id;

      namespaceData.records[id] = recordHash;

      this._setNamespaceData(type, namespaceData).then(() => {
        resolve();
      });
    });
  });
}
<|endoftext|>


<commit_before>"use strict";

angular.module("ngTouchmove", []).directive("ngTouchmove", function () {
  return {
    controller: function ($scope, $element, $attrs) {
      $element.bind('touchstart', onTouchStart);

      function onTouchStart(event) {
        event.preventDefault();
        $element.bind('touchmove', onTouchMove);
        $element.bind('touchend', onTouchEnd);
      };

      function onTouchMove(event) {
          var method = $element.attr('ng-touchmove');
          $scope.$apply(method);
      };

      function onTouchEnd(event) {
        event.preventDefault();
        $element.unbind('touchmove', onTouchMove);
        $element.unbind('touchend', onTouchEnd);
      };
    }
  };
});
<commit_msg>Added $event to $scope.<commit_after>"use strict";

angular.module("ngTouchmove", []).directive("ngTouchmove", function () {
  return {
    controller: function ($scope, $element, $attrs) {
      $element.bind('touchstart', onTouchStart);

      function onTouchStart(event) {
        event.preventDefault();
        $element.bind('touchmove', onTouchMove);
        $element.bind('touchend', onTouchEnd);
      };

      function onTouchMove(event) {
          var method = $element.attr('ng-touchmove');
          $scope.$event = event;
          $scope.$apply(method);
      };

      function onTouchEnd(event) {
        event.preventDefault();
        $element.unbind('touchmove', onTouchMove);
        $element.unbind('touchend', onTouchEnd);
      };
    }
  };
});
<|endoftext|>