<commit_before>window.onload = function() {
	document.getElementById("confirmLogout").onclick = logout;
	document.getElementById("submit").onclick = submit;
	spamChecker();
}

var spamValue1 = Math.round(Math.random() * 10);
var spamValue2 = Math.round(Math.random() * 10);

function isLoggedIn()
{
	var user = app.sessionDatabase.read();
	return user.isLoggedIn === true;
}

function logout()
{
	// log the user out and create an empty user that is not logged in
	// as a default
	var user = {email: "",
				password: "",
				LoggedIn: false}
	app.sessionDatabase.write(user);
	window.location = "SignIn.html";
}

function spamChecker()
{

var spamAnswer = spamValue1 + spamValue2;

var label = document.getElementById("spamChecker");

label.innerHTML = "What is " + spamValue1 + " + " + spamValue2 + "?";

}

function submit()
{

	var answer = document.getElementById("InputReal").value;
	if (answer == (spamValue1 + spamValue2))
	{
		alert("Holy shit, it worked.");
	}
	else
	{
		alert("You're bad at this.");
	}

}<commit_msg>Added "(Prove you're a human)" to the spam checker<commit_after>window.onload = function() {
	document.getElementById("confirmLogout").onclick = logout;
	document.getElementById("submit").onclick = submit;
	spamChecker();
}

var spamValue1 = Math.round(Math.random() * 10);
var spamValue2 = Math.round(Math.random() * 10);

function isLoggedIn()
{
	var user = app.sessionDatabase.read();
	return user.isLoggedIn === true;
}

function logout()
{
	// log the user out and create an empty user that is not logged in
	// as a default
	var user = {email: "",
				password: "",
				LoggedIn: false}
	app.sessionDatabase.write(user);
	window.location = "SignIn.html";
}

function spamChecker()
{

var spamAnswer = spamValue1 + spamValue2;

var label = document.getElementById("spamChecker");

label.innerHTML = "What is " + spamValue1 + " + " + spamValue2 + "? (Prove you're a human)";

}

function submit()
{

	var answer = document.getElementById("InputReal").value;
	if (answer == (spamValue1 + spamValue2))
	{
		alert("Holy shit, it worked.");
	}
	else
	{
		alert("You're bad at this.");
	}

}<|endoftext|>


<commit_before>'use strict';

module.exports = {
    type: 'object', title: 'Favorites',
    properties: {
        years: {
            type: 'object', title: 'Years',
            properties: {
                from: {
                    type: 'integer', title: 'From'
                },
                to: {
                    type: 'integer', title: 'To'
                }
            }
        },
        contry: {
            type: 'string', title: 'Country'
        },
        bands: {
            type: 'array', title: 'The Bands',
            items: {
                type: 'object', title: 'Band data',
                properties: {
                    name: { type: 'string', title: 'Band name' },
                    members: {
                        type: 'array', title: 'Band members',
                        items: {
                            type: 'object', title: 'Band member',
                            properties: {
                                first: {
                                    type: 'string',
                                    title: 'First name',
                                    pattern: '^[a-zA-Z]+$'
                                },
                                last: {
                                    type: 'string', title: 'Last name'
                                },
                                tools: {
                                    type: 'array', title: 'Tools',
                                    items: {
                                        type: 'string', title: 'instrument'
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
<commit_msg>testing null and number<commit_after>'use strict';

module.exports = {
    type: 'object', title: 'Favorites',
    properties: {
        years: {
            type: 'object', title: 'Years',
            properties: {
                from: {
                    type: 'integer', title: 'From'
                },
                to: {
                    type: 'number', title: 'To'
                }
            }
        },
        contry: {
            type: 'null', title: 'Country'
        },
        bands: {
            type: 'array', title: 'The Bands',
            items: {
                type: 'object', title: 'Band data',
                properties: {
                    name: { type: 'string', title: 'Band name' },
                    members: {
                        type: 'array', title: 'Band members',
                        items: {
                            type: 'object', title: 'Band member',
                            properties: {
                                first: {
                                    type: 'string',
                                    title: 'First name',
                                    pattern: '^[a-zA-Z]+$'
                                },
                                last: {
                                    type: 'string', title: 'Last name'
                                },
                                tools: {
                                    type: 'array', title: 'Tools',
                                    items: {
                                        type: 'string', title: 'instrument'
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
};
<|endoftext|>


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

var suite = vows.describe('mpld3.Coordinates')

suite.addBatch({
    "Coordinates": {
	topic: load("core/figure", "core/axes", "core/coordinates").document(),
	"A Coordinates('display') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [0, 1], ylim: [0, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("display", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([100, 100])[0], 100);
                assert.equal(coords.xy([100, 100])[1], 100);
	    }
	},
	"A Coordinates('data') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("data", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 208);
                assert.equal(coords.xy([0.3, 0.4])[1], 72);
	    }
	},
	"A Coordinates('axes') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("axes", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 96);
                assert.equal(coords.xy([0.3, 0.4])[1], 144);
	    }
	},
	"A Coordinates('figure') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("figure", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 80);
                assert.equal(coords.xy([0.3, 0.4])[1], 150);
	    }
	}
    }
});

suite.export(module);
<commit_msg>BUG: change assert.equals to a quick version of 'almost equals'<commit_after>var vows = require("vows"),
    load = require("../load"),
    assert = require("assert");

var suite = vows.describe('mpld3.Coordinates')

suite.addBatch({
    "Coordinates": {
	topic: load("core/figure", "core/axes", "core/coordinates").document(),
	"A Coordinates('display') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [0, 1], ylim: [0, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("display", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([100, 100])[0], 100);
                assert.equal(coords.xy([100, 100])[1], 100);
	    }
	},
	"A Coordinates('data') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("data", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 208);
                assert.equal(Math.round(coords.xy([0.3, 0.4])[1]*1000), Math.round(72*1000));
	    }
	},
	"A Coordinates('axes') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("axes", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 96);
                assert.equal(coords.xy([0.3, 0.4])[1], 144);
	    }
	},
	"A Coordinates('figure') object": {
	    topic: function(mpld3) {
                var fig_props = {
                    width: 400,
                    height: 300,
                    axes: [{xlim: [-1, 1], ylim: [-1, 1]}]
                };
                var fig = new mpld3.Figure("chart", fig_props);
                fig.draw();
                return new mpld3.Coordinates("figure", fig.axes[0]);
	    },
	    "transforms to the correct display coordinates": function(coords){
                assert.equal(coords.xy([0.3, 0.4])[0], 80);
                assert.equal(coords.xy([0.3, 0.4])[1], 150);
	    }
	}
    }
});

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


<commit_before>// Third Party
var PdfPrinter = require('pdfmake/src/printer');
//PdfModule
var fichaReme = require('../pdf/fichaReme');
// Models
Ficha = require('../models/').Ficha;
Users = require('../models/').User;
// CRUD Operations for Ficha Model
module.exports = {
    index(req, res) {
        Ficha.findAll()
            .then(function (Fichas) {
                res.status(200).json(Fichas);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    show(req, res) {
        Ficha.findAll({
            where: {
                id: req.params.id
            },
            include: [{ model: Users }]
        })
            .then(function (Ficha) {
                res.status(200).json(Ficha);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    create(req, res) {
        // res.status(200).json(req.body);
        Ficha.create(req.body)
            .then(function (newFicha) {
                res.status(200).json(newFicha);
            })
            .catch(function (error) {
                res.status(500).json(error);
            });
    },

    update(req, res) {
        Ficha.update(req.body, {
            where: {
                id: req.body.id
            }
        })
            .then(function (updatedRecords) {
                res.status(200).json(updatedRecords);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    delete(req, res) {
        console.log(req.params)
        Ficha.destroy({
            where: {
                id: req.params.id
            }
        })
            .then(function (deletedRecords) {
                res.status(200).json(deletedRecords);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    print(req, res) {
        Ficha.findAll({
            where: {
                id: req.params.id
            },
            include: [{ model: Users }]
        })
            .then(function (Ficha) {
                var doc = fichaReme.pdf(Ficha);
                doc.pipe(res);
                doc.end();
                // req.body
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    }
};
<commit_msg>Update fichas.js<commit_after>// Third Party
var PdfPrinter = require('pdfmake/src/printer');
//PdfModule
var fichaReme = require('../pdf/fichaReme');
// Models
Ficha = require('../models/').Ficha;
Users = require('../models/').User;
// CRUD Operations for Ficha Model
module.exports = {
    index(req, res) {
        Ficha.findAll()
            .then(function (Fichas) {
                res.status(200).json(Fichas);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    show(req, res) {
        Ficha.findAll({
            where: {
                id: req.params.id
            },
            include: [{ model: Users }]
        })
            .then(function (Ficha) {
                res.status(200).json(Ficha);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    create(req, res) {
        // res.status(200).json(req.body);
        Ficha.create(req.body)
            .then(function (newFicha) {
                res.status(200).json(newFicha);
            })
            .catch(function (error) {
                res.status(500).json(error);
            });
    },

    update(req, res) {
        Ficha.update(req.body, {
            where: {
                id: req.body.id
            }
        })
            .then(function (updatedRecords) {
                res.status(200).json(updatedRecords);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    delete(req, res) {
        console.log(req.params)
        Ficha.destroy({
            where: {
                id: req.params.id
            }
        })
            .then(function (deletedRecords) {
                res.status(200).json(deletedRecords);
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    },

    print(req, res) {
        Ficha.findAll({
            where: {
                id: req.params.id
            },
            include: [{ model: Users }]
        })
            .then(function (Ficha) {
                console.log(Ficha);
                var doc = fichaReme.pdf(Ficha);
                doc.pipe(res);
                doc.end();
                // req.body
            })
            .catch(function (error) {
                console.log(error);
                res.status(500).json(error);
            });
    }
};
<|endoftext|>


<commit_before>//Include our time library
var rocky = require('rocky');

//Every minute we update
rocky.on('minutechange', function(event) {
  rocky.requestDraw();
});

rocky.on('draw', function(event) {
  // Get the CanvasRenderingContext2D object
  var ctx = event.context;

  //radius of the circle is based on the hour (smaller as hours is higher)

  //Angle drawn is based on the minute (minute 60 is a complete circle)

});<commit_msg>Adding some more code from the tutorial<commit_after>//This file is started from this: https://developer.pebble.com/tutorials/js-watchface-tutorial/part1/
//Include our time library
var rocky = require('rocky');

function fractionToRadian(fraction) {
  return fraction * 2 * Math.PI;
}

function drawAngle(ctx, cx, cy, angle, length, color){
  // Find the end points
  var x2 = cx + Math.sin(angle) * length;
  var y2 = cy - Math.cos(angle) * length;

  // Configure how we want to draw the hand
  ctx.lineWidth = 8;
  ctx.strokeStyle = color;

  // Begin drawing
  ctx.beginPath();

  // Move to the center point, then draw the line
  ctx.moveTo(cx, cy);
  ctx.lineTo(x2, y2);

  // Stroke the line (output to display)
  ctx.stroke();
}

//Every minute we update
rocky.on('minutechange', function(event) {
  rocky.requestDraw();
});

rocky.on('draw', function(event) {
  // Get the CanvasRenderingContext2D object
  var ctx = event.context;

  var d = new Date();

  // Clear the screen
  //ctx.clearRect(0, 0, ctx.canvas.clientWidth, ctx.canvas.clientHeight);

  // Determine the width and height of the display
  var w = ctx.canvas.unobstructedWidth;
  var h = ctx.canvas.unobstructedHeight;

  // Determine the center point of the display
  // and the max size of watch hands
  var cx = w / 2;
  var cy = h / 2;

  // -20 so we're inset 10px on each side
  var maxLength = (Math.min(w, h) - 20) / 2;

  // Calculate the minute hand angle
  var minuteFraction = (d.getMinutes()) / 60;
  var minuteAngle = fractionToRadian(minuteFraction);

  // Draw the minute hand
  drawHand(ctx, cx, cy, minuteAngle, maxLength, "white");

  // Calculate the hour hand angle
  var hourFraction = (d.getHours() % 12 + minuteFraction) / 12;
  var hourAngle = fractionToRadian(hourFraction);

  // Draw the hour hand
  drawHand(ctx, cx, cy, hourAngle, maxLength * 0.6, "lightblue");
});

  //radius of the circle is based on the hour (smaller as hours is higher)

  //Angle drawn is based on the minute (minute 60 is a complete circle)
<|endoftext|>


<commit_before>import User from '../models/users';
import Api from '../services/api';
import ShareAnalyse from '../models/share-analyse';
import getCacheKey from './helper/cacheKey';

/* ================== router handler ================== */

const toggleShare = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const { enable } = ctx.request.body;
  await ShareAnalyse.changeShareStatus({
    enable,
    url: `github/${githubLogin}`
  });
  const message = Boolean(enable) == true ? "messages.share.toggleOpen" : "messages.share.toggleClose"
  ctx.body = {
    success: true,
    message: ctx.__(message)
  };
};

const getUser = async (ctx, next) => {
  const { githubLogin, githubToken, userId } = ctx.session;
  const user = await Api.getUser(githubLogin, githubToken);
  const login = user.login;
  const shareAnalyse = await ShareAnalyse.findShare({ login, url: `github/${login}` });

  const result = Object.assign({}, user);
  result.openShare = shareAnalyse.enable;
  result.shareUrl = `github/${login}?locale=${ctx.session.locale}`;
  return ctx.body = {
    success: true,
    result
  };
};

const fetchRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserRepos(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch repos success'
  };
};

const fetchCommits = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserCommits(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch commits success'
  };
};

const fetchOrgs = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserOrgs(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch orgs success'
  };
};

const getAllRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { repos } = await Api.getUserRepos(githubLogin, githubToken);
  const result = repos.filter(repository => !repository.fork).map((repository) => {
    const { reposId, name, language, stargazers_count } = repository;
    return {
      name,
      reposId,
      language,
      stargazers_count
    }
  });

  ctx.body = {
    success: true,
    result
  };
};

const getUserRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const { repos } = await Api.getUserRepos(login || githubLogin, githubToken);
  const pinnedRepos = await User.findPinnedRepos(login || githubLogin);
  if (!repos.length) {
    ctx.query.shouldCache = false;
  }
  const results = pinnedRepos.length
    ? repos.filter(repository => pinnedRepos.some(pinned => pinned === repository.reposId))
    : repos;

  ctx.body = {
    success: true,
    result: {
      repos: results
    }
  };
  await next();
};

const getUserCommits = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const { commits } = await Api.getUserCommits(login || githubLogin, githubToken);
  if (!commits.length) {
    ctx.query.shouldCache = false;
  }
  ctx.body = {
    success: true,
    result: {
      commits
    }
  };
  await next();
};

const getUserOrgs = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const orgs = await Api.getUserOrgs(login || githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: {
      orgs
    }
  };
  await next();
};

const getSharedUser = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await Api.getUser(login);
  if (user) {
    ctx.body = {
      success: true,
      result: user
    };
  } else {
    ctx.body = {
      success: true,
      error: ctx.__("messages.error.findUser")
    };
  }
  await next();
};

const checkShareEnable = async (ctx) => {
  const { login } = ctx.params;
  const { githubLogin } = ctx.session;

  const shareAnalyse = await ShareAnalyse.findShare({ login, url: `github/${login}` });
  const user = await Api.getUser(login);

  if (!user || !shareAnalyse.enable) {
    ctx.redirect('/404');
    return;
  }

  return user;
};

const sharePageMobile = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await checkShareEnable(ctx);
  const { githubLogin } = ctx.session;
  const title = ctx.__("sharePage.github", user.name || user.login);

  const {
    bio,
    name,
    created_at,
    avatar_url,
    public_repos,
    followers,
    following
  } = user;

  await ctx.render('user/mobile/share', {
    user: {
      bio,
      name: name || login,
      login,
      avatar_url,
      public_repos,
      followers,
      following,
      isAdmin: login === githubLogin,
      created_at: created_at.split('T')[0]
    },
    title,
    shareText: ctx.__("messages.share.mobileText"),
    joinAt: ctx.__("sharePage.joinAt"),
    menu: {
      shareDatas: ctx.__("mobilePage.menu.shareDatas"),
      githubAnalysis: ctx.__("mobilePage.menu.githubAnalysis"),
      dataRefresh: ctx.__("mobilePage.menu.dataRefresh"),
      logout: ctx.__("mobilePage.menu.logout"),
    }
  });
};

const sharePage = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await checkShareEnable(ctx);
  const { githubLogin } = ctx.session;
  const title = ctx.__("sharePage.github", user.name || user.login);

  await ctx.render('user/share', {
    user: {
      login,
      isAdmin: login === githubLogin
    },
    title,
    shareText: ctx.__("messages.share.text")
  });
};

const getStareRecords = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const url = `github/${githubLogin}`;
  const shareAnalyse = await ShareAnalyse.findShare({ login: githubLogin, url });
  const { viewDevices, viewSources, pageViews, enable } = shareAnalyse;
  ctx.body = {
    success: true,
    result: {
      url: `${url}?locale=${ctx.session.locale}`,
      viewDevices,
      viewSources,
      pageViews,
      openShare: enable
    }
  };
};

const getUpdateTime = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const result = await Api.getUpdateTime(githubLogin);
  return ctx.body = {
    success: true,
    result
  };
};

const refreshRepos = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserRepos(githubLogin, githubToken);
  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('repos', {
      session: ['githubLogin']
    }),
    cacheKey(`sharedUser.${githubLogin}`)
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateRepos"),
    result
  };

  await next();
};

const refreshCommits = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserCommits(githubLogin, githubToken);

  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('commits', {
      session: ['githubLogin']
    })
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateCommits"),
    result
  };

  await next();
};

const refreshOrgs = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserOrgs(githubLogin, githubToken);
  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('orgs', {
      session: ['githubLogin']
    })
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateOrgs"),
    result
  };

  await next();
};

const getZen = async (ctx) => {
  const result = await Api.getZen();
  ctx.body = {
    success: true,
    result
  }
};

const getOctocat = async (ctx) => {
  const result = await Api.getOctocat();
  ctx.body = {
    success: true,
    result
  }
};

export default {
  getUser,
  getSharedUser,
  sharePage,
  sharePageMobile,
  fetchRepos,
  fetchCommits,
  fetchOrgs,
  getAllRepos,
  getUserRepos,
  getUserCommits,
  getUserOrgs,
  toggleShare,
  getStareRecords,
  /* ===== refresh & update ====== */
  getUpdateTime,
  refreshRepos,
  refreshOrgs,
  refreshCommits,
  /* ========== */
  getZen,
  getOctocat
}
<commit_msg>bugfixed<commit_after>import User from '../models/users';
import Api from '../services/api';
import ShareAnalyse from '../models/share-analyse';
import getCacheKey from './helper/cacheKey';

/* ================== router handler ================== */

const toggleShare = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const { enable } = ctx.request.body;
  await ShareAnalyse.changeShareStatus({
    enable,
    url: `github/${githubLogin}`
  });
  const message = Boolean(enable) == true ? "messages.share.toggleOpen" : "messages.share.toggleClose"
  ctx.body = {
    success: true,
    message: ctx.__(message)
  };
};

const getUser = async (ctx, next) => {
  const { githubLogin, githubToken, userId } = ctx.session;
  const user = await Api.getUser(githubLogin, githubToken);
  const login = user.login;
  const shareAnalyse = await ShareAnalyse.findShare({ login, url: `github/${login}` });

  const result = Object.assign({}, user);
  result.openShare = shareAnalyse.enable;
  result.shareUrl = `github/${login}?locale=${ctx.session.locale}`;
  return ctx.body = {
    success: true,
    result
  };
};

const fetchRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserRepos(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch repos success'
  };
};

const fetchCommits = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserCommits(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch commits success'
  };
};

const fetchOrgs = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  await Api.getUserOrgs(githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: 'fetch orgs success'
  };
};

const getAllRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { repos } = await Api.getUserRepos(githubLogin, githubToken);
  const result = repos.filter(repository => !repository.fork).map((repository) => {
    const { reposId, name, language, stargazers_count } = repository;
    return {
      name,
      reposId,
      language,
      stargazers_count
    }
  });

  ctx.body = {
    success: true,
    result
  };
};

const getUserRepos = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const { repos } = await Api.getUserRepos(login || githubLogin, githubToken);
  const pinned = await User.findPinnedRepos(login || githubLogin);
  if (!repos.length) {
    ctx.query.shouldCache = false;
  }
  const checkPinned = (repository) => pinned.some(item => item === repository.reposId);
  const pinnedRepos = pinned.length
    ? repos.filter(repository => checkPinned(repository) || repository.fork)
    : repos;

  ctx.body = {
    success: true,
    result: {
      repos: pinnedRepos
    }
  };
  await next();
};

const getUserCommits = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const { commits } = await Api.getUserCommits(login || githubLogin, githubToken);
  if (!commits.length) {
    ctx.query.shouldCache = false;
  }
  ctx.body = {
    success: true,
    result: {
      commits
    }
  };
  await next();
};

const getUserOrgs = async (ctx, next) => {
  const { githubLogin, githubToken } = ctx.session;
  const { login } = ctx.query;
  const orgs = await Api.getUserOrgs(login || githubLogin, githubToken);
  ctx.body = {
    success: true,
    result: {
      orgs
    }
  };
  await next();
};

const getSharedUser = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await Api.getUser(login);
  if (user) {
    ctx.body = {
      success: true,
      result: user
    };
  } else {
    ctx.body = {
      success: true,
      error: ctx.__("messages.error.findUser")
    };
  }
  await next();
};

const checkShareEnable = async (ctx) => {
  const { login } = ctx.params;
  const { githubLogin } = ctx.session;

  const shareAnalyse = await ShareAnalyse.findShare({ login, url: `github/${login}` });
  const user = await Api.getUser(login);

  if (!user || !shareAnalyse.enable) {
    ctx.redirect('/404');
    return;
  }

  return user;
};

const sharePageMobile = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await checkShareEnable(ctx);
  const { githubLogin } = ctx.session;
  const title = ctx.__("sharePage.github", user.name || user.login);

  const {
    bio,
    name,
    created_at,
    avatar_url,
    public_repos,
    followers,
    following
  } = user;

  await ctx.render('user/mobile/share', {
    user: {
      bio,
      name: name || login,
      login,
      avatar_url,
      public_repos,
      followers,
      following,
      isAdmin: login === githubLogin,
      created_at: created_at.split('T')[0]
    },
    title,
    shareText: ctx.__("messages.share.mobileText"),
    joinAt: ctx.__("sharePage.joinAt"),
    menu: {
      shareDatas: ctx.__("mobilePage.menu.shareDatas"),
      githubAnalysis: ctx.__("mobilePage.menu.githubAnalysis"),
      dataRefresh: ctx.__("mobilePage.menu.dataRefresh"),
      logout: ctx.__("mobilePage.menu.logout"),
    }
  });
};

const sharePage = async (ctx, next) => {
  const { login } = ctx.params;
  const user = await checkShareEnable(ctx);
  const { githubLogin } = ctx.session;
  const title = ctx.__("sharePage.github", user.name || user.login);

  await ctx.render('user/share', {
    user: {
      login,
      isAdmin: login === githubLogin
    },
    title,
    shareText: ctx.__("messages.share.text")
  });
};

const getStareRecords = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const url = `github/${githubLogin}`;
  const shareAnalyse = await ShareAnalyse.findShare({ login: githubLogin, url });
  const { viewDevices, viewSources, pageViews, enable } = shareAnalyse;
  ctx.body = {
    success: true,
    result: {
      url: `${url}?locale=${ctx.session.locale}`,
      viewDevices,
      viewSources,
      pageViews,
      openShare: enable
    }
  };
};

const getUpdateTime = async (ctx, next) => {
  const { githubLogin } = ctx.session;
  const result = await Api.getUpdateTime(githubLogin);
  return ctx.body = {
    success: true,
    result
  };
};

const refreshRepos = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserRepos(githubLogin, githubToken);
  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('repos', {
      session: ['githubLogin']
    }),
    cacheKey(`sharedUser.${githubLogin}`)
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateRepos"),
    result
  };

  await next();
};

const refreshCommits = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserCommits(githubLogin, githubToken);

  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('commits', {
      session: ['githubLogin']
    })
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateCommits"),
    result
  };

  await next();
};

const refreshOrgs = async (ctx, next) => {
  const { githubToken, githubLogin } = ctx.session;
  const result = await Api.refreshUserOrgs(githubLogin, githubToken);
  if (result === false) {
    return ctx.body = {
      success: true,
      error: ctx.__("messages.error.frequent"),
      result: null
    };
  }

  // set cache keys to remove
  const cacheKey = getCacheKey(ctx);
  ctx.query.deleteKeys = [
    cacheKey('orgs', {
      session: ['githubLogin']
    })
  ];

  ctx.body = {
    success: true,
    message: ctx.__("messages.success.updateOrgs"),
    result
  };

  await next();
};

const getZen = async (ctx) => {
  const result = await Api.getZen();
  ctx.body = {
    success: true,
    result
  }
};

const getOctocat = async (ctx) => {
  const result = await Api.getOctocat();
  ctx.body = {
    success: true,
    result
  }
};

export default {
  getUser,
  getSharedUser,
  sharePage,
  sharePageMobile,
  fetchRepos,
  fetchCommits,
  fetchOrgs,
  getAllRepos,
  getUserRepos,
  getUserCommits,
  getUserOrgs,
  toggleShare,
  getStareRecords,
  /* ===== refresh & update ====== */
  getUpdateTime,
  refreshRepos,
  refreshOrgs,
  refreshCommits,
  /* ========== */
  getZen,
  getOctocat
}
<|endoftext|>


<commit_before>/**
 * Dependencies.
 */
var _ = require('lodash');
var async = require('async');
var utils = require('../lib/utils');

/**
 * Controller.
 */
module.exports = function(app) {

  /**
   * Internal Dependencies.
   */
  var errors = app.errors;
  var models = app.set('models');
  var sequelize = models.sequelize;
  var Group = models.Group;
  var Activity = models.Activity;
  var User = models.User;
  var Transaction = models.Transaction;
  var UserGroup = models.UserGroup;
  var StripeManagedAccount = models.StripeManagedAccount;
  var transactions = require('../controllers/transactions')(app);

  /**
   * Private methods.
   */
  var addGroupMember = function(group, user, options, callback) {
    group
      .addMember(user, {role: options.role})
      .then(function(usergroup) {
        callback();

        // Create activities.
        var activity = {
          type: 'group.user.added',
          GroupId: group.id,
          data: {
            group: group.info,
            user: options.remoteUser.info,
            target: user.info,
            usergroup: usergroup.info
          }
        };
        Activity.create(_.extend({UserId: options.remoteUser.id}, activity));
        if (user.id !== options.remoteUser.id)
          Activity.create(_.extend({UserId: user.id}, activity));
      })
      .catch(callback);
  };

  var getBalance = function(id, cb) {
    Transaction
      .find({
        attributes: [
          [sequelize.fn('SUM', sequelize.col('amount')), 'total']
        ],
        where: {
          GroupId: id,
          approved: true
        }
      })
      .then(function(result) {
        cb(null, result.toJSON().total);
      })
      .catch(cb);
  };

  var getUsers = function(req, res, next) {

    /**
     * Doing this in SQL because could not find how to do it with sequelize
     */
    var sql = 'SELECT * FROM "UserGroups" ' +
              'LEFT JOIN "Users" ' +
              'ON "UserId" = "id" ' +
              'WHERE "GroupId" = :GroupId';

    sequelize.query(sql, {
      replacements: {
        GroupId: req.group.id
      },

      // we could pass the model but it only works with sequelize >= 3.0.0
      type: sequelize.QueryTypes.SELECT
    })
    .map(function createUserInstance(userData) {
      return User.build(userData).public; // only return public data
    })
    .then(res.send.bind(res))
    .catch(next);
  };

  /**
   * Public methods.
   */
  return {

    /**
     * Create a group.
     */
    create: function(req, res, next) {
      Group
        .create(req.required.group)
        .then(function(group) {

          // Create activity.
          Activity.create({
            type: 'group.created',
            UserId: req.remoteUser.id,
            GroupId: group.id,
            data: {
              group: group.info,
              user: req.remoteUser.info
            }
          });

          async.series({
            addMember: function(cb) {
              // Add caller to the group if `role` specified.
              var role = req.body.role;
              if (!role)
                return cb();
              var options = {
                role: role,
                remoteUser: req.remoteUser
              };
              addGroupMember(group, req.remoteUser, options, cb);
            },
            createStripeManagedAccount: function(cb) {
              app.stripe.accounts.create({
                email: req.required.stripeEmail
              }, function(e, account) {
                if (e) return cb(e);

                StripeManagedAccount
                  .create({
                    stripeId: account.id,
                    stripeSecret: account.keys.secret,
                    stripeKey: account.keys.publishable,
                    stripeEmail: account.email
                  })
                  .then(function(account) {
                    account.addGroup(group.id).done(cb);
                  })
                  .catch(cb);
              });
            }
          }, function(e) {
            if (e) return next(e);
            res.send(group.info);
          });

        })
        .catch(next);
    },

    /**
     * Update.
     */
    update: function(req, res, next) {
      ['name',
       'description',
       'budget',
       'currency',
       'longDescription',
       'logo',
       'video',
       'image',
       'expensePolicy',
       'membershipType',
       'membershipfee',
       'isPublic'].forEach(function(prop) {
        if (req.required.group[prop])
          req.group[prop] = req.required.group[prop];
      });
      req.group.updatedAt = new Date();

      req.group
        .save()
        .then(function(group) {
          res.send(group.info);
        })
        .catch(next);
    },

    /**
     * Get group content.
     */
    get: function(req, res, next) {

      async.auto({

        getBalance: getBalance.bind(this, req.group.id),

        getActivities: function(cb) {
          if (!req.query.activities && !req.body.activities)
            return cb();

          var query = {
            where: {
              GroupId: req.group.id
            },
            order: [['createdAt', 'DESC']],
            offset: 0,
            limit: 20 // [TODO] I need to put this default value
            // as a global parameter. Using mw.paginate?
          };

          Activity
            .findAndCountAll(query)
            .then(function(activities) {
              cb(null, activities.rows);
            })
            .catch(cb);
        },

        getStripeManagedAccount: function(cb) {
          req.group.getStripeManagedAccount()
            .done(cb);
        }

      }, function(e, results) {
        if (e) return next(e);

        var group = req.group.info;
        group.balance = results.getBalance;

        if (results.getActivities) {
          group.activities = results.getActivities;
        }

        if (results.getStripeManagedAccount) {
          group.stripeManagedAccount = _.pick(results.getStripeManagedAccount,
                                              'stripeKey');
        }

        res.send(group);
      });

    },

    /**
     * Add a user to a group.
     */
    addMember: function(req, res, next) {
      var options = {
        role: req.body.role || 'viewer',
        remoteUser: req.remoteUser
      };
      addGroupMember(req.group, req.user, options, function(e) {
        if (e) return next(e);
        else res.send({success: true});
      });
    },

    /**
     * Update a member.
     */
    updateMember: function(req, res, next) {
      var query = {
        where: {
          GroupId: req.group.id,
          UserId: req.user.id
        }
      };

      models
        .UserGroup
        .findOne(query)
        .then(function(usergroup) {
          if (!usergroup) {
            throw (new errors.NotFound('The user is not part of the group yet.'));
          }

          return usergroup;
        })
        .then(function(usergroup) {
          ['role'].forEach(function(prop) {
            if (req.body[prop])
              usergroup[prop] = req.body[prop];
          });
          usergroup.updatedAt = new Date();

          return usergroup
            .save();
        })
        .then(function(usergroup) {
          // Create activities.
          var remoteUser = (req.remoteUser && req.remoteUser.info) || (req.application && req.application.info);
          var activity = {
            type: 'group.user.updated',
            GroupId: req.group.id,
            data: {
              group: req.group.info,
              user: remoteUser,
              target: req.user.info,
              usergroup: usergroup.info
            }
          };
          Activity.create(_.extend({UserId: req.user.id}, activity));
          if (req.remoteUser && req.user.id !== req.remoteUser.id)
            Activity.create(_.extend({UserId: req.remoteUser.id}, activity));

          return usergroup;
        })
        .then(function(usergroup) {
          res.send(usergroup);
        })
        .catch(next);
    },

    /**
     * Delete a member.
     */
    deleteMember: function(req, res, next) {
      var query = {
        where: {
          GroupId: req.group.id,
          UserId: req.user.id
        }
      };

      models
        .UserGroup
        .findOne(query)
        .then(function(usergroup) {
          if (!usergroup) {
            throw (new errors.NotFound('The user is not part of the group yet.'));
          }

          return usergroup;
        })
        .then(function(usergroup) {
          return usergroup.destroy();
        })
        .then(function() {
          // Create activities.
          var remoteUser = (req.remoteUser && req.remoteUser.info) || (req.application && req.application.info);
          var activity = {
            type: 'group.user.deleted',
            GroupId: req.group.id,
            data: {
              group: req.group.info,
              user: remoteUser,
              target: req.user.info
            }
          };
          Activity.create(_.extend({UserId: req.user.id}, activity));
          if (req.remoteUser && req.user.id !== req.remoteUser.id)
            Activity.create(_.extend({UserId: req.remoteUser.id}, activity));
          return;
        })
        .then(function() {
          res.send({success: true});
        })
        .catch(next);
    },

    /**
     * Create a transaction and add it to a group.
     */
    createTransaction: function(req, res, next) {
      var transaction = req.required.transaction;
      var group = req.group;

      // Caller.
      var user = req.remoteUser || transaction.user || {};

      var t = {
        transaction: transaction,
        group: group,
        user: user
      };
      transactions._create(t, function(e, transactionCreated) {
        if (e) return next(e);
        res.send(transactionCreated);
      });

    },

    /**
     * Delete a transaction.
     */
    deleteTransaction: function(req, res, next) {
       var transaction = req.transaction;
       var group = req.group;
       var user = req.remoteUser || {};

       async.auto({

         deleteTransaction: function(cb) {
           transaction
             .destroy()
             .done(cb);
         },

         createActivity: ['deleteTransaction', function(cb) {
           Activity.create({
             type: 'group.transaction.deleted',
             UserId: user.id,
             GroupId: group.id,
             data: {
               group: group.info,
               transaction: transaction,
               user: user.info
             }
           }).done(cb);
         }]

       }, function(e) {
         if (e) return next(e);
         res.send({success: true});
       });

     },

    /**
     * Get group's transactions.
     */
    getTransactions: function(req, res, next) {
      var query = _.merge({
        where: {
          GroupId: req.group.id
        },
        order: [[req.sorting.key, req.sorting.dir]]
      }, req.pagination);

      Transaction
        .findAndCountAll(query)
        .then(function(transactions) {

          // Set headers for pagination.
          req.pagination.total = transactions.count;
          res.set({
            Link: utils.getLinkHeader(utils.getRequestedUrl(req),
                                        req.pagination)
          });

          res.send(_.pluck(transactions.rows, 'info'));
        })
        .catch(next);
    },

    /**
     * Get a group's transaction.
     */
    getTransaction: function(req, res, next) {
      res.json(req.transaction.info);
    },

    /**
     * Get the balance of a group
     * Also used in users controller
     */
    getBalance: getBalance,

    /**
     * Get users of a group
     */
    getUsers: getUsers
  };

};
<commit_msg>add public page info/<commit_after>/**
 * Dependencies.
 */
var _ = require('lodash');
var async = require('async');
var utils = require('../lib/utils');

/**
 * Controller.
 */
module.exports = function(app) {

  /**
   * Internal Dependencies.
   */
  var errors = app.errors;
  var models = app.set('models');
  var sequelize = models.sequelize;
  var Group = models.Group;
  var Activity = models.Activity;
  var User = models.User;
  var Transaction = models.Transaction;
  var UserGroup = models.UserGroup;
  var StripeManagedAccount = models.StripeManagedAccount;
  var transactions = require('../controllers/transactions')(app);

  /**
   * Private methods.
   */
  var addGroupMember = function(group, user, options, callback) {
    group
      .addMember(user, {role: options.role})
      .then(function(usergroup) {
        callback();

        // Create activities.
        var activity = {
          type: 'group.user.added',
          GroupId: group.id,
          data: {
            group: group.info,
            user: options.remoteUser.info,
            target: user.info,
            usergroup: usergroup.info
          }
        };
        Activity.create(_.extend({UserId: options.remoteUser.id}, activity));
        if (user.id !== options.remoteUser.id)
          Activity.create(_.extend({UserId: user.id}, activity));
      })
      .catch(callback);
  };

  var getBalance = function(id, cb) {
    Transaction
      .find({
        attributes: [
          [sequelize.fn('SUM', sequelize.col('amount')), 'total']
        ],
        where: {
          GroupId: id,
          approved: true
        }
      })
      .then(function(result) {
        cb(null, result.toJSON().total);
      })
      .catch(cb);
  };

  var getPublicPageInfo = function(id, cb) {
    Transaction
      .find({
        attributes: [
          [sequelize.fn('SUM', sequelize.col('amount')), 'donationTotal'],
          [sequelize.fn('COUNT', sequelize.col('UserId')), 'backersCount']
        ],
        where: {
          GroupId: id,
          approved: true,
          amount: {
            $gt: 0
          }
        }
      })
      .then(function(result) {
        var json = result.toJSON();

        cb(null, {
          donationTotal: Number(json.donationTotal),
          backersCount: Number(json.backersCount)
        });
      })
      .catch(cb);
  };

  var getUsers = function(req, res, next) {

    /**
     * Doing this in SQL because could not find how to do it with sequelize
     */
    var sql = 'SELECT * FROM "UserGroups" ' +
              'LEFT JOIN "Users" ' +
              'ON "UserId" = "id" ' +
              'WHERE "GroupId" = :GroupId';

    sequelize.query(sql, {
      replacements: {
        GroupId: req.group.id
      },

      // we could pass the model but it only works with sequelize >= 3.0.0
      type: sequelize.QueryTypes.SELECT
    })
    .map(function createUserInstance(userData) {
      return User.build(userData).public; // only return public data
    })
    .then(res.send.bind(res))
    .catch(next);
  };

  /**
   * Public methods.
   */
  return {

    /**
     * Create a group.
     */
    create: function(req, res, next) {
      Group
        .create(req.required.group)
        .then(function(group) {

          // Create activity.
          Activity.create({
            type: 'group.created',
            UserId: req.remoteUser.id,
            GroupId: group.id,
            data: {
              group: group.info,
              user: req.remoteUser.info
            }
          });

          async.series({
            addMember: function(cb) {
              // Add caller to the group if `role` specified.
              var role = req.body.role;
              if (!role)
                return cb();
              var options = {
                role: role,
                remoteUser: req.remoteUser
              };
              addGroupMember(group, req.remoteUser, options, cb);
            },
            createStripeManagedAccount: function(cb) {
              app.stripe.accounts.create({
                email: req.required.stripeEmail
              }, function(e, account) {
                if (e) return cb(e);

                StripeManagedAccount
                  .create({
                    stripeId: account.id,
                    stripeSecret: account.keys.secret,
                    stripeKey: account.keys.publishable,
                    stripeEmail: account.email
                  })
                  .then(function(account) {
                    account.addGroup(group.id).done(cb);
                  })
                  .catch(cb);
              });
            }
          }, function(e) {
            if (e) return next(e);
            res.send(group.info);
          });

        })
        .catch(next);
    },

    /**
     * Update.
     */
    update: function(req, res, next) {
      ['name',
       'description',
       'budget',
       'currency',
       'longDescription',
       'logo',
       'video',
       'image',
       'expensePolicy',
       'membershipType',
       'membershipfee',
       'isPublic'].forEach(function(prop) {
        if (req.required.group[prop])
          req.group[prop] = req.required.group[prop];
      });
      req.group.updatedAt = new Date();

      req.group
        .save()
        .then(function(group) {
          res.send(group.info);
        })
        .catch(next);
    },

    /**
     * Get group content.
     */
    get: function(req, res, next) {

      async.auto({

        getBalance: getBalance.bind(this, req.group.id),
        getPublicPageInfo: getPublicPageInfo.bind(this, req.group.id),

        getActivities: function(cb) {
          if (!req.query.activities && !req.body.activities)
            return cb();

          var query = {
            where: {
              GroupId: req.group.id
            },
            order: [['createdAt', 'DESC']],
            offset: 0,
            limit: 20 // [TODO] I need to put this default value
            // as a global parameter. Using mw.paginate?
          };

          Activity
            .findAndCountAll(query)
            .then(function(activities) {
              cb(null, activities.rows);
            })
            .catch(cb);
        },

        getStripeManagedAccount: function(cb) {
          req.group.getStripeManagedAccount()
            .done(cb);
        }

      }, function(e, results) {
        if (e) return next(e);

        var group = req.group.info;
        group.balance = results.getBalance;
        group.backersCount = results.getPublicPageInfo.backersCount;
        group.donationTotal = results.getPublicPageInfo.donationTotal;

        if (results.getActivities) {
          group.activities = results.getActivities;
        }

        if (results.getStripeManagedAccount) {
          group.stripeManagedAccount = _.pick(results.getStripeManagedAccount,
                                              'stripeKey');
        }

        res.send(group);
      });

    },

    /**
     * Add a user to a group.
     */
    addMember: function(req, res, next) {
      var options = {
        role: req.body.role || 'viewer',
        remoteUser: req.remoteUser
      };
      addGroupMember(req.group, req.user, options, function(e) {
        if (e) return next(e);
        else res.send({success: true});
      });
    },

    /**
     * Update a member.
     */
    updateMember: function(req, res, next) {
      var query = {
        where: {
          GroupId: req.group.id,
          UserId: req.user.id
        }
      };

      models
        .UserGroup
        .findOne(query)
        .then(function(usergroup) {
          if (!usergroup) {
            throw (new errors.NotFound('The user is not part of the group yet.'));
          }

          return usergroup;
        })
        .then(function(usergroup) {
          ['role'].forEach(function(prop) {
            if (req.body[prop])
              usergroup[prop] = req.body[prop];
          });
          usergroup.updatedAt = new Date();

          return usergroup
            .save();
        })
        .then(function(usergroup) {
          // Create activities.
          var remoteUser = (req.remoteUser && req.remoteUser.info) || (req.application && req.application.info);
          var activity = {
            type: 'group.user.updated',
            GroupId: req.group.id,
            data: {
              group: req.group.info,
              user: remoteUser,
              target: req.user.info,
              usergroup: usergroup.info
            }
          };
          Activity.create(_.extend({UserId: req.user.id}, activity));
          if (req.remoteUser && req.user.id !== req.remoteUser.id)
            Activity.create(_.extend({UserId: req.remoteUser.id}, activity));

          return usergroup;
        })
        .then(function(usergroup) {
          res.send(usergroup);
        })
        .catch(next);
    },

    /**
     * Delete a member.
     */
    deleteMember: function(req, res, next) {
      var query = {
        where: {
          GroupId: req.group.id,
          UserId: req.user.id
        }
      };

      models
        .UserGroup
        .findOne(query)
        .then(function(usergroup) {
          if (!usergroup) {
            throw (new errors.NotFound('The user is not part of the group yet.'));
          }

          return usergroup;
        })
        .then(function(usergroup) {
          return usergroup.destroy();
        })
        .then(function() {
          // Create activities.
          var remoteUser = (req.remoteUser && req.remoteUser.info) || (req.application && req.application.info);
          var activity = {
            type: 'group.user.deleted',
            GroupId: req.group.id,
            data: {
              group: req.group.info,
              user: remoteUser,
              target: req.user.info
            }
          };
          Activity.create(_.extend({UserId: req.user.id}, activity));
          if (req.remoteUser && req.user.id !== req.remoteUser.id)
            Activity.create(_.extend({UserId: req.remoteUser.id}, activity));
          return;
        })
        .then(function() {
          res.send({success: true});
        })
        .catch(next);
    },

    /**
     * Create a transaction and add it to a group.
     */
    createTransaction: function(req, res, next) {
      var transaction = req.required.transaction;
      var group = req.group;

      // Caller.
      var user = req.remoteUser || transaction.user || {};

      var t = {
        transaction: transaction,
        group: group,
        user: user
      };
      transactions._create(t, function(e, transactionCreated) {
        if (e) return next(e);
        res.send(transactionCreated);
      });

    },

    /**
     * Delete a transaction.
     */
    deleteTransaction: function(req, res, next) {
       var transaction = req.transaction;
       var group = req.group;
       var user = req.remoteUser || {};

       async.auto({

         deleteTransaction: function(cb) {
           transaction
             .destroy()
             .done(cb);
         },

         createActivity: ['deleteTransaction', function(cb) {
           Activity.create({
             type: 'group.transaction.deleted',
             UserId: user.id,
             GroupId: group.id,
             data: {
               group: group.info,
               transaction: transaction,
               user: user.info
             }
           }).done(cb);
         }]

       }, function(e) {
         if (e) return next(e);
         res.send({success: true});
       });

     },

    /**
     * Get group's transactions.
     */
    getTransactions: function(req, res, next) {
      var query = _.merge({
        where: {
          GroupId: req.group.id
        },
        order: [[req.sorting.key, req.sorting.dir]]
      }, req.pagination);

      Transaction
        .findAndCountAll(query)
        .then(function(transactions) {

          // Set headers for pagination.
          req.pagination.total = transactions.count;
          res.set({
            Link: utils.getLinkHeader(utils.getRequestedUrl(req),
                                        req.pagination)
          });

          res.send(_.pluck(transactions.rows, 'info'));
        })
        .catch(next);
    },

    /**
     * Get a group's transaction.
     */
    getTransaction: function(req, res, next) {
      res.json(req.transaction.info);
    },

    /**
     * Get the balance of a group
     * Also used in users controller
     */
    getBalance: getBalance,

    /**
     * Get users of a group
     */
    getUsers: getUsers
  };

};
<|endoftext|>


<commit_before>var _ = require("underscore");
var assert = require("assert");
var Backbone = require("backbone");
var ComputedAttributeMixin = require("../backbone.computed.js");

suite("Backbone computed attributes", function() {
  suite("Basic Tests", function() {
    var TestModel;
    setup(function() {
      TestModel = Backbone.Model.extend({
        initialize: function() {
          this.createComputedAttribute({
            attr: "Z",
            get: function() {
              return this.get("X") + this.get("Y");
            },
            bindings: [
              { model: this, attribute: "X" },
              { model: this, attribute: "Y" }
            ]
          });
        },
        defaults: {
          "X": 1,
          "Y": 2
        }
      });
      _.extend(TestModel.prototype, ComputedAttributeMixin);
    });

    test("initialize and get computed attribute", function() {
      var test = new TestModel();
      assert.equal(test.get("Z"), 3);
    });
  });
});

/*

    test("initialize and get computed attribute", 1, function()
    {
        var test = new TestModel();
        equal(test.get("Z"), 3);
    });

    test("set dependency and get computed attribute", 1, function()
    {
        var test = new TestModel();
        test.set("X", 2);
        equal(test.get("Z"), 4);
    });

    test("bind to computed attribute and set dependency", 1, function()
    {
        var test = new TestModel();
        test.on("change:Z", function() { equal(test.get("Z"), 5); });
        test.set("X", 3);
    });

    test("computed attribute only fires one change event when multiple dependencies change", 1, function()
    {
        var test = new TestModel();
        test.on("change:Z", function() { equal(test.get("Z"), 9); });
        test.set({ "X": 4, "Y": 5 });
    });

    test("computed attribute does not fire change event when dependency is set to an unchanged value", 0, function()
    {
        var test = new TestModel();
        test.on("change:Z", function() { ok(true); });
        test.set("X", 1);
    });

    test("computed attribute does not fire change event when dependencies are changed but computed value remains the same", 1, function()
    {
        var counter = 0;
        var test = new TestModel();
        test.get("Z"); // Caches it once. I don't know if this should happen first automatically.
        test.on("change:Z", function() { counter++; });
        test.set({ X: 2, Y: 1 });
        equal(counter, 0);
    });

    test("computed attribute does not fire multiple change events when dependencies are changed within an atomic block", 1, function() {
        var counter = 0;
        var test = new TestModel();
        test.on("change:Z", function() { counter++; });
        Backbone.atomic(function() {
            test.set("X", 4);
            test.set("Y", 5);
        });
        equal(counter, 1);
    });

    // Tests that bind to different models.

    var ModelWithDependencies = Backbone.Model.extend({

        initialize: function()
        {
            this.child1 = new Backbone.Model();
            this.child1.set("A", 2);

            this.child2 = new Backbone.Model();
            this.child2.set("B", 1);

            this.createComputedAttribute({
                attr: "C",
                get: function()
                {
                    return this.child1.get("A") + this.child2.get("B");
                },
                bindings: [{ model: this.child1, attribute: "A" }, { model: this.child2, attribute: "B"}]
            });
        }
    });
    _.extend(ModelWithDependencies.prototype, Backbone.ComputedAttributeMixin);

    test("initialize and get computed attribute that uses a different model as a dependency", 1, function()
    {
        var test = new ModelWithDependencies();
        equal(test.get("C"), 3);
    });

    test("bind to computed attribute and set attribute on depended-on model", 1, function()
    {
        var test = new ModelWithDependencies();
        test.on("change:C", function() { equal(test.get("C"), 5); });
        test.child1.set("A", 4);
    });

    test("listening to change when the value of a computed attribute changes passes the model correctly to listeners", 1, function()
    {
        var test = new ModelWithDependencies();
        test.on('change', function(model) { equal(model, test); });
        test.child1.set("A", 4);
    });

    test("bind to non-specific change and set attribute on depended-on model", 1, function()
    {
        var test = new ModelWithDependencies();
        test.on("change", function() { equal(test.get("C"), 5); });
        test.child1.set("A", 4);
    });

    // A different variation on bindings.

    var ModelWithDifferentBindings = Backbone.Model.extend({

        initialize: function()
        {
            this.child1 = new Backbone.Model();
            this.child1.set({ A: "A", B: "B" });

            this.createComputedAttribute({
                attr: "C",
                get: function()
                {
                    return this.child1.get("A") + this.child1.get("B");
                },
                bindings: [{ model: this.child1, attributes: ["A", "B"]}]
            });
        }
    });
    _.extend(ModelWithDifferentBindings.prototype, Backbone.ComputedAttributeMixin);

    test("initialize and get computed attribute with an attribute array binding", 1, function()
    {
        var test = new ModelWithDifferentBindings();
        equal(test.get("C"), "AB");
    });

    test("change first attribute within an attribute array binding", 1, function()
    {
        var test = new ModelWithDifferentBindings();
        test.on("change:C", function() { equal(test.get("C"), "dB"); });
        test.child1.set("A", "d");
    });

    test("change second attribute within an attribute array binding", 1, function()
    {
        var test = new ModelWithDifferentBindings();
        test.on("change:C", function() { equal(test.get("C"), "Ad"); });
        test.child1.set("B", "d");
    });

    // This might not be considered "expected" behavior.
    test("non-specific change will fire even if there is no change if computed attribute has not yet been cached", 1, function()
    {
        var test = new ModelWithDifferentBindings();
        test.on("change", function() { ok(true); });
        test.child1.set({ "A": "AB", "B": "" });
    });

    test("no non-specific change will fire if cached computed attribute is unchanged", 0, function()
    {
        var test = new ModelWithDifferentBindings();
        // Have to call the getter once, just to cache it.
        test.get("C");
        test.on("change", function() { ok(false); });
        test.child1.set({ "A": "AB", "B": "" });
    });

    test("events will not be triggered on a destroyed model", 0, function()
    {
        var test = new ModelWithDifferentBindings();
        var c1 = test.child1;
        test.on("change:C", function() { ok(false); });
        test.destroy();
        c1.set("B", "d");
    });

    test("lots of sets, only one change", 1, function()
    {
        var test = new ModelWithDifferentBindings();
        test.on("change:C", function() { equal(test.get("C"), "de"); });
        test.child1.set({ "A": "d", "B": "e" });
        test.child1.set("B", "e");
    });

    // Tests multi level dependencies. The order of these computed properties is important, because
    // it puts some stress on how the attributes are cached.

    var MultiLevelDependencyModel = Backbone.Model.extend({

        initialize: function()
        {
            this.child1 = new TestModel();

            this.createComputedAttribute({
                attr: "C",
                get: function()
                {
                    return this.child1.get("Z") + "c";
                },
                bindings: [{ model: this.child1, attribute: "Z"}]
            });

            this.createComputedAttribute({
                attr: "E",
                get: function()
                {
                    return this.child1.get("X") + "e";
                },
                bindings: [{ model: this.child1, attribute: "X"}]
            });

            this.createComputedAttribute({
                attr: "D",
                get: function()
                {
                    return this.child1.get("Y") + this.get("E") + "d";
                },
                bindings: [{ model: this.child1, attribute: "Y"}, "E"]
            });

            this.createComputedAttribute({
                attr: "F",
                get: function()
                {
                    return this.child1.get("Z") + this.get("D") + "f";
                },
                bindings: [{ model: this.child1, attribute: "Z"}, "D"]
            });
        }
    });
    _.extend(MultiLevelDependencyModel.prototype, Backbone.ComputedAttributeMixin);

    test("get computed attribute with a multi-level dependency", 1, function()
    {
        var test = new MultiLevelDependencyModel();
        equal(test.get("C"), "3c");
    });

    test("change the root attribute of a multi-level dependency", 1, function()
    {
        var test = new MultiLevelDependencyModel();
        test.on("change:C", function() { equal(test.get("C"), "4c"); });
        test.child1.set("X", 2);
    });

    test("change event is fired on middle tier of multi-level dependency", 1, function()
    {
        var test = new MultiLevelDependencyModel();
        test.on("change:D", function() { equal(test.get("D"), "yxed"); });
        test.child1.set({ Y : "y", X : "x" });
    });

    // Additional tests for caching computed attributes.

    var NoCacheModel = Backbone.Model.extend({

        initialize: function()
        {
            this.child1 = new Backbone.Model({ Z : 1, Y : 2 });

            this.createComputedAttribute({
                attr: "cached",
                get: function()
                {
                    return { z: this.child1.get("Z"), y : this.child1.get("Y") };
                },
                bindings: [{ model: this.child1, attributes: ["Y", "Z"]}]
            });

            this.createComputedAttribute({
                attr: "uncached",
                get: function()
                {
                    return { z: this.child1.get("Z"), y : this.child1.get("Y") };
                },
                bindings: [{ model: this.child1, attributes: ["Y", "Z"]}],
                nocache: true
            });
        }
    });
    _.extend(NoCacheModel.prototype, Backbone.ComputedAttributeMixin);

    test("if a cached object is modified, the modified object will be returned by the getter", 1, function()
    {
        var test = new NoCacheModel();
        var obj = test.get("cached");
        obj.z = 3;
        var obj2 = test.get("cached");
        equal(obj2.z, 3);
    });

    test("if an uncached object is modified, a new object will be returned by the getter", 1, function()
    {
        var test = new NoCacheModel();
        var obj = test.get("uncached");
        obj.z = 3;
        var obj2 = test.get("uncached");
        equal(obj2.z, 1);
    });

    // Tests for different interplays between setting values in event handlers while also using computed attributes.

    var MixedAttributeAndEventModel = Backbone.Model.extend({

        initialize: function()
        {
            this.child1 = new Backbone.Model({ X : "x", Y : "y", Z : "z" });

            this.createComputedAttribute({
                attr: "A",
                get: function()
                {
                    return this.child1.get("X") + this.child1.get("Y");
                },
                bindings: [{ model: this.child1, attributes: ["X", "Y"]}]
            });

            this.createComputedAttribute({
                attr: "V",
                get: function()
                {
                    return this.child1.get("Z");
                },
                bindings: [{ model: this.child1, attributes: ["Z"]}]
            });

            this.on("change:A", function()
            {
                this.child1.set("Z", "w");
            }, this);
        }
    });
    _.extend(MixedAttributeAndEventModel.prototype, Backbone.ComputedAttributeMixin);

    test("setting a different depended-on attribute when a computed attribute changes can recompute other attributes", 1, function()
    {
        var test = new MixedAttributeAndEventModel();
        test.on("change:V", function() { equal(test.get("V"), "w"); });
        test.child1.set("X", "v");
    });

    // Wacky test to handle how caching attributes is affected when multiple models are being updated.

    test("all computed properties change events despite weird dependency order", 2, function()
    {
        var m1 = new Backbone.Model({ A : "a", B : "b" });

        var M2 = Backbone.Model.extend({
            initialize: function()
            {
                this.createComputedAttribute({
                    attr: "C",
                    get: function()
                    {
                        return m1.get("A") + "c";
                    },
                    bindings: [{ model: m1, attribute: "A" }]
                });
            }
        });
        _.extend(M2.prototype, Backbone.ComputedAttributeMixin);
        var m2 = new M2();

        var M3 = Backbone.Model.extend({
            initialize: function()
            {
                this.createComputedAttribute({
                    attr: "D",
                    get: function()
                    {
                        return m1.get("B") + m2.get("C") + "d";
                    },
                    bindings: [{ model: m1, attribute: "B" }, { model : m2, attribute: "C" }]
                });
            }
        });
        _.extend(M3.prototype, Backbone.ComputedAttributeMixin);
        var m3 = new M3();

        m2.on("change:C", function() { equal(m2.get("C"), "xc"); });
        m3.on("change:D", function() { equal(m3.get("D"), "yxcd"); });
        // The order of these matter, because by updating A first, we cause m2 to get added to the _changeQueue first,
        // which means m3 gets popped off first and D is evaluated, which results in C being re-evaluated, and the
        // tricky part is making sure that when we then pop off m2 that C is still marked as unchanged.
        m1.set({ A : "x", B : "y" });
    });

    test("specifying attributes via computed hash", 1, function()
    {
        var Model = Backbone.Model.extend({
            computed : {
                "C" : {
                    get: function()
                    {
                        return "hello";
                    },
                    bindings: function()
                    {
                        return [];
                    }
                }
            },
            initialize: function()
            {
                this.createComputedAttributes();
            }
        });
        _.extend(Model.prototype, Backbone.ComputedAttributeMixin);
        var m = new Model();
        equal(m.get("C"), "hello");
    });

    test("overriding attributes in computed hash", 1, function()
    {
        var Parent = Backbone.Model.extend({
            computed : {
                "C" : {
                    get: function()
                    {
                        return "hello";
                    },
                    bindings: function()
                    {
                        return [];
                    }
                }
            },
            initialize: function()
            {
                this.createComputedAttributes();
            }
        });
        _.extend(Parent.prototype, Backbone.ComputedAttributeMixin);

        var Child = Parent.extend({
            computed : {
                "C" : {
                    get: function()
                    {
                        return "bye";
                    },
                    bindings: function()
                    {
                        return [];
                    }
                }
            }
        });

        var c = new Child();
        equal(c.get("C"), "bye");
    });

});

*/
<commit_msg>running tests<commit_after>var _ = require("underscore");
var assert = require("assert");
var Backbone = require("backbone");
var ComputedAttributeMixin = require("../backbone.computed.js");

suite("Backbone computed attributes", function() {
  suite("Basic Tests", function() {
    var TestModel;
    setup(function() {
      TestModel = Backbone.Model.extend({
        initialize: function() {
          this.createComputedAttribute({
            attr: "Z",
            get: function() {
              return this.get("X") + this.get("Y");
            },
            bindings: [
              { model: this, attribute: "X" },
              { model: this, attribute: "Y" }
            ]
          });
        },
        defaults: {
          "X": 1,
          "Y": 2
        }
      });
      _.extend(TestModel.prototype, ComputedAttributeMixin);
    });

    test("initialize and get computed attribute", function() {
      var test = new TestModel();
      assert.equal(test.get("Z"), 3);
    });

    test("set dependency and get computed attribute", function() {
      var test = new TestModel();
      test.set("X", 2);
      assert.equal(test.get("Z"), 4);
    });

    test("bind to computed attribute and set dependency", function() {
      var test = new TestModel();
      test.on("change:Z", function() { assert.equal(test.get("Z"), 5); });
      test.set("X", 3);
    });

    test("computed attribute only fires one change event when multiple dependencies change", function() {
      var test = new TestModel();
      test.on("change:Z", function() { assert.equal(test.get("Z"), 9); });
      test.set({ "X": 4, "Y": 5 });
    });

    test("computed attribute does not fire change event when dependency is set to an unchanged value", function() {
      var test = new TestModel();
      test.on("change:Z", function() { assert.ok(true); });
      test.set("X", 1);
    });

    test("computed attribute does not fire change event when dependencies are changed but computed value remains the same", function() {
      var counter = 0;
      var test = new TestModel();
      test.get("Z"); // Caches it once. I don't know if this should happen first automatically.
      test.on("change:Z", function() { counter++; });
      test.set({ X: 2, Y: 1 });
      assert.equal(counter, 0);
    });

    test("computed attribute does not fire multiple change events when dependencies are changed within an atomic block", function() {
        var counter = 0;
        var test = new TestModel();
        test.on("change:Z", function() { counter++; });
        Backbone.atomic(function() {
            test.set("X", 4);
            test.set("Y", 5);
        });
        assert.equal(counter, 1);
    });
  });

  suite("Tests that bind to different models", function() {
    var ModelWithDependencies;
    setup(function() {
      ModelWithDependencies = Backbone.Model.extend({
        initialize: function() {
          this.child1 = new Backbone.Model();
          this.child1.set("A", 2);

          this.child2 = new Backbone.Model();
          this.child2.set("B", 1);

          this.createComputedAttribute({
            attr:"C",
            get: function() {
              return this.child1.get("A") + this.child2.get("B");
            },
            bindings: [
              { model: this.child1, attribute: "A" },
              { model: this.child2, attribute: "B" }
            ]
          });
        }
      });
      _.extend(ModelWithDependencies.prototype, ComputedAttributeMixin);
    });

    test("initialize and get computed attribute that uses a different model as a dependency", function() {
        var test = new ModelWithDependencies();
        assert.equal(test.get("C"), 3);
    });

    test("bind to computed attribute and set attribute on depended-on model", function() {
      var test = new ModelWithDependencies();
      test.on("change:C", function() { assert.equal(test.get("C"), 5); });
      test.child1.set("A", 4);
    });

    test("listening to change when the value of a computed attribute changes passes the model correctly to listeners", function() {
        var test = new ModelWithDependencies();
        test.on('change', function(model) { assert.equal(model, test); });
        test.child1.set("A", 4);
    });

    test("bind to non-specific change and set attribute on depended-on model", function() {
      var test = new ModelWithDependencies();
      test.on("change", function() { assert.equal(test.get("C"), 5); });
      test.child1.set("A", 4);
    });
  });

  suite("A different variation on bindings", function() {
    var ModelWithDifferentBindings;
    setup(function() {
      ModelWithDifferentBindings = Backbone.Model.extend({
        initialize: function() {
          this.child1 = new Backbone.Model();
          this.child1.set({ A: "A", B: "B" });
          this.createComputedAttribute({
            attr: "C",
            get: function() {
              return this.child1.get("A") + this.child1.get("B");
            },
            bindings: [{ model: this.child1, attributes: ["A", "B"] }]
          });
        }
      });
    _.extend(ModelWithDifferentBindings.prototype, ComputedAttributeMixin);
    });

    test("initialize and get computed attribute with an attribute array binding", function() {
      var test = new ModelWithDifferentBindings();
      assert.equal(test.get("C"), "AB");
    });

    test("change first attribute within an attribute array binding", function() {
      var test = new ModelWithDifferentBindings();
      test.on("change:C", function() { assert.equal(test.get("C"), "dB"); });
      test.child1.set("A", "d");
    });

    test("change second attribute within an attribute array binding", function() {
      var test = new ModelWithDifferentBindings();
      test.on("change:C", function() { assert.equal(test.get("C"), "Ad"); });
      test.child1.set("B", "d");
    });

    // This might not be considered "expected" behavior.
    test("non-specific change will fire even if there is no change if computed attribute has not yet been cached", function() {
      var test = new ModelWithDifferentBindings();
      test.on("change", function() { assert.ok(true); });
      test.child1.set({ "A": "AB", "B": "" });
    });

    test("no non-specific change will fire if cached computed attribute is unchanged", function() {
      var test = new ModelWithDifferentBindings();
      // Have to call the getter once, just to cache it.
      test.get("C");
      test.on("change", function() { assert.ok(false); });
      test.child1.set({ "A": "AB", "B": "" });
    });

    test("events will not be triggered on a destroyed model", function() {
      var test = new ModelWithDifferentBindings();
      var c1 = test.child1;
      test.on("change:C", function() { assert.ok(false); });
      test.destroy();
      c1.set("B", "d");
    });

    test("lots of sets, only one change", function() {
      var test = new ModelWithDifferentBindings();
      test.on("change:C", function() { assert.equal(test.get("C"), "de"); });
      test.child1.set({ "A": "d", "B": "e" });
      test.child1.set("B", "e");
    });
  });

  suite("Tests multi level dependencies. The order of these computed properties is important, because it puts some stress on how the attributes are cached.", function() {
    var TestMode;
    var MultiLevelDependencyModel;
    setup(function() {
      TestModel = Backbone.Model.extend({
        initialize: function() {
          this.createComputedAttribute({
            attr: "Z",
            get: function() {
              return this.get("X") + this.get("Y");
            },
            bindings: [
              { model: this, attribute: "X" },
              { model: this, attribute: "Y" }
            ]
          });
        },
        defaults: {
          "X": 1,
          "Y": 2
        }
      });
      _.extend(TestModel.prototype, ComputedAttributeMixin);

      MultiLevelDependencyModel = Backbone.Model.extend({
        initialize: function() {
          this.child1 = new TestModel();
          this.createComputedAttribute({
            attr: "C",
            get: function() {
              return this.child1.get("Z") + "c";
            },
            bindings: [{ model: this.child1, attribute: "Z"}]
          });
          this.createComputedAttribute({
            attr: "E",
            get: function() {
              return this.child1.get("X") + "e";
            },
            bindings: [{ model: this.child1, attribute: "X"}]
          });
          this.createComputedAttribute({
            attr: "D",
            get: function() {
              return this.child1.get("Y") + this.get("E") + "d";
            },
            bindings: [{ model: this.child1, attribute: "Y"}, "E"]
          });
          this.createComputedAttribute({
            attr: "F",
            get: function() {
              return this.child1.get("Z") + this.get("D") + "f";
            },
            bindings: [{ model: this.child1, attribute: "Z"}, "D"]
          });
        }
      });
      _.extend(MultiLevelDependencyModel.prototype, ComputedAttributeMixin);
    });

    test("get computed attribute with a multi-level dependency", function() {
      var test = new MultiLevelDependencyModel();
      assert.equal(test.get("C"), "3c");
    });

    test("change the root attribute of a multi-level dependency", function() {
      var test = new MultiLevelDependencyModel();
      test.on("change:C", function() { assert.equal(test.get("C"), "4c"); });
      test.child1.set("X", 2);
    });

    test("change event is fired on middle tier of multi-level dependency", function() {
      var test = new MultiLevelDependencyModel();
      test.on("change:D", function() { assert.equal(test.get("D"), "yxed"); });
      test.child1.set({ Y : "y", X : "x" });
    });
  });

  suite("Additional tests for caching computed attributes", function() {
    var NoCacheModel;
    setup(function() {
      NoCacheModel = Backbone.Model.extend({
        initialize: function() {
          this.child1 = new Backbone.Model({ Z : 1, Y : 2 });
          this.createComputedAttribute({
            attr: "cached",
            get: function() {
              return { z: this.child1.get("Z"), y : this.child1.get("Y") };
            },
            bindings: [{ model: this.child1, attributes: ["Y", "Z"]}]
          });
          this.createComputedAttribute({
            attr: "uncached",
            get: function() {
              return { z: this.child1.get("Z"), y : this.child1.get("Y") };
            },
            bindings: [{ model: this.child1, attributes: ["Y", "Z"]}],
            nocache: true
          });
        }
      });
      _.extend(NoCacheModel.prototype, ComputedAttributeMixin);
    });

    test("if a cached object is modified, the modified object will be returned by the getter", function() {
      var test = new NoCacheModel();
      var obj = test.get("cached");
      obj.z = 3;
      var obj2 = test.get("cached");
      assert.equal(obj2.z, 3);
    });

    test("if an uncached object is modified, a new object will be returned by the getter", function() {
      var test = new NoCacheModel();
      var obj = test.get("uncached");
      obj.z = 3;
      var obj2 = test.get("uncached");
      assert.equal(obj2.z, 1);
    });
  });

  suite("Tests for different interplays between setting values in event handles while also using computed attributes", function() {
    var MixedAttributeAndEventModel;
    setup(function() {
      MixedAttributeAndEventModel = Backbone.Model.extend({
          initialize: function() {
            this.child1 = new Backbone.Model({ X : "x", Y : "y", Z : "z" });
            this.createComputedAttribute({
              attr: "A",
              get: function() {
                return this.child1.get("X") + this.child1.get("Y");
              },
              bindings: [{ model: this.child1, attributes: ["X", "Y"]}]
            });
            this.createComputedAttribute({
              attr: "V",
              get: function() {
                return this.child1.get("Z");
              },
              bindings: [{ model: this.child1, attributes: ["Z"]}]
            });
            this.on("change:A", function()  {
              this.child1.set("Z", "w");
            }, this);
          }
      });
      _.extend(MixedAttributeAndEventModel.prototype, ComputedAttributeMixin);
    });

    test("setting a different depended-on attribute when a computed attribute changes can recompute other attributes", function() {
      var test = new MixedAttributeAndEventModel();
      test.on("change:V", function() { assert.equal(test.get("V"), "w"); });
      test.child1.set("X", "v");
    });
  });

  suite("Misc", function() {
    test("all computed properties change events despite weird dependency order", function() {
        var m1 = new Backbone.Model({ A : "a", B : "b" });
        var M2 = Backbone.Model.extend({
          initialize: function() {
            this.createComputedAttribute({
              attr: "C",
              get: function() {
                return m1.get("A") + "c";
              },
              bindings: [{ model: m1, attribute: "A" }]
            });
          }
        });
        _.extend(M2.prototype, ComputedAttributeMixin);

        var m2 = new M2();

        var M3 = Backbone.Model.extend({
          initialize: function() {
            this.createComputedAttribute({
              attr: "D",
              get: function() {
                return m1.get("B") + m2.get("C") + "d";
              },
              bindings: [{ model: m1, attribute: "B" }, { model : m2, attribute: "C" }]
            });
          }
        });
        _.extend(M3.prototype, ComputedAttributeMixin);

        var m3 = new M3();

        m2.on("change:C", function() { assert.equal(m2.get("C"), "xc"); });
        m3.on("change:D", function() { assert.equal(m3.get("D"), "yxcd"); });
        // The order of these matter, because by updating A first, we cause m2 to get added to the _changeQueue first,
        // which means m3 gets popped off first and D is evaluated, which results in C being re-evaluated, and the
        // tricky part is making sure that when we then pop off m2 that C is still marked as unchanged.
        m1.set({ A : "x", B : "y" });
    });

    test("specifying attributes via computed hash", function() {
      var Model = Backbone.Model.extend({
        computed : {
          "C" : {
            get: function() {
              return "hello";
            },
            bindings: function() {
              return [];
            }
          }
        },
        initialize: function() {
          this.createComputedAttributes();
        }
      });
      _.extend(Model.prototype, ComputedAttributeMixin);

      var m = new Model();
      assert.equal(m.get("C"), "hello");
    });

    test("overriding attributes in computed hash", function() {
      var Parent = Backbone.Model.extend({
        computed : {
          "C" : {
            get: function() {
              return "hello";
            },
            bindings: function() {
              return [];
            }
          }
        },
        initialize: function() {
          this.createComputedAttributes();
        }
      });
      _.extend(Parent.prototype, ComputedAttributeMixin);

      var Child = Parent.extend({
        computed : {
          "C" : {
            get: function() {
              return "bye";
            },
            bindings: function() {
              return [];
            }
          }
        }
      });

      var c = new Child();
      assert.equal(c.get("C"), "bye");
    });
  });
});

<|endoftext|>