<commit_before>import Ember from 'ember';

export default Ember.Controller.extend({
    isAdding: false,
    isInErrorState: false,
    rssURL: '',
    actions: {
        create: function(url) {
            var self = this;

            if (url) {
                self.set('rssURL', url);
                console.log('RssURL Set...', url);
            }

            if (!self.get('rssURL') || !self.get('rssURL').length) {
                console.log('No rssUrl specified!');
                return;
            }

            // If the URL entered doesn't have a protocol attached, make
            // sure one is added so we don't get an error (#43).
            if (!self.get('rssURL').match(/^http[s]?:\/\//i)) {
                self.set('rssURL', 'http://' + self.get('rssURL'));
            }

            self.set('isAdding', true);

            var existingPodcast = self.store.find('podcast', {
                rssURL: self.get('rssURL')
            }).then(function(podcast) {
                console.log('Podcast: ', podcast);
                console.info('Podcast already exists', podcast.objectAt(0));

                self.set('isAdding', false);
                self.set('rssURL', '');
                self.transitionToRoute('podcast', podcast.objectAt(0));
            }, function() {

                console.info('Creating new podcast.');

                var podcast = self.store.createRecord('podcast', {
                    rssURL: self.get('rssURL')
                });

                podcast.update().then(function() {
                    console.log('Finished Updating!');
                    self.set('isAdding', false);
                    self.set('rssURL', '');
                    self.transitionToRoute('podcast', podcast);
                }, function() {
                    self.set('isAdding', false);
                    self.set('isInErrorState', true);
                });
            });
        }
    }
});
<commit_msg>Fixup indentation<commit_after>import Ember from 'ember';

export default Ember.Controller.extend({
  isAdding: false,
  isInErrorState: false,
  rssURL: '',
  actions: {
    create: function(url) {
      var self = this;

      if (url) {
        self.set('rssURL', url);
        console.log('RssURL Set...', url);
      }

      if (!self.get('rssURL') || !self.get('rssURL').length) {
        console.log('No rssUrl specified!');
        return;
      }

      // If the URL entered doesn't have a protocol attached, make
      // sure one is added so we don't get an error (#43).
      if (!self.get('rssURL').match(/^http[s]?:\/\//i)) {
        self.set('rssURL', 'http://' + self.get('rssURL'));
      }

      self.set('isAdding', true);

      var existingPodcast = self.store.find('podcast', {
        rssURL: self.get('rssURL')
      }).then(function(podcast) {
        console.log('Podcast: ', podcast);
        console.info('Podcast already exists', podcast.objectAt(0));

        self.set('isAdding', false);
        self.set('rssURL', '');
        self.transitionToRoute('podcast', podcast.objectAt(0));
      }, function() {

        console.info('Creating new podcast.');

        var podcast = self.store.createRecord('podcast', {
          rssURL: self.get('rssURL')
        });

        podcast.update().then(function() {
          console.log('Finished Updating!');
          self.set('isAdding', false);
          self.set('rssURL', '');
          self.transitionToRoute('podcast', podcast);
        }, function() {
          self.set('isAdding', false);
          self.set('isInErrorState', true);
        });
      });
    }
  }
});
<|endoftext|>


<commit_before><commit_msg>Destroy stream after first match found<commit_after><|endoftext|>


<commit_before>module.exports = function (server) {
  const SurveyAnswer = server.plugins['hapi-shelf'].model('SurveyAnswer')

  return [    {
    method: 'GET',
    path: '/login',
    handler: {
      view: 'Login'
    }
  },
  {
    method: 'GET',
    path: '/api/survey_answer',
    handler: (request, reply) => {
      SurveyAnswer
        .fetchAll()
        .then(survey_answers => {
          reply(survey_answers)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/survey_answer/{id}',
    handler: (request, reply) => {
      SurveyAnswer
        .where({id: request.params.id})
        .fetch()
        .then(survey_answers => {
          reply(survey_answers)
        })
    }
  },
  {
    method: 'POST',
    path: '/api/survey_answer',
    handler: (request, reply) => {
      const survey_answer = new SurveyAnswer()

      Promise.all(
        request.payload.responses.map(response => {
          survey_answer
            .save({
              survey_response_id: response.surveyResponseId,
              question_id: response.questionId,
              answer_id: response.answerId,
              intensity: response.intensity
            })
        })
      ).then(function (newSurveyAnswer) {
        reply(newSurveyAnswer)
      })
      .catch()
    }
  },
  {
    method: 'POST',
    path: '/api/survey_answer/{id}',
    handler: (request, reply) => {
      survey_answer
        .where({id: request.params.id})
        .fetch()
        .then(function (survey_answer) {
          survey_answer.save({
            survey_response_id: request.payload.surveyResponseId,
            question_id: request.payload.questionId,
            answer_id: request.payload.answerId
          })
            .then(function (surveyResponse) {
              reply(surveyResponse)
            })
            .catch()
        })
        .catch()
    }
  }]
}
<commit_msg>fixes #67<commit_after>module.exports = function (server) {
  const SurveyAnswer = server.plugins['hapi-shelf'].model('SurveyAnswer')

  return [    {
    method: 'GET',
    path: '/login',
    handler: {
      view: 'Login'
    }
  },
  {
    method: 'GET',
    path: '/api/survey_answer',
    handler: (request, reply) => {
      SurveyAnswer
        .fetchAll()
        .then(survey_answers => {
          reply(survey_answers)
        })
    }
  },
  {
    method: 'GET',
    path: '/api/survey_answer/{id}',
    handler: (request, reply) => {
      SurveyAnswer
        .where({id: request.params.id})
        .fetch()
        .then(survey_answers => {
          reply(survey_answers)
        })
    }
  },
  {
    method: 'POST',
    path: '/api/survey_answer',
    handler: (request, reply) => {
      if (request.payload.responses) {
        Promise.all(
          request.payload.responses.map(function(answer) {
              var surveyAnswer = new SurveyAnswer()
              var newAnswer = surveyAnswer
              .save({
                survey_response_id: answer.surveyResponseId,
                question_id: answer.questionId,
                answer_id: answer.answerId,
                intensity: answer.intensity
              })
              .catch(function(err) {
                console.error(err)
              })

              return newAnswer
          })
        ).then(function (newSurveyAnswers) {
          reply(newSurveyAnswers)
        })
      }
    }
  },
  {
    method: 'POST',
    path: '/api/survey_answer/{id}',
    handler: (request, reply) => {
      survey_answer
        .where({id: request.params.id})
        .fetch()
        .then(function (survey_answer) {
          survey_answer.save({
            survey_response_id: request.payload.surveyResponseId,
            question_id: request.payload.questionId,
            answer_id: request.payload.answerId
          })
            .then(function (surveyResponse) {
              reply(surveyResponse)
            })
            .catch()
        })
        .catch()
    }
  }]
}
<|endoftext|>


<commit_before>"use strict";

const express = require("express");
const cache = require("apicache");
const fs = require("fs");

const {
  db,
  as,
  CREATE_ORGANIZATION,
  ORGANIZATION_BY_ID,
  INSERT_AUTHOR,
  INSERT_LOCALIZED_TEXT,
  UPDATE_ORGANIZATION,
  UPDATE_AUTHOR_FIRST,
  UPDATE_AUTHOR_LAST,
  listUsers,
  refreshSearch,
  listMethods,
  ErrorReporter,
} = require("../helpers/db");

const {
  setConditional,
  maybeUpdateUserText,
  parseGetParams,
  validateUrl,
  verifyOrUpdateUrl,
  returnByType,
  fixUpURLs,
  createLocalizedRecord,
  getCollections
} = require("../helpers/things");

const logError = require("../helpers/log-error.js");

const requireAuthenticatedUser = require("../middleware/requireAuthenticatedUser.js");
const setAndValidateLanguage = require("../middleware/setAndValidateLanguage.js");
const ORGANIZATION_STRUCTURE = JSON.parse(
  fs.readFileSync("api/helpers/data/organization-structure.json", "utf8")
);
const sharedFieldOptions = require("../helpers/shared-field-options.js");

async function getEditStaticText(params) {
  let staticText = {};
  const lang = params.lang;

  staticText.authors = listUsers();
  staticText.methods = listMethods(lang).filter(article => !article.hidden);

  staticText = Object.assign({}, staticText, sharedFieldOptions);
  staticText.collections = await getCollections(lang);

  return staticText;
}

/**
 * @api {post} /organization/new Create new organization
 * @apiGroup Organizations
 * @apiVersion 0.1.0
 * @apiName newOrganization
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} organization data
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *         "ID": 3,
 *         "Description": 'foo'
 *        }
 *     }
 *
 * @apiError NotAuthenticated The user is not authenticated
 * @apiError NotAuthorized The user doesn't have permission to perform this operation.
 *
 */
async function postOrganizationNewHttp(req, res) {
  // create new `organization` in db
  try {
    cache.clear();
    let title = req.body.title;
    let body = req.body.body || req.body.summary || "";
    let description = req.body.description;
    let original_language = req.body.original_language || "en";
    if (!title) {
      return res.status(400).json({
        OK: false,
        errors: ["Cannot create Organization without at least a title"],
      });
    }

    const user_id = req.user.id;
    const thing = await db.one(CREATE_ORGANIZATION, {
      title,
      body,
      description,
      original_language,
    });
    req.params.thingid = thing.thingid;
    await postOrganizationUpdateHttp(req, res);
    let localizedData = {
      body: body,
      description: description,
      language: original_language,
      title: title
    };
    createLocalizedRecord(localizedData, thing.thingid);
  } catch (error) {
    logError(error);
    return res.status(400).json({ OK: false, error: error });
  }
}

/**
 * @api {put} /organization/:id  Submit a new version of a organization
 * @apiGroup Organizations
 * @apiVersion 0.1.0
 * @apiName editOrganization
 * @apiParam {Number} id Organization ID
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} organization data
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *         "ID": 3,
 *         "Description": 'foo'
 *        }
 *     }
 *
 * @apiError NotAuthenticated The user is not authenticated
 * @apiError NotAuthorized The user doesn't have permission to perform this operation.
 *
 */

async function getOrganization(params, res) {
  try {
    if (Number.isNaN(params.articleid)) {
      return null;
    }
    const articleRow = await db.one(ORGANIZATION_BY_ID, params);
    const article = articleRow.results;
    fixUpURLs(article);
    return article;
  } catch (error) {
    // only log actual excaptional results, not just data not found
    if (error.message !== "No data returned from the query.") {
      logError(error);
    }
    // if no entry is found, render the 404 page
    return null;
  }
}

async function postOrganizationUpdateHttp(req, res) {
  cache.clear();
  const params = parseGetParams(req, "organization");
  const user = req.user;
  const { articleid, type, view, userid, lang, returns } = params;
  const newOrganization = req.body;
  const links = req.body.links;

  //validate url

  if (links) {
    for (let key in links) {
      let url = links[key].url;
      if (url.length > 0) {
        newOrganization.links = verifyOrUpdateUrl(newOrganization.links);
        const isUrlValid = validateUrl(newOrganization);
        if (!isUrlValid) {
          return res.status(400).json({
            OK: false,
            errors: ["Invalid link url."],
          });
        }
      }
    }
  }

  // if this is a new organization, we don't have a post_date yet, so we set it here
  if (!newOrganization.post_date) {
    newOrganization.post_date = Date.now();
  }

  // if this is a new method, we don't have a updated_date yet, so we set it here
  if (!newOrganization.updated_date) {
    newOrganization.updated_date = Date.now();
  }

  const {
    updatedText,
    author,
    oldArticle: oldOrganization,
  } = await maybeUpdateUserText(req, res, "organization");
  const [updatedOrganization, er] = getUpdatedOrganization(
    user,
    params,
    newOrganization,
    oldOrganization
  );
  //get current date when user.isAdmin is false;
  updatedOrganization.updated_date = !user.isadmin
    ? "now"
    : updatedOrganization.updated_date;
  if (!er.hasErrors()) {
    if (updatedText) {
      await db.tx("update-organization", async t => {
        await t.none(INSERT_AUTHOR, author);
        await t.none(INSERT_LOCALIZED_TEXT, updatedText);
        await t.none(UPDATE_ORGANIZATION, updatedOrganization);
      });
      //if this is a new organization, set creator id to userid and isAdmin
      if (user.isadmin) {
        const creator = {
          user_id: newOrganization.creator
            ? newOrganization.creator
            : params.userid,
          thingid: params.articleid,
        };
        const updatedBy = {
          user_id: newOrganization.last_updated_by
            ? newOrganization.last_updated_by
            : params.userid,
          thingid: params.articleid,
          updated_date: newOrganization.updated_date || "now",
        };
        await db.tx("update-organization", async t => {
          await t.none(UPDATE_AUTHOR_FIRST, creator);
          await t.none(UPDATE_AUTHOR_LAST, updatedBy);
        });
      }
    } else {
      await db.tx("update-organization", async t => {
        await t.none(INSERT_AUTHOR, author);
        await t.none(UPDATE_ORGANIZATION, updatedOrganization);
      });
    }
    const freshArticle = await getOrganization(params, res);
    res.status(200).json({
      OK: true,
      article: freshArticle,
    });
    refreshSearch();
  } else {
    logError(`400 with errors: ${er.errors.join(", ")}`);
    res.status(400).json({
      OK: false,
      errors: er.errors,
    });
  }
}

function getUpdatedOrganization(
  user,
  params,
  newOrganization,
  oldOrganization
) {
  const updatedOrganization = Object.assign({}, oldOrganization);
  const er = new ErrorReporter();
  const cond = (key, fn) =>
    setConditional(updatedOrganization, newOrganization, er, fn, key);
  // admin-only
  if (user.isadmin) {
    cond("featured", as.boolean);
    cond("hidden", as.boolean);
    cond("original_language", as.text);
    cond("post_date", as.date);
    cond("completeness", as.text);
    cond("updated_date", as.date);
  }
  // media lists
  ["links", "videos", "audio"].map(key => cond(key, as.media));
  // photos and files are slightly different from other media as they have a source url too
  ["photos", "files"].map(key => cond(key, as.sourcedMedia));
  // boolean (would include "published" but we don't really support it)
  ["facilitators"].map(key => cond(key, as.yesno));
  // text
  [
    "location_name",
    "address1",
    "address2",
    "city",
    "province",
    "postal_code",
    "country",
  ].map(key => cond(key, as.text));
  ["latitude", "longitude"].map(key => cond(key, as.float));
  // key
  ["sector"].map(key => cond(key, as.organizationkeyflat));
  // list of keys
  [
    "scope_of_influence",
    "type_method",
    "type_tool",
    "specific_topics",
    "general_issues"
  ].map(key => cond(key, as.organizationkeys));
  // list of {id, type, title}
  ["specific_methods_tools_techniques", "collections"].map(key => cond(key, as.ids));
  return [updatedOrganization, er];
}

async function getOrganizationHttp(req, res) {
  /* This is the entry point for getting an article */
  const params = parseGetParams(req, "organization");

  const article = await getOrganization(params, res);
  if (!article) {
    res.status(404).render("404");
    return null;
  }
  const staticText = {};
  returnByType(res, params, article, staticText, req.user);
}

async function getOrganizationEditHttp(req, res) {
  const params = parseGetParams(req, "organization");
  params.view = "edit";
  const article = await getOrganization(params, res);
  if (!article) {
    res.status(404).render("404");
    return null;
  }
  const staticText = await getEditStaticText(params);
  returnByType(res, params, article, staticText, req.user);
}

async function getOrganizationNewHttp(req, res) {
  const params = parseGetParams(req, "organization");
  params.view = "edit";
  const article = ORGANIZATION_STRUCTURE;
  const staticText = await getEditStaticText(params);
  returnByType(res, params, article, staticText, req.user);
}

const router = express.Router(); // eslint-disable-line new-cap
router.get("/new", requireAuthenticatedUser(), getOrganizationNewHttp);
router.post("/new", requireAuthenticatedUser(), postOrganizationNewHttp);
router.post(
  "/:thingid",
  requireAuthenticatedUser(),
  postOrganizationUpdateHttp
);
router.get("/:thingid/:language?", setAndValidateLanguage(), getOrganizationHttp);
router.get(
  "/:thingid/edit",
  requireAuthenticatedUser(),
  getOrganizationEditHttp
);

module.exports = {
  organization: router,
  postOrganizationNewHttp,
  postOrganizationUpdateHttp,
  getOrganizationHttp,
  getOrganizationEditHttp,
  getOrganizationNewHttp,
};
<commit_msg>arrange organization route to fix edit page<commit_after>"use strict";

const express = require("express");
const cache = require("apicache");
const fs = require("fs");

const {
  db,
  as,
  CREATE_ORGANIZATION,
  ORGANIZATION_BY_ID,
  INSERT_AUTHOR,
  INSERT_LOCALIZED_TEXT,
  UPDATE_ORGANIZATION,
  UPDATE_AUTHOR_FIRST,
  UPDATE_AUTHOR_LAST,
  listUsers,
  refreshSearch,
  listMethods,
  ErrorReporter,
} = require("../helpers/db");

const {
  setConditional,
  maybeUpdateUserText,
  parseGetParams,
  validateUrl,
  verifyOrUpdateUrl,
  returnByType,
  fixUpURLs,
  createLocalizedRecord,
  getCollections
} = require("../helpers/things");

const logError = require("../helpers/log-error.js");

const requireAuthenticatedUser = require("../middleware/requireAuthenticatedUser.js");
const setAndValidateLanguage = require("../middleware/setAndValidateLanguage.js");
const ORGANIZATION_STRUCTURE = JSON.parse(
  fs.readFileSync("api/helpers/data/organization-structure.json", "utf8")
);
const sharedFieldOptions = require("../helpers/shared-field-options.js");

async function getEditStaticText(params) {
  let staticText = {};
  const lang = params.lang;

  staticText.authors = listUsers();
  staticText.methods = listMethods(lang).filter(article => !article.hidden);

  staticText = Object.assign({}, staticText, sharedFieldOptions);
  staticText.collections = await getCollections(lang);

  return staticText;
}

/**
 * @api {post} /organization/new Create new organization
 * @apiGroup Organizations
 * @apiVersion 0.1.0
 * @apiName newOrganization
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} organization data
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *         "ID": 3,
 *         "Description": 'foo'
 *        }
 *     }
 *
 * @apiError NotAuthenticated The user is not authenticated
 * @apiError NotAuthorized The user doesn't have permission to perform this operation.
 *
 */
async function postOrganizationNewHttp(req, res) {
  // create new `organization` in db
  try {
    cache.clear();
    let title = req.body.title;
    let body = req.body.body || req.body.summary || "";
    let description = req.body.description;
    let original_language = req.body.original_language || "en";
    if (!title) {
      return res.status(400).json({
        OK: false,
        errors: ["Cannot create Organization without at least a title"],
      });
    }

    const user_id = req.user.id;
    const thing = await db.one(CREATE_ORGANIZATION, {
      title,
      body,
      description,
      original_language,
    });
    req.params.thingid = thing.thingid;
    await postOrganizationUpdateHttp(req, res);
    let localizedData = {
      body: body,
      description: description,
      language: original_language,
      title: title
    };
    createLocalizedRecord(localizedData, thing.thingid);
  } catch (error) {
    logError(error);
    return res.status(400).json({ OK: false, error: error });
  }
}

/**
 * @api {put} /organization/:id  Submit a new version of a organization
 * @apiGroup Organizations
 * @apiVersion 0.1.0
 * @apiName editOrganization
 * @apiParam {Number} id Organization ID
 *
 * @apiSuccess {Boolean} OK true if call was successful
 * @apiSuccess {String[]} errors List of error strings (when `OK` is false)
 * @apiSuccess {Object} organization data
 *
 * @apiSuccessExample Success-Response:
 *     HTTP/1.1 200 OK
 *     {
 *       "OK": true,
 *       "data": {
 *         "ID": 3,
 *         "Description": 'foo'
 *        }
 *     }
 *
 * @apiError NotAuthenticated The user is not authenticated
 * @apiError NotAuthorized The user doesn't have permission to perform this operation.
 *
 */

async function getOrganization(params, res) {
  try {
    if (Number.isNaN(params.articleid)) {
      return null;
    }
    const articleRow = await db.one(ORGANIZATION_BY_ID, params);
    const article = articleRow.results;
    fixUpURLs(article);
    return article;
  } catch (error) {
    // only log actual excaptional results, not just data not found
    if (error.message !== "No data returned from the query.") {
      logError(error);
    }
    // if no entry is found, render the 404 page
    return null;
  }
}

async function postOrganizationUpdateHttp(req, res) {
  cache.clear();
  const params = parseGetParams(req, "organization");
  const user = req.user;
  const { articleid, type, view, userid, lang, returns } = params;
  const newOrganization = req.body;
  const links = req.body.links;

  //validate url

  if (links) {
    for (let key in links) {
      let url = links[key].url;
      if (url.length > 0) {
        newOrganization.links = verifyOrUpdateUrl(newOrganization.links);
        const isUrlValid = validateUrl(newOrganization);
        if (!isUrlValid) {
          return res.status(400).json({
            OK: false,
            errors: ["Invalid link url."],
          });
        }
      }
    }
  }

  // if this is a new organization, we don't have a post_date yet, so we set it here
  if (!newOrganization.post_date) {
    newOrganization.post_date = Date.now();
  }

  // if this is a new method, we don't have a updated_date yet, so we set it here
  if (!newOrganization.updated_date) {
    newOrganization.updated_date = Date.now();
  }

  const {
    updatedText,
    author,
    oldArticle: oldOrganization,
  } = await maybeUpdateUserText(req, res, "organization");
  const [updatedOrganization, er] = getUpdatedOrganization(
    user,
    params,
    newOrganization,
    oldOrganization
  );
  //get current date when user.isAdmin is false;
  updatedOrganization.updated_date = !user.isadmin
    ? "now"
    : updatedOrganization.updated_date;
  if (!er.hasErrors()) {
    if (updatedText) {
      await db.tx("update-organization", async t => {
        await t.none(INSERT_AUTHOR, author);
        await t.none(INSERT_LOCALIZED_TEXT, updatedText);
        await t.none(UPDATE_ORGANIZATION, updatedOrganization);
      });
      //if this is a new organization, set creator id to userid and isAdmin
      if (user.isadmin) {
        const creator = {
          user_id: newOrganization.creator
            ? newOrganization.creator
            : params.userid,
          thingid: params.articleid,
        };
        const updatedBy = {
          user_id: newOrganization.last_updated_by
            ? newOrganization.last_updated_by
            : params.userid,
          thingid: params.articleid,
          updated_date: newOrganization.updated_date || "now",
        };
        await db.tx("update-organization", async t => {
          await t.none(UPDATE_AUTHOR_FIRST, creator);
          await t.none(UPDATE_AUTHOR_LAST, updatedBy);
        });
      }
    } else {
      await db.tx("update-organization", async t => {
        await t.none(INSERT_AUTHOR, author);
        await t.none(UPDATE_ORGANIZATION, updatedOrganization);
      });
    }
    const freshArticle = await getOrganization(params, res);
    res.status(200).json({
      OK: true,
      article: freshArticle,
    });
    refreshSearch();
  } else {
    logError(`400 with errors: ${er.errors.join(", ")}`);
    res.status(400).json({
      OK: false,
      errors: er.errors,
    });
  }
}

function getUpdatedOrganization(
  user,
  params,
  newOrganization,
  oldOrganization
) {
  const updatedOrganization = Object.assign({}, oldOrganization);
  const er = new ErrorReporter();
  const cond = (key, fn) =>
    setConditional(updatedOrganization, newOrganization, er, fn, key);
  // admin-only
  if (user.isadmin) {
    cond("featured", as.boolean);
    cond("hidden", as.boolean);
    cond("original_language", as.text);
    cond("post_date", as.date);
    cond("completeness", as.text);
    cond("updated_date", as.date);
  }
  // media lists
  ["links", "videos", "audio"].map(key => cond(key, as.media));
  // photos and files are slightly different from other media as they have a source url too
  ["photos", "files"].map(key => cond(key, as.sourcedMedia));
  // boolean (would include "published" but we don't really support it)
  ["facilitators"].map(key => cond(key, as.yesno));
  // text
  [
    "location_name",
    "address1",
    "address2",
    "city",
    "province",
    "postal_code",
    "country",
  ].map(key => cond(key, as.text));
  ["latitude", "longitude"].map(key => cond(key, as.float));
  // key
  ["sector"].map(key => cond(key, as.organizationkeyflat));
  // list of keys
  [
    "scope_of_influence",
    "type_method",
    "type_tool",
    "specific_topics",
    "general_issues"
  ].map(key => cond(key, as.organizationkeys));
  // list of {id, type, title}
  ["specific_methods_tools_techniques", "collections"].map(key => cond(key, as.ids));
  return [updatedOrganization, er];
}

async function getOrganizationHttp(req, res) {
  /* This is the entry point for getting an article */
  const params = parseGetParams(req, "organization");

  const article = await getOrganization(params, res);
  if (!article) {
    res.status(404).render("404");
    return null;
  }
  const staticText = {};
  returnByType(res, params, article, staticText, req.user);
}

async function getOrganizationEditHttp(req, res) {
  const params = parseGetParams(req, "organization");
  params.view = "edit";
  const article = await getOrganization(params, res);
  if (!article) {
    res.status(404).render("404");
    return null;
  }
  const staticText = await getEditStaticText(params);
  returnByType(res, params, article, staticText, req.user);
}

async function getOrganizationNewHttp(req, res) {
  const params = parseGetParams(req, "organization");
  params.view = "edit";
  const article = ORGANIZATION_STRUCTURE;
  const staticText = await getEditStaticText(params);
  returnByType(res, params, article, staticText, req.user);
}

const router = express.Router(); // eslint-disable-line new-cap
router.get("/:thingid/edit", getOrganizationEditHttp);
router.get("/new", requireAuthenticatedUser(), getOrganizationNewHttp);
router.post("/new", requireAuthenticatedUser(), postOrganizationNewHttp);
router.get("/:thingid/:language?", setAndValidateLanguage(), getOrganizationHttp);
router.post("/:thingid", requireAuthenticatedUser(), postOrganizationUpdateHttp);

module.exports = {
  organization: router,
  postOrganizationNewHttp,
  postOrganizationUpdateHttp,
  getOrganizationHttp,
  getOrganizationEditHttp,
  getOrganizationNewHttp,
};
<|endoftext|>


<commit_before>const userColumns = q => q.column('users.id', 'users.name', 'users.avatar_url')

var postRelations = (userId, opts = {}) => {
  var relations = _.filter([
    {user: userColumns},
    {communities: qb => qb.column('community.id', 'name', 'slug', 'avatar_url')},
    'contributions',
    {'contributions.user': userColumns},
    {followers: userColumns},
    'media',
    {relatedUsers: userColumns},
    {responders: qb => qb.column('users.id', 'name', 'avatar_url', 'event_responses.response')},
    (opts.fromProject ? null : {projects: qb => qb.column('projects.id', 'title', 'slug')}),
    (opts.withComments
      ? {comments: qb => qb.column('comment.id', 'text', 'created_at', 'user_id', 'post_id')}
      : null),
    (opts.withComments
      ? {'comments.user': userColumns}
      : null),
    (opts.withVotes
      ? {votes: qb => { // all votes
        qb.column('id', 'post_id', 'user_id')
      }}
      : {votes: qb => { // only the user's own vote
        qb.column('id', 'post_id')
        qb.where('user_id', userId)
      }}),
    (opts.withVotes
      ? {'votes.user': userColumns}
      : null)
  ], x => !!x)
  return relations
}

var postAttributes = (post, userId, opts = {}) => {
  // userId is only used if opts.withVotes, so there are times when this is called with userId=undefined.
  var rel = post.relations
  var extendedPost = _.extend(
    _.pick(post.toJSON(), [
      'id',
      'name',
      'description',
      'fulfilled_at',
      'type',
      'created_at',
      'updated_at',
      'projects',
      'similarity',
      'start_time',
      'end_time',
      'location'
    ]),
    {
      user: rel.user ? rel.user.pick('id', 'name', 'avatar_url') : null,
      communities: rel.communities.map(c => c.pick('id', 'name', 'slug', 'avatar_url', 'banner_url')),
      contributors: rel.contributions.map(c => c.relations.user.pick('id', 'name', 'avatar_url')),
      followers: rel.followers.map(u => u.pick('id', 'name', 'avatar_url')),
      responders: rel.responders.map(u => u.pick('id', 'name', 'avatar_url', 'response')),
      media: rel.media.map(m => m.pick('name', 'type', 'url', 'thumbnail_url', 'width', 'height')),
      numComments: post.get('num_comments'),
      relatedUsers: rel.relatedUsers.map(u => u.pick('id', 'name', 'avatar_url')),
      public: post.get('visibility') === Post.Visibility.PUBLIC_READABLE
    })
  if (opts.withComments) {
    extendedPost.comments = rel.comments.map(c => _.merge(
      c.pick('id', 'text', 'created_at', 'user'),
      {user: c.relations.user.pick('id', 'name', 'avatar_url')}
    ))
  }
  if (opts.withVotes) {
    extendedPost.voters = rel.votes.map(v => v.relations.user.pick('id', 'name', 'avatar_url'))
  } else {
    // for compatability with angular frontend
    extendedPost.votes = post.get('num_votes')
    extendedPost.myVote = rel.votes.length > 0
  }
  return extendedPost
}

module.exports = {
  relations: postRelations,
  present: postAttributes
}
<commit_msg>don't assume comments will be returned in order<commit_after>const userColumns = q => q.column('users.id', 'users.name', 'users.avatar_url')

var postRelations = (userId, opts = {}) => {
  var relations = _.filter([
    {user: userColumns},
    {communities: qb => qb.column('community.id', 'name', 'slug', 'avatar_url')},
    'contributions',
    {'contributions.user': userColumns},
    {followers: userColumns},
    'media',
    {relatedUsers: userColumns},
    {responders: qb => qb.column('users.id', 'name', 'avatar_url', 'event_responses.response')},
    (opts.fromProject ? null : {projects: qb => qb.column('projects.id', 'title', 'slug')}),
    (opts.withComments
      ? {comments: qb => {
        qb.column('comment.id', 'text', 'created_at', 'user_id', 'post_id')
        qb.orderBy('comment.id', 'asc')
      }}
      : null),
    (opts.withComments
      ? {'comments.user': userColumns}
      : null),
    (opts.withVotes
      ? {votes: qb => { // all votes
        qb.column('id', 'post_id', 'user_id')
      }}
      : {votes: qb => { // only the user's own vote
        qb.column('id', 'post_id')
        qb.where('user_id', userId)
      }}),
    (opts.withVotes
      ? {'votes.user': userColumns}
      : null)
  ], x => !!x)
  return relations
}

var postAttributes = (post, userId, opts = {}) => {
  // userId is only used if opts.withVotes, so there are times when this is called with userId=undefined.
  var rel = post.relations
  var extendedPost = _.extend(
    _.pick(post.toJSON(), [
      'id',
      'name',
      'description',
      'fulfilled_at',
      'type',
      'created_at',
      'updated_at',
      'projects',
      'similarity',
      'start_time',
      'end_time',
      'location'
    ]),
    {
      user: rel.user ? rel.user.pick('id', 'name', 'avatar_url') : null,
      communities: rel.communities.map(c => c.pick('id', 'name', 'slug', 'avatar_url', 'banner_url')),
      contributors: rel.contributions.map(c => c.relations.user.pick('id', 'name', 'avatar_url')),
      followers: rel.followers.map(u => u.pick('id', 'name', 'avatar_url')),
      responders: rel.responders.map(u => u.pick('id', 'name', 'avatar_url', 'response')),
      media: rel.media.map(m => m.pick('name', 'type', 'url', 'thumbnail_url', 'width', 'height')),
      numComments: post.get('num_comments'),
      relatedUsers: rel.relatedUsers.map(u => u.pick('id', 'name', 'avatar_url')),
      public: post.get('visibility') === Post.Visibility.PUBLIC_READABLE
    })
  if (opts.withComments) {
    extendedPost.comments = rel.comments.map(c => _.merge(
      c.pick('id', 'text', 'created_at', 'user'),
      {user: c.relations.user.pick('id', 'name', 'avatar_url')}
    ))
  }
  if (opts.withVotes) {
    extendedPost.voters = rel.votes.map(v => v.relations.user.pick('id', 'name', 'avatar_url'))
  } else {
    // for compatability with angular frontend
    extendedPost.votes = post.get('num_votes')
    extendedPost.myVote = rel.votes.length > 0
  }
  return extendedPost
}

module.exports = {
  relations: postRelations,
  present: postAttributes
}
<|endoftext|>


<commit_before>// Score.js
//
// @description :: A service to manage scores of Bet, User and Group models

module.exports = {

  // computeBets
  //
  // @description :: Compute each bets score attribute of a single match
  // @param       :: matchId (required): the match concerned
  //                 cb (required): the function called when it's done or an error occured
  computeBets: function (matchId, cb) {
    var bets = [],
        scoreA = 0,
        scoreB = 0,
        importance = 1.0;

    if(!matchId || !cb)
      throw new Error('Missing param');
    if(!matchId.length || typeof cb !== 'function')
      throw new Error('Invalid param');

    // 1. In parallel,
    //    - Find all bets related to this matchId
    //    - Get this match informations
    // 2. For each bet
    //    1. Compute its score
    //    2. Save it
    async.series([
      function findBetsAndGetMatch(next) {
        async.parallel([
          function findBets(next2) {
            Bet
              .find({ match: matchId }, { fields: ['id', 'scoreTeamA', 'scoreTeamB', 'score'] })
              .limit(0)
              .exec(function (err, instances) {
                if(err)
                  return next2(err);

                if(!instances)
                  return next2('Error trying to find bets with matchId "' + matchId + '"');

                bets = instances;

                return next2();
            });
          },
          function getMatch(next2) {
            Match
              .findOne(matchId, { fields: ['importance', 'scoreTeamA', 'scoreTeamB'] })
              .exec(function (err, instance) {
                if(err)
                  return next2(err);

                if(!instance)
                  return next2('Error trying to find match "' + matchId + '"');

                importance = instance.importance;
                scoreA = instance.scoreTeamA;
                scoreB = instance.scoreTeamB;

                return next2();
            });
          }
        ], next);
      },
      function updateEachBet(next) {
        var score = 0;

        async.each(bets, function (bet, nextBet) {
          score = 0;

          async.waterfall([
            function computeBetScore(next2) {
              // ToDo
              score = 10;

              return next2();
            },
            function updateBet(score, next2) {
              bet.score = score;

              bet
                .save()
                .exec(function (err, instance) {
                  if(err)
                    return next2(err);

                  if(!instance)
                    return next2('Error trying to update bet "' + bet.id + '" with score "' + score + '"');

                  return next2();
              });
            }
          ], nextBet);
        }, next);
      }
    ], cb);
  },

  // computeUser
  //
  // @description :: Compute a user score attribute from his bets
  // @param       :: userId (required): the user concerned
  //                 cb (required): the function called when it's done or an error occured
  computeUser: function (userId, cb) {
    var bets = [],
        user = {};

    if(!userId || !cb)
      throw new Error('Missing param');
    if(!userId.length || typeof cb !== 'function')
      throw new Error('Invalid param');

    // 1. In parallel,
    //    - Find all bets related to this userId
    //    - Get this user informations (to check he exists)
    // 2. Sum his bets' scores
    // 3. Save it
    async.waterfall([
      function findBetsAndGetUser(next) {
        async.parallel([
          function findBets(next2) {
            Bet
              .find({ user: userId }, { fields: ['score'] })
              .limit(0)
              .exec(function (err, instances) {
                if(err)
                  return next2(err);

                if(!instances)
                  return next2('Error trying to find bets with userId "' + userId + '"');

                bets = instances;

                return next2();
            });
          },
          function getUser(next2) {
            User
              .findOne(userId, { fields: ['id', 'score'] })
              .exec(function (err, instance) {
                if(err)
                  return next2(err);

                if(!instance)
                  return next2('Error trying to find user "' + userId + '"');

                user = instance;

                return next2();
            });
          }
        ], next);
      },
      function sumBetsScores(next) {
        var score = _.sum(_.pluck(bets, 'score'));

        return next(null, score);
      },
      function updateUser(score, next) {
        user.score = score;

        user
          .save()
          .exec(function (err, instance) {
            if(err)
              return next(err);

            if(!instance)
              return next('Error trying to update user "' + userId + '" with score "' + score + '"');

            return next();
        });
      }
    ], cb);
  },

  // computeGroup
  //
  // @description :: Compute a group score attribute from its users score
  // @param       :: groupId (required): the group concerned
  //                 cb (required): the function called when it's done or an error occured
  computeGroup: function (groupId, cb) {
    var users = [],
        group = {};

    if(!groupId || !cb)
      throw new Error('Missing param');
    if(!groupId.length || typeof cb !== 'function')
      throw new Error('Invalid param');

    // 1. In parallel,
    //    - Find all users related to this groupId
    //    - Get this group informations (to check it exists)
    // 2. Sum the users' scores
    // 3. Save it
    async.waterfall([
      function findUsersAndGetGroup(next) {
        async.parallel([
          function findUsers(next2) {
            Membership
              .find({ group: groupId }, { fields: ['id'] })
              .populate('user')
              .limit(0)
              .exec(function (err, instances) {
                if(err)
                  return next2(err);

                if(!instances)
                  return next2('Error trying to find memberships with groupId "' + groupId + '"');

                users = _.pluck(instances, 'user');

                return next2();
            });
          },
          function getGroup(next2) {
            Group
              .findOne(groupId, { fields: ['id', 'score'] })
              .exec(function (err, instance) {
                if(err)
                  return next2(err);

                if(!instance)
                  return next2('Error trying to find gruop "' + gruopId + '"');

                group = instance;

                return next2();
            });
          }
        ], next);
      },
      function sumUsersScores(next) {
        var score = _.sum(_.pluck(users, 'score'));

        return next(null, score);
      },
      function updateGroup(score, next) {
        group.score = score;

        group
          .save()
          .exec(function (err, instance) {
            if(err)
              return next(err);

            if(!instance)
              return next('Error trying to update group "' + groupId + '" with score "' + score + '"');

            return next();
        });
      }
    ], cb);
  },

  // computeUsers
  //
  // @description :: Compute a list of users' score attribute from his bets
  // @param       :: usersIds (required): the users concerned
  //                 cb (required): the function called when it's done or an error occured
  computeUsers: function (usersIds, cb) {
    if(!usersIds || !cb)
      throw new Error('Missing param');
    if(!usersIds.length || typeof cb !== 'function')
      throw new Error('Invalid param');

    async.each(usersIds, function (userId, next) {
      this.computeUser(userId, next);
    }, cb);
  },

  // computeGroups
  //
  // @description :: Compute a list of groups' score attribute from its users score
  // @param       :: groupsIds (required): the groups concerned
  //                 cb (required): the function called when it's done or an error occured
  computeGroups: function (groupsIds, cb) {
    if(!groupsIds || !cb)
      throw new Error('Missing param');
    if(!groupsIds.length || typeof cb !== 'function')
      throw new Error('Invalid param');

    async.each(groupsIds, function (groupId, next) {
      this.computeGroup(groupId, next);
    }, cb);
  }

};
<commit_msg>refactor(scorecaculator): 80 columns max width<commit_after>// Score.js
//
// @description :: A service to manage scores of Bet, User and Group models

module.exports = {

  // computeBets
  //
  // @description :: Compute each bets score attribute of a single match
  // @param       :: matchId (required): the match concerned
  //                 cb (required): the function called when it's done or an
  //                 error occured
  computeBets: function (matchId, cb) {
    var bets = [],
        scoreA = 0,
        scoreB = 0,
        importance = 1.0;

    if(!matchId || !cb) {
      throw new Error('Missing param');
    }
    if(!matchId.length || typeof cb !== 'function') {
      throw new Error('Invalid param');
    }

    // 1. In parallel,
    //    - Find all bets related to this matchId
    //    - Get this match informations
    // 2. For each bet
    //    1. Compute its score
    //    2. Save it
    async.series([
      function findBetsAndGetMatch(next) {
        async.parallel([
          function findBets(next2) {
            Bet
              .find({ match: matchId }, {
                fields: ['id', 'scoreTeamA', 'scoreTeamB', 'score']
              })
              .limit(0)
              .exec(function (err, instances) {
                if(err) {
                  return next2(err);
                }

                if(!instances) {
                  return next2(
                    new Error('Error trying to find bets with matchId "'
                      + matchId + '"'));
                }

                bets = instances;

                return next2();
            });
          },
          function getMatch(next2) {
            Match
              .findOne(matchId, {
                fields: ['importance', 'scoreTeamA', 'scoreTeamB']
              })
              .exec(function (err, instance) {
                if(err) {
                  return next2(err);
                }

                if(!instance) {
                  return next2(
                    new Error('Error trying to find match "' + matchId + '"'));
                }

                importance = instance.importance;
                scoreA = instance.scoreTeamA;
                scoreB = instance.scoreTeamB;

                return next2();
            });
          }
        ], next);
      },
      function updateEachBet(next) {
        var score = 0;

        async.each(bets, function (bet, nextBet) {
          score = 0;

          async.waterfall([
            function computeBetScore(next2) {
              // ToDo
              score = 10;

              return next2();
            },
            function updateBet(score, next2) {
              bet.score = score;

              bet
                .save()
                .exec(function (err, instance) {
                  if(err) {
                    return next2(err);
                  }

                  if(!instance) {
                    return next2(new Error('Error trying to update bet "'
                      + bet.id + '" with score "' + score + '"'));
                  }

                  return next2();
              });
            }
          ], nextBet);
        }, next);
      }
    ], cb);
  },

  // computeUser
  //
  // @description :: Compute a user score attribute from his bets
  // @param       :: userId (required): the user concerned
  //                 cb (required): the function called when it's done or an
  //                 error occured
  computeUser: function (userId, cb) {
    var bets = [],
        user = {};

    if(!userId || !cb) {
      throw new Error('Missing param');
    }

    if(!userId.length || typeof cb !== 'function') {
      throw new Error('Invalid param');
    }

    // 1. In parallel,
    //    - Find all bets related to this userId
    //    - Get this user informations (to check he exists)
    // 2. Sum his bets' scores
    // 3. Save it
    async.waterfall([
      function findBetsAndGetUser(next) {
        async.parallel([
          function findBets(next2) {
            Bet
              .find({ user: userId }, { fields: ['score'] })
              .limit(0)
              .exec(function (err, instances) {
                if(err) {
                  return next2(err);
                }

                if(!instances) {
                  return next2(
                    new Error('Error trying to find bets with userId "'
                      + userId + '"'));
                }

                bets = instances;

                return next2();
            });
          },
          function getUser(next2) {
            User
              .findOne(userId, { fields: ['id', 'score'] })
              .exec(function (err, instance) {
                if(err) {
                  return next2(err);
                }

                if(!instance) {
                  return next2(
                    new Error('Error trying to find user "' + userId + '"'));
                }

                user = instance;

                return next2();
            });
          }
        ], next);
      },
      function sumBetsScores(next) {
        var score = _.sum(_.pluck(bets, 'score'));

        return next(null, score);
      },
      function updateUser(score, next) {
        user.score = score;

        user
          .save()
          .exec(function (err, instance) {
            if(err) {
              return next(err);
            }

            if(!instance) {
              return next(
                new Error('Error trying to update user "'
                  + userId + '" with score "' + score + '"'));
            }

            return next();
        });
      }
    ], cb);
  },

  // computeGroup
  //
  // @description :: Compute a group score attribute from its users score
  // @param       :: groupId (required): the group concerned
  //                 cb (required): the function called when it's done or an
  //                 error occured
  computeGroup: function (groupId, cb) {
    var users = [],
        group = {};

    if(!groupId || !cb) {
      throw new Error('Missing param');
    }
    if(!groupId.length || typeof cb !== 'function') {
      throw new Error('Invalid param');
    }

    // 1. In parallel,
    //    - Find all users related to this groupId
    //    - Get this group informations (to check it exists)
    // 2. Sum the users' scores
    // 3. Save it
    async.waterfall([
      function findUsersAndGetGroup(next) {
        async.parallel([
          function findUsers(next2) {
            Membership
              .find({ group: groupId }, { fields: ['id'] })
              .populate('user')
              .limit(0)
              .exec(function (err, instances) {
                if(err) {
                  return next2(err);
                }

                if(!instances) {
                  return next2(
                    new Error('Error trying to find memberships with groupId "'
                      + groupId + '"'));
                }

                users = _.pluck(instances, 'user');

                return next2();
            });
          },
          function getGroup(next2) {
            Group
              .findOne(groupId, { fields: ['id', 'score'] })
              .exec(function (err, instance) {
                if(err) {
                  return next2(err);
                }

                if(!instance) {
                  return next2('Error trying to find group "' + groupId + '"');
                }

                group = instance;

                return next2();
            });
          }
        ], next);
      },
      function sumUsersScores(next) {
        var score = _.sum(_.pluck(users, 'score'));

        return next(null, score);
      },
      function updateGroup(score, next) {
        group.score = score;

        group
          .save()
          .exec(function (err, instance) {
            if(err) {
              return next(err);
            }

            if(!instance) {
              return next(
                new Error('Error trying to update group "'
                  + groupId + '" with score "' + score + '"'));
            }

            return next();
        });
      }
    ], cb);
  },

  // computeUsers
  //
  // @description :: Compute a list of users' score attribute from his bets
  // @param       :: usersIds (required): the users concerned
  //                 cb (required): the function called when it's done or
  //                 an error occured
  computeUsers: function (usersIds, cb) {
    if(!usersIds || !cb) {
      throw new Error('Missing param');
    }
    if(!usersIds.length || typeof cb !== 'function') {
      throw new Error('Invalid param');
    }

    async.each(usersIds, function (userId, next) {
      this.computeUser(userId, next);
    }, cb);
  },

  // computeGroups
  //
  // @description :: Compute a list of groups' score attribute from its users
  //                 core
  // @param       :: groupsIds (required): the groups concerned
  //                 cb (required): the function called when it's done or an
  //                 error occured
  computeGroups: function (groupsIds, cb) {
    if(!groupsIds || !cb) {
      throw new Error('Missing param');
    }
    if(!groupsIds.length || typeof cb !== 'function') {
      throw new Error('Invalid param');
    }

    async.each(groupsIds, function (groupId, next) {
      this.computeGroup(groupId, next);
    }, cb);
  }

};
<|endoftext|>


<commit_before>module.exports = function (req, username, password, next) {
  sails.log('using basic strategy for user', username, ', password', password);
  User.findOne({ username: username })
    .populate('passports')
    .then(function (user) {
      sails.log('found user', user.username);
      if (!user) {
        return next(null, false);
      }

      var local = _.find(user.passports, {
        protocol: 'local',
        user: user.id
      });

      sails.log('found passport', local.id);
      sails.log('validating password...');

      local.validatePassword(password, function(err, res) {
        sails.log('passport validation complete');
        if (err) {
          return next(err);
        }
        next (null, res ? user : false);
      });
    })
    .catch(next);
};
<commit_msg>removed logs<commit_after>module.exports = function (req, username, password, next) {
  sails.log('using basic auth strategy for user', username, ', password', password);
  User.findOne({ username: username })
    .populate('passports')
    .then(function (user) {
      if (!user) {
        return next(null, false);
      }

      var local = _.find(user.passports, {
        protocol: 'local',
        user: user.id
      });

      local.validatePassword(password, function(err, res) {
        if (err) {
          return next(err);
        }
        next (null, res ? user : false);
      });
    })
    .catch(next);
};
<|endoftext|>


<commit_before>module.exports = Zeppelin.CollectionView.extend({
  tagName: 'ol',

  className: 'cards-list list-unstyled clearfix',

  subscriptions: {
    'cardsList:layout': 'triggerLayout'
  },

  addMethod: 'prepend',

  itemView: function(model) {
    return require('account/views/' + model.get('type'));
  },

  collection: function() {
    return App.Cards;
  },

  initialize: function() {
    _.bindAll(this, ['layout']);
  },

  onRenderItems: function() {
    this.triggerLayout();
  },

  onPrependItem: function() {
    if (!this.isFirstCollectionRender()) this.triggerLayout();
  },

  triggerLayout: function() {
    _.delay(this.layout, 1);
  },

  layout: function() {
    if (this.collection.isEmpty()) return this;
    if (this.$list.data('masonry')) this.$list.masonry('destroy');

    this.$list.masonry({
      gutter: 15,
      itemSelector: 'li.card'
    });

    return this;
  }
});

<commit_msg>Clear the layout delay timer when unplugging the cards list view.<commit_after>module.exports = Zeppelin.CollectionView.extend({
  tagName: 'ol',

  className: 'cards-list list-unstyled clearfix',

  subscriptions: {
    'cardsList:layout': 'triggerLayout'
  },

  addMethod: 'prepend',

  layoutTimer: null,

  itemView: function(model) {
    return require('account/views/' + model.get('type'));
  },

  collection: function() {
    return App.Cards;
  },

  initialize: function() {
    _.bindAll(this, ['layout']);
  },

  triggerLayout: function() {
    this.layoutTimer = _.delay(this.layout, 1);
  },

  layout: function() {
    if (this.collection.isEmpty()) return this;
    if (this.$list.data('masonry')) this.$list.masonry('destroy');

    this.$list.masonry({
      gutter: 15,
      itemSelector: 'li.card'
    });

    return this;
  },

  onRenderItems: function() {
    this.triggerLayout();
  },

  onPrependItem: function() {
    if (!this.isFirstCollectionRender()) this.triggerLayout();
  },

  onUnplug: function() {
    clearTimeout(this.layoutTimer);
    if (this.$list.data('masonry')) this.$list.masonry('destroy');
  }
});

<|endoftext|>


<commit_before>/*

 Copyright (c) 2017 Jirtdan Team and other collaborators

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 */

"use strict";
/*jshint esversion: 6*/

export const DEFAULT_STROKE_COLOR = "#000";
export const DEFAULT_FILL_COLOR = "#0000ff";
export const DEFAULT_STROKE_WIDTH = 2;
export const DEFAULT_SIGNAL_PRESENCE_COLOR = "#fa0";
export const POWER_STATE_HIGH = 1;
export const POWER_STATE_LOW = 0;
export const DEBUG = true;

const LOGTAG = "BaseControl";
/**
 * BaseControl is the super-class of all logic controls.
 * The BaseControl class provides basic functionality that all
 * controls should have:
 * - drag-drop
 * - focus
 * - manage the input/output pins
 *
 * All classes that extends BaseControl should override the initControl()
 * method.
 */
//TODO: remove the direct dependency from raphael
export class BaseControl {

    constructor(board) {
        this.board = board;
        this.paper = board.paper;
        this.shapes = [];
        this.inPins = [];
        this.outPins = [];
        this.glow = null;
        this.isComponentSelected = false;
        this.draw();
        this.initControl();
    }

    /**
     * This method needs to be overridden in subclass.
     * It is aim is to draw the component.
     */
    draw() {
        // Empty
    }

    /**
     * This method needs to be overridden in subclass.
     */
    initControl() {
        // Empty
    }

    /**
     * An event fired when element is selected.
     * @param event
     */
    onSelect(event) {
        console.log("Selected");
    }

    unselect() {
        this.isComponentSelected = false;
        if (this.glow == null) return;
        for (let i = 0; i < this.glow.length; i++) {
            this.glow[i].remove();
            this.glow[i] = null;
        }
        this.glow = null;
    }

    isSelected() {
        return this.isComponentSelected;
    }

    /**
     * Fired on dragging start.
     * Basically it is almost same as mouse-down.
     */
    onDragStart() {
        this.oldX = 0;
        this.oldY = 0;
        // Bring to front the selected element.
        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].toFront();
        }
    }

    /**
     * Fired while dragging.
     * @param dx
     * @param dy
     */
    onDrag(dx, dy) {
        const x = dx - this.oldX;
        const y = dy - this.oldY;

        if (this.glow != null) {
            this.glow.translate(x, y);
        }

        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].translate(x, y);
        }
        for (let i = 0; i < this.inPins.length; i++) {
            this.inPins[i].translate(x, y);
            this.board.translateConnections(this.inPins[i], x, y);
        }

        for (let i = 0; i < this.outPins.length; i++) {
            this.outPins[i].translate(x, y);
            this.board.translateConnections(this.outPins[i], x, y);
        }

        this.oldX = dx;
        this.oldY = dy;
    }

    /**
     * Due to bug in raphael gradient is lost after transformations.
     * We do some workaround to bypass the issue.
     * @param obj
     * @param newGradient
     */
    changeGradient(obj, newGradient) {
        const tr = obj.transform();
        obj.transform("");
        obj.attr("fill", newGradient);
        obj.transform(tr);
    }

    /**
     * Set the shapes to be drawn to represent the current control visually.
     * @param {Array} shapes array of shapes.
     */
    setShapes(shapes) {
        this.shapes = shapes;

        const obj = this.paper.set(this.shapes);

        const _this = this;

        obj.drag(
            function (dx, dy) {
                _this.onDrag(dx, dy);
            }, function () {
                _this.onDragStart();
            });

        obj.mousedown(function (event) {
            if (!event.ctrlKey) {
                _this.board.unselect();
            }
            _this.isComponentSelected = true;
            _this.onSelect(event);
        });
    }

    /**
     * Add input pin objects into this control.
     * @param {...T} pins list of input pin objects as arguments.
     */
    addInputPins(pins) {
        for (let i = 0; i < arguments.length; i++) {
            this.inPins.push(arguments[i]);
        }
    }

    /**
     * Add output pin objects into this control.
     * @param {...T} pins list of output pin objects as arguments.
     */
    addOutputPins(pins) {
        for (let i = 0; i < arguments.length; i++) {
            this.outPins.push(arguments[i]);
        }
    }

    getPins() {
        let pins = [];
        pins = this.inPins.concat(this.outPins);
        return pins;
    }

    deleteControl() {
        this.unselect();
        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].remove();
        }
        this.shapes = null;

        for (let i = 0; i < this.inPins.length; i++) {
            this.inPins[i].deletePin();
        }
        this.inPins = null;

        for (let i = 0; i < this.outPins.length; i++) {
            this.outPins[i].deletePin();
        }
        this.outPins = null;
    }
}<commit_msg>feat: [BaseControl] Provide isWithinArea() API<commit_after>/*

 Copyright (c) 2017 Jirtdan Team and other collaborators

 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
 in the Software without restriction, including without limitation the rights
 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the Software is
 furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all
 copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.

 */

"use strict";
/*jshint esversion: 6*/

export const DEFAULT_STROKE_COLOR = "#000";
export const DEFAULT_FILL_COLOR = "#0000ff";
export const DEFAULT_STROKE_WIDTH = 2;
export const DEFAULT_SIGNAL_PRESENCE_COLOR = "#fa0";
export const POWER_STATE_HIGH = 1;
export const POWER_STATE_LOW = 0;
export const DEBUG = true;

const LOGTAG = "BaseControl";
/**
 * BaseControl is the super-class of all logic controls.
 * The BaseControl class provides basic functionality that all
 * controls should have:
 * - drag-drop
 * - focus
 * - manage the input/output pins
 *
 * All classes that extends BaseControl should override the initControl()
 * method.
 */
//TODO: remove the direct dependency from raphael
export class BaseControl {

    constructor(board) {
        this.board = board;
        this.paper = board.paper;
        this.shapes = [];
        this.inPins = [];
        this.outPins = [];
        this.glow = null;
        this.isComponentSelected = false;
        this.draw();
        this.initControl();
    }

    /**
     * This method needs to be overridden in subclass.
     * It is aim is to draw the component.
     */
    draw() {
        // Empty
    }

    /**
     * This method needs to be overridden in subclass.
     */
    initControl() {
        // Empty
    }

    /**
     * An event fired when element is selected.
     * @param event
     */
    onSelect(event) {
        console.log("Selected");
    }

    unselect() {
        this.isComponentSelected = false;
        if (this.glow == null) return;
        for (let i = 0; i < this.glow.length; i++) {
            this.glow[i].remove();
            this.glow[i] = null;
        }
        this.glow = null;
    }

    isSelected() {
        return this.isComponentSelected;
    }

    /**
     * Fired on dragging start.
     * Basically it is almost same as mouse-down.
     */
    onDragStart() {
        this.oldX = 0;
        this.oldY = 0;
        // Bring to front the selected element.
        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].toFront();
        }
    }

    /**
     * Fired while dragging.
     * @param dx
     * @param dy
     */
    onDrag(dx, dy) {
        const x = dx - this.oldX;
        const y = dy - this.oldY;

        if (this.glow != null) {
            this.glow.translate(x, y);
        }

        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].translate(x, y);
        }
        for (let i = 0; i < this.inPins.length; i++) {
            this.inPins[i].translate(x, y);
            this.board.translateConnections(this.inPins[i], x, y);
        }

        for (let i = 0; i < this.outPins.length; i++) {
            this.outPins[i].translate(x, y);
            this.board.translateConnections(this.outPins[i], x, y);
        }

        this.oldX = dx;
        this.oldY = dy;
    }

    /**
     * Due to bug in raphael gradient is lost after transformations.
     * We do some workaround to bypass the issue.
     * @param obj
     * @param newGradient
     */
    changeGradient(obj, newGradient) {
        const tr = obj.transform();
        obj.transform("");
        obj.attr("fill", newGradient);
        obj.transform(tr);
    }

    /**
     * Set the shapes to be drawn to represent the current control visually.
     * @param {Array} shapes array of shapes.
     */
    setShapes(shapes) {
        this.shapes = shapes;

        const obj = this.paper.set(this.shapes);

        const _this = this;

        obj.drag(
            function (dx, dy) {
                _this.onDrag(dx, dy);
            }, function () {
                _this.onDragStart();
            });

        obj.mousedown(function (event) {
            if (!event.ctrlKey) {
                _this.board.unselect();
            }
            _this.isComponentSelected = true;
            _this.onSelect(event);
        });
    }

    /**
     * Add input pin objects into this control.
     * @param {...T} pins list of input pin objects as arguments.
     */
    addInputPins(pins) {
        for (let i = 0; i < arguments.length; i++) {
            this.inPins.push(arguments[i]);
        }
    }

    /**
     * Add output pin objects into this control.
     * @param {...T} pins list of output pin objects as arguments.
     */
    addOutputPins(pins) {
        for (let i = 0; i < arguments.length; i++) {
            this.outPins.push(arguments[i]);
        }
    }

    getPins() {
        let pins = [];
        pins = this.inPins.concat(this.outPins);
        return pins;
    }

    deleteControl() {
        this.unselect();
        for (let i = 0; i < this.shapes.length; i++) {
            this.shapes[i].remove();
        }
        this.shapes = null;

        for (let i = 0; i < this.inPins.length; i++) {
            this.inPins[i].deletePin();
        }
        this.inPins = null;

        for (let i = 0; i < this.outPins.length; i++) {
            this.outPins[i].deletePin();
        }
        this.outPins = null;
    }

    /**
     * Find out if element is within
     * the boundaries of given area.
     *
     * x,y--------------x2,y
     * |///////////////////|
     * |///////////////////|
     * |///////////////////|
     * x,y2-------------x2,y2
     */
    isWithinArea(x, y, x2, y2) {
        for (let i = 0; i < this.shapes.length; i++) {
            let position = this.shapes[i].getBBox();

            if ((position.x < x || position.x2 > x2) ||
                (position.y < y || position.y2 > y2)) {
                return false;
            }
        }

        return true;
    }
}<|endoftext|>


<commit_before>	function fight() {}

fight.prototype = {
  create: function() {
  	console.log(game.state.current)

  	// Enemy Bullets
    enemyBullets = game.add.group();
    enemyBullets.enableBody = true;
    enemyBullets.physicsBodyType = Phaser.Physics.ARCADE;
    enemyBullets.createMultiple(30, 'enemyBullet');
    enemyBullets.setAll('anchor.x', 0.5);
    enemyBullets.setAll('anchor.y', 1);
    enemyBullets.setAll('outOfBoundsKill', true);
    enemyBullets.setAll('checkWorldBounds', true);

  	// Logic to only scatter aliens once, called in fight - update loop
  	gameUI.alienScatterEnabled = true

	  game.physics.startSystem(Phaser.Physics.ARCADE)

	  // scrolling tile sprite
    var planet = gameState.planetBg = game.add.tileSprite(0, 200, 1200, 600, 'planet_bg')

	  // Ceiling visible line and collision detector
	  gameUI.gameAreaCeilingLine = new Phaser.Rectangle(0,200, 1200, 1)
	  gameUI.gameAreaCeiling = game.add.sprite(0,200,null)
	  game.physics.enable(gameUI.gameAreaCeiling, Phaser.Physics.ARCADE)
	  gameUI.gameAreaCeiling.body.setSize(1200, 1, 0, 0)
	  gameUI.gameAreaCeiling.body.immovable = true

	  // rain
	  rain.create(game)

	  // playerBullets
	  playerBullets = gameState.groups.playerBullets = game.add.group()
	  playerBullets.enableBody = true
	  playerBullets.physicsBodyType = Phaser.Physics.ARCADE
	   // All 100 of them
	  playerBullets.createMultiple(250, 'bullet')

	  playerBullets.setAll('anchor.x', 0.5)
	  playerBullets.setAll('anchor.y', 0.5)
	  playerBullets.setAll('damage', 5)

	  bossAlienBullets = gameState.groups.bossAlienBullets = game.add.group()
	  bossAlienBullets.enableBody = true
	  bossAlienBullets.physicsBodyType = Phaser.Physics.ARCADE
	   // All 100 of them
	  bossAlienBullets.createMultiple(250, 'bullet')

	  bossAlienBullets.setAll('anchor.x', 0.5)
	  bossAlienBullets.setAll('anchor.y', 0.5)
	  bossAlienBullets.setAll('damage', 50)

	  // create player object
	  var player = gameState.player = game.add.sprite(32, game.world.height - 150, 'dude')
	  game.physics.arcade.enable(player)
	  player.body.bounce.y = 0.2
	  player.body.collideWorldBounds = true
	  player.health = 100
	  gameUI.upgradeGun(gameState.questionsCorrect)

	  // create explosions
	  var explosions = gameState.groups.explosions = game.add.group()
    explosions.createMultiple(21, 'explosion')
    explosions.forEach(setUpExplosion)

    function setUpExplosion(explode){
    	explode.animations.add('explosion')
    }

	  // create regular aliens
	  aliens = gameState.groups.aliens = game.add.group()
	  aliens.enableBody = true
	  aliens.physicsBodyType = Phaser.Physics.ARCADE

	  // create boss alien
	  bossAlien = gameState.bossAlien = game.add.sprite(1500, 350, 'diamond')
	  game.physics.arcade.enable(bossAlien)
	  bossAlien.enableBody = true
	  bossAlien.physicsBodyType = Phaser.Physics.ARCADE
	  bossAlien.body.bounce.x = .9
	  bossAlien.body.bounce.y = .9
	  var scale = gameUI.alienBossScale(gameState.questionsCorrect)
	  bossAlien.scale.setTo(scale,scale)
	  bossAlien.anchor.x = 0.5
	  bossAlien.anchor.y = 0.5

	  // create keyboard listeners
	  gameState.cursors = game.input.keyboard.createCursorKeys()

	  // create text fields ------------------------------------------------
	  gameUI.scoreObject = game.add.text(
	    32, 32, // x coord, y coord
	    'Score: ' + gameUI.score, // text field
	    {fontSize: '32px', fill: '#ffffff'} // text styling
	    )

	  gameUI.spawnAliens()
  },
  update: function() {
  	// scroll speed of background:
  		gameState.planetBg.tilePosition.x -= 1

  	 	var player = gameState.player
		  var cursors = gameState.cursors
		  var shootThePlayerGun = false
		  var shootTheBossAlienGun = false

		  // Player cannot pass through the game boundaries
		  game.physics.arcade.collide(player, gameUI.gameAreaCeiling)
		  // Player cannot pass through the game boundaries
		  game.physics.arcade.collide(gameState.bossAlien, gameUI.gameAreaCeiling)
		  // aliens cannot pass through game boundaries
		  game.physics.arcade.collide(gameState.groups.aliens, gameUI.gameAreaCeiling)
		  // boss alien cannot pass through game boundaries
		  game.physics.arcade.collide(gameState.bossAlien, gameUI.gameAreaCeiling)
		  // aliens bounce off of each other
		  game.physics.arcade.collide(gameState.groups.aliens, gameState.groups.aliens)
		  // aliens bounce off of boss alien
		  game.physics.arcade.collide(gameState.groups.aliens, gameState.bossAlien)

		  // Bullets cause damage to aliens and then disappear
		  game.physics.arcade.overlap(gameState.groups.playerBullets, gameState.groups.aliens, gameUI.hitAlien , null, this)
		  // Bullets cause the boss alien to shrink
		  game.physics.arcade.overlap(gameState.groups.playerBullets, gameState.bossAlien, gameUI.shrinkBoss , null, this)
		  // Aliens damage the player when they touch
		  game.physics.arcade.overlap(player, gameState.groups.aliens, gameUI.hitPlayer, null, this)
		  // Boss alien bullets damage player
		  game.physics.arcade.overlap(player, gameState.groups.bossAlienBullets, gameUI.hitPlayer, null, this)

		    if (cursors.left.isDown) {
		      // player UP
		      player.body.velocity.y = -250;
		      // player.animations.play('up')
		    } else if (cursors.right.isDown){
		      // player DOWN
		      player.body.velocity.y = 250;
		      // player.animations.play('down')
		    } else {
		      // player stand still
		      player.animations.stop()
		      player.frame = 4
		      player.body.velocity.y = 0
		    }

		  // fire!
		  gameUI.firePlayerGunCounter += 1
		  if (gameUI.firePlayerGunCounter >= gameUI.firePlayerGunRate){
		    shootThePlayerGun = true
		    gameUI.firePlayerGunCounter = 0
		  }
		  if (shootThePlayerGun) {
		    gameUI.firePlayerBullet()
		  }

		  // gameState.bossAlien.rotation = game.physics.arcade.angleBetween(gameState.bossAlien, gameState.player)
		  gameUI.fireBossAlienGunCounter += 1
		  if (gameUI.fireBossAlienGunCounter >= gameUI.fireBossAlienGunRate){
		    shootTheBossAlienGun = true
		    gameUI.fireBossAlienGunCounter = 0
		  }

		  if (shootTheBossAlienGun && gameState.bossAlien.alive) {
		    gameUI.fireBossAlienBullet()
		  }

		  if (gameUI.aliensDead()) {
		    gameState.currentLevel++
		    gameState.firstTimeOnLevel = true
		    game.state.start('level_intro')
		  }

		  if (gameUI.playerDead(gameState.player)){
		  	gameState.lifes -= 1
		  	if (gameState.lifes <= 0){
		  		console.log('game over')
		  		// gameState.firstTimeOnLevel = false
		  		// game.state.start('game_over')
		  	} else {
		  		gameState.firstTimeOnLevel = false
		    	game.state.start('level_intro')
		  	}
		  }

		  if (gameState.groups.aliens.x < 850){
		    if (gameUI.alienScatterEnabled) {
		    	gameUI.scatterAliens()
		    	gameUI.sendBossAlien()
		    }
		  }

		  if (gameState.bossAlien.x < 800 ){
		  	gameUI.bossInGameArea = true
		  	gameState.bossAlien.body.collideWorldBounds = true
		  	gameState.bossAlien.body.velocity.x = 30
		  	gameState.bossAlien.body.velocity.y = gameUI.getRandomInt(0, 30)
		  }

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x === 0) {
		  		alien.body.velocity.x = 50}
		  })

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x  < 100.5 & alien.body.x  > 99.5  ) {
		  		alien.body.velocity.x = 0}
		  })

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x <101 && alien.body.x >99 ) gameUI.kamikazeCounter++
		  	// if (alien.body.x <= 10  &&  alien.body.velocity.x > 0) alien.velocity.x = 200
		  })

		  if (gameUI.kamikazeCounter > 1) {
		  	gameState.groups.aliens.forEach(function(alien){
		  		if (alien.body.x <101 && alien.body.x >99)  {
		  			alien.body.velocity.x = -300
		  			gameUI.kamikazeCounter = 0
		  		}
		  	})

		  	console.log("kamikaze")
		  }
		  gameUI.kamikazeCounter = 0

  },
  render: function() {
    game.debug.geom(gameUI.gameAreaCeilingLine,'#FFFFFF')
  },
}
<commit_msg>boss doesn't shoot unless he's in bounds<commit_after>	function fight() {}

fight.prototype = {
  create: function() {
  	console.log(game.state.current)

  	// Enemy Bullets
    enemyBullets = game.add.group();
    enemyBullets.enableBody = true;
    enemyBullets.physicsBodyType = Phaser.Physics.ARCADE;
    enemyBullets.createMultiple(30, 'enemyBullet');
    enemyBullets.setAll('anchor.x', 0.5);
    enemyBullets.setAll('anchor.y', 1);
    enemyBullets.setAll('outOfBoundsKill', true);
    enemyBullets.setAll('checkWorldBounds', true);

  	// Logic to only scatter aliens once, called in fight - update loop
  	gameUI.alienScatterEnabled = true

	  game.physics.startSystem(Phaser.Physics.ARCADE)

	  // scrolling tile sprite
    var planet = gameState.planetBg = game.add.tileSprite(0, 200, 1200, 600, 'planet_bg')

	  // Ceiling visible line and collision detector
	  gameUI.gameAreaCeilingLine = new Phaser.Rectangle(0,200, 1200, 1)
	  gameUI.gameAreaCeiling = game.add.sprite(0,200,null)
	  game.physics.enable(gameUI.gameAreaCeiling, Phaser.Physics.ARCADE)
	  gameUI.gameAreaCeiling.body.setSize(1200, 1, 0, 0)
	  gameUI.gameAreaCeiling.body.immovable = true

	  // rain
	  rain.create(game)

	  // playerBullets
	  playerBullets = gameState.groups.playerBullets = game.add.group()
	  playerBullets.enableBody = true
	  playerBullets.physicsBodyType = Phaser.Physics.ARCADE
	   // All 100 of them
	  playerBullets.createMultiple(250, 'bullet')

	  playerBullets.setAll('anchor.x', 0.5)
	  playerBullets.setAll('anchor.y', 0.5)
	  playerBullets.setAll('damage', 5)

	  bossAlienBullets = gameState.groups.bossAlienBullets = game.add.group()
	  bossAlienBullets.enableBody = true
	  bossAlienBullets.physicsBodyType = Phaser.Physics.ARCADE
	   // All 100 of them
	  bossAlienBullets.createMultiple(250, 'bullet')

	  bossAlienBullets.setAll('anchor.x', 0.5)
	  bossAlienBullets.setAll('anchor.y', 0.5)
	  bossAlienBullets.setAll('damage', 50)

	  // create player object
	  var player = gameState.player = game.add.sprite(32, game.world.height - 150, 'dude')
	  game.physics.arcade.enable(player)
	  player.body.bounce.y = 0.2
	  player.body.collideWorldBounds = true
	  player.health = 100
	  gameUI.upgradeGun(gameState.questionsCorrect)

	  // create explosions
	  var explosions = gameState.groups.explosions = game.add.group()
    explosions.createMultiple(21, 'explosion')
    explosions.forEach(setUpExplosion)

    function setUpExplosion(explode){
    	explode.animations.add('explosion')
    }

	  // create regular aliens
	  aliens = gameState.groups.aliens = game.add.group()
	  aliens.enableBody = true
	  aliens.physicsBodyType = Phaser.Physics.ARCADE

	  // create boss alien
	  bossAlien = gameState.bossAlien = game.add.sprite(1500, 350, 'diamond')
	  game.physics.arcade.enable(bossAlien)
	  bossAlien.enableBody = true
	  bossAlien.physicsBodyType = Phaser.Physics.ARCADE
	  bossAlien.body.bounce.x = .9
	  bossAlien.body.bounce.y = .9
	  var scale = gameUI.alienBossScale(gameState.questionsCorrect)
	  bossAlien.scale.setTo(scale,scale)
	  bossAlien.anchor.x = 0.5
	  bossAlien.anchor.y = 0.5

	  // create keyboard listeners
	  gameState.cursors = game.input.keyboard.createCursorKeys()

	  // create text fields ------------------------------------------------
	  gameUI.scoreObject = game.add.text(
	    32, 32, // x coord, y coord
	    'Score: ' + gameUI.score, // text field
	    {fontSize: '32px', fill: '#ffffff'} // text styling
	    )

	  gameUI.spawnAliens()
  },
  update: function() {
  	// scroll speed of background:
  		gameState.planetBg.tilePosition.x -= 1

  	 	var player = gameState.player
		  var cursors = gameState.cursors
		  var shootThePlayerGun = false
		  var shootTheBossAlienGun = false

		  // Player cannot pass through the game boundaries
		  game.physics.arcade.collide(player, gameUI.gameAreaCeiling)
		  // Player cannot pass through the game boundaries
		  game.physics.arcade.collide(gameState.bossAlien, gameUI.gameAreaCeiling)
		  // aliens cannot pass through game boundaries
		  game.physics.arcade.collide(gameState.groups.aliens, gameUI.gameAreaCeiling)
		  // boss alien cannot pass through game boundaries
		  game.physics.arcade.collide(gameState.bossAlien, gameUI.gameAreaCeiling)
		  // aliens bounce off of each other
		  game.physics.arcade.collide(gameState.groups.aliens, gameState.groups.aliens)
		  // aliens bounce off of boss alien
		  game.physics.arcade.collide(gameState.groups.aliens, gameState.bossAlien)

		  // Bullets cause damage to aliens and then disappear
		  game.physics.arcade.overlap(gameState.groups.playerBullets, gameState.groups.aliens, gameUI.hitAlien , null, this)
		  // Bullets cause the boss alien to shrink
		  game.physics.arcade.overlap(gameState.groups.playerBullets, gameState.bossAlien, gameUI.shrinkBoss , null, this)
		  // Aliens damage the player when they touch
		  game.physics.arcade.overlap(player, gameState.groups.aliens, gameUI.hitPlayer, null, this)
		  // Boss alien bullets damage player
		  game.physics.arcade.overlap(player, gameState.groups.bossAlienBullets, gameUI.hitPlayer, null, this)

		    if (cursors.left.isDown) {
		      // player UP
		      player.body.velocity.y = -250;
		      // player.animations.play('up')
		    } else if (cursors.right.isDown){
		      // player DOWN
		      player.body.velocity.y = 250;
		      // player.animations.play('down')
		    } else {
		      // player stand still
		      player.animations.stop()
		      player.frame = 4
		      player.body.velocity.y = 0
		    }

		  // fire!
		  gameUI.firePlayerGunCounter += 1
		  if (gameUI.firePlayerGunCounter >= gameUI.firePlayerGunRate){
		    shootThePlayerGun = true
		    gameUI.firePlayerGunCounter = 0
		  }
		  if (shootThePlayerGun) {
		    gameUI.firePlayerBullet()
		  }

		  // gameState.bossAlien.rotation = game.physics.arcade.angleBetween(gameState.bossAlien, gameState.player)
		  gameUI.fireBossAlienGunCounter += 1
		  if ((gameUI.fireBossAlienGunCounter >= gameUI.fireBossAlienGunRate) && gameState.bossAlien.inWorld){
		    shootTheBossAlienGun = true
		    gameUI.fireBossAlienGunCounter = 0
		  }

		  if (shootTheBossAlienGun && gameState.bossAlien.alive) {
		    gameUI.fireBossAlienBullet()
		  }

		  if (gameUI.aliensDead()) {
		    gameState.currentLevel++
		    gameState.firstTimeOnLevel = true
		    game.state.start('level_intro')
		  }

		  if (gameUI.playerDead(gameState.player)){
		  	gameState.lifes -= 1
		  	if (gameState.lifes <= 0){
		  		console.log('game over')
		  		// gameState.firstTimeOnLevel = false
		  		// game.state.start('game_over')
		  	} else {
		  		gameState.firstTimeOnLevel = false
		    	game.state.start('level_intro')
		  	}
		  }

		  if (gameState.groups.aliens.x < 850){
		    if (gameUI.alienScatterEnabled) {
		    	gameUI.scatterAliens()
		    	gameUI.sendBossAlien()
		    }
		  }

		  if (gameState.bossAlien.x < 800 ){
		  	gameUI.bossInGameArea = true
		  	gameState.bossAlien.body.collideWorldBounds = true
		  	gameState.bossAlien.body.velocity.x = 30
		  	gameState.bossAlien.body.velocity.y = gameUI.getRandomInt(0, 30)
		  }

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x === 0) {
		  		alien.body.velocity.x = 50}
		  })

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x  < 100.5 & alien.body.x  > 99.5  ) {
		  		alien.body.velocity.x = 0}
		  })

		  gameState.groups.aliens.forEach(function(alien){
		  	if (alien.body.x <101 && alien.body.x >99 ) gameUI.kamikazeCounter++
		  	// if (alien.body.x <= 10  &&  alien.body.velocity.x > 0) alien.velocity.x = 200
		  })

		  if (gameUI.kamikazeCounter > 1) {
		  	gameState.groups.aliens.forEach(function(alien){
		  		if (alien.body.x <101 && alien.body.x >99)  {
		  			alien.body.velocity.x = -300
		  			gameUI.kamikazeCounter = 0
		  		}
		  	})

		  	console.log("kamikaze")
		  }
		  gameUI.kamikazeCounter = 0

  },
  render: function() {
    game.debug.geom(gameUI.gameAreaCeilingLine,'#FFFFFF')
  },
}
<|endoftext|>


<commit_before>var KZ =  KZ || {};
KZ.arabicRegex = /[\u0600-\u06FF]/;

// https://gist.github.com/michelcmorel/6725279
// add html elements inside content editable area, credits Tim Down on stackoverflow.
// original post at:http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
function pasteHtmlAtCaret(html, selectPastedContent) {
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            // Range.createContextualFragment() would be useful here but is
            // only relatively recently standardized and is not supported in
            // some browsers (IE9, for one)
            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(), node, lastNode;
            while ( (node = el.firstChild) ) {
                lastNode = frag.appendChild(node);
            }
            var firstNode = frag.firstChild;
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                if (selectPastedContent) {
                    range.setStartBefore(firstNode);
                } else {
                    range.collapse(true);
                }
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        var range = sel.createRange();
        range.setEndPoint("StartToStart", originalRange);
        range.select();
    }
}

var keyPressHandler = function (e) {
    e = e || window.event;
    var charCode = e.which || e.keyCode;
    var charTyped = String.fromCharCode(charCode);

    // Autocorrect logic
    // -----------------

    // Commas (same logic for dashes)
    // ******************************
    // Synonyms are usually separated by commas. They should appear
    // in the order in which they are typed, i.e. left-to-right.
    // But by default, BiDi algorithm treats the list of synonyms
    // as a right-to-left sentence, and the words order is reversed.
    // To fix this, we insert a left-to-right mark before each comma character
    // in order to force a left-to-right word sequence.
    if (charTyped === ',' || charTyped === '-') {
        // Insert a left-to-right mark (U+200E) then the actual comma
        pasteHtmlAtCaret('', false);
    }

    // Index numbers
    // *************
    // Make sure index numbers (ex: '4.') following an arabic/right-to-left word
    // are treated as left-to-right elements.
    // Otherwise, the BiDi algorithm thinks it belongs to the right-to-left word and places it wrong.
    // Further reading: https://www.w3.org/International/articles/inline-bidi-markup/
    else if ( /^\d+$/.test(charTyped) ) { //0-9 only
        pasteHtmlAtCaret('', false);
    }

};

var keyUpHandler = function (e) {
    // If current font style is italic, reset it when starting a new paragraph
    if (e.keyCode === 13) {
        KZ.trixEditorElement.editor.deactivateAttribute("italic");
    }

    // if before the cursor there is a normal space
    // and more character before there is an arabic character
    // then replace the normal space by a non-breaking space
    // in order to avoid breaking arabic composed words

    var range = KZ.trixEditorElement.editor.getSelectedRange(); // returns a array of indexes, not a DOM Range

    // if there is no selection
    // and if there are at least 2 characters before the cursor
    // (one space and one arabic for the autocorrect to apply)
    if(range[0] > 1 && range[0] === range[1]) {
        var sel = window.getSelection();
        var originalDomRange = sel.getRangeAt(0);

        // The actual DOM range might be at the beginning of a node,
        // For instance right after inserting a dash '-' character inside a word.
        if (originalDomRange.startOffset > 1) {
            var extendedDomRange = document.createRange();
            extendedDomRange.setStart(originalDomRange.startContainer, originalDomRange.startOffset - 2);
            extendedDomRange.setEnd(originalDomRange.endContainer, originalDomRange.startOffset);

            var clonedSelection = extendedDomRange.cloneContents();
            var div = document.createElement('div');
            div.appendChild(clonedSelection);
            var content = div.innerText;

            var lastCharacterEntered = content[1];

            // If last character was a space, we may need to replace it by a non-breaking one.
            if(/\s/g.test(lastCharacterEntered)) {
                var characterEnteredBeforeSpace = content[0];

                if(KZ.arabicRegex.test(characterEnteredBeforeSpace)) {
                    KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);

                    // Use a narrow no-break space to separate word parts without indicating a word boundary.
                    KZ.trixEditorElement.editor.insertString("\u202F\u202F\u202F");
                }
            }

            // If last character was a simple dash, replace it by a long one
            else if (lastCharacterEntered === '-') {
                KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);
                KZ.trixEditorElement.editor.insertString("—");
            }

            // If last character was arabic, disable italic
            else if (KZ.arabicRegex.test(lastCharacterEntered)) {
                KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);
                KZ.trixEditorElement.editor.deactivateAttribute("italic");
                KZ.trixEditorElement.editor.setSelectedRange([range[0], range[0]]);
            }
        }
    }
};

// Display the transcription instructions on first use.
jQuery(document).ready(function() {
    if(!Cookies.get('transcriptionInstructionsDismissed')) {
        Cookies.set('transcriptionInstructionsDismissed', true);
        jQuery('#instructions-modal').modal();
    }

    KZ.trixEditor = $('trix-editor');
    KZ.trixEditorElement = KZ.trixEditor[0];

    KZ.trixEditor.on("keypress", keyPressHandler).on("keyup", keyUpHandler);

    // Make the editor toolbar float over the text
    // When the virtual keyboard is shown on iOS
    // In order to make sure the buttons are always accessible.
    var headerSize = 60;
    var trixToolbar;
    var scrollY;

    $(window).scroll(function() {
        if(!trixToolbar) {
            trixToolbar = $('trix-toolbar');
        }

        trixToolbar.hide();
        setTimeout(function() {
            trixToolbar.show();
            scrollY = window.scrollY;
            if(scrollY > headerSize) {
                trixToolbar.css('top', scrollY - headerSize);
                trixToolbar.addClass('floating');
            } else {
                trixToolbar.css('top', '0px');
                trixToolbar.removeClass('floating');
            }
        }, 1000);
    });
});
<commit_msg>Fixed regex typo<commit_after>var KZ =  KZ || {};
KZ.arabicRegex = /[\u0600-\u06FF]/;

// https://gist.github.com/michelcmorel/6725279
// add html elements inside content editable area, credits Tim Down on stackoverflow.
// original post at:http://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div
function pasteHtmlAtCaret(html, selectPastedContent) {
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            // Range.createContextualFragment() would be useful here but is
            // only relatively recently standardized and is not supported in
            // some browsers (IE9, for one)
            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(), node, lastNode;
            while ( (node = el.firstChild) ) {
                lastNode = frag.appendChild(node);
            }
            var firstNode = frag.firstChild;
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                if (selectPastedContent) {
                    range.setStartBefore(firstNode);
                } else {
                    range.collapse(true);
                }
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        var range = sel.createRange();
        range.setEndPoint("StartToStart", originalRange);
        range.select();
    }
}

var keyPressHandler = function (e) {
    e = e || window.event;
    var charCode = e.which || e.keyCode;
    var charTyped = String.fromCharCode(charCode);

    // Autocorrect logic
    // -----------------

    // Commas (same logic for dashes)
    // ******************************
    // Synonyms are usually separated by commas. They should appear
    // in the order in which they are typed, i.e. left-to-right.
    // But by default, BiDi algorithm treats the list of synonyms
    // as a right-to-left sentence, and the words order is reversed.
    // To fix this, we insert a left-to-right mark before each comma character
    // in order to force a left-to-right word sequence.
    if (charTyped === ',' || charTyped === '-') {
        // Insert a left-to-right mark (U+200E) then the actual comma
        pasteHtmlAtCaret('', false);
    }

    // Index numbers
    // *************
    // Make sure index numbers (ex: '4.') following an arabic/right-to-left word
    // are treated as left-to-right elements.
    // Otherwise, the BiDi algorithm thinks it belongs to the right-to-left word and places it wrong.
    // Further reading: https://www.w3.org/International/articles/inline-bidi-markup/
    else if ( /^\d$/.test(charTyped) ) { //0-9 only
        pasteHtmlAtCaret('', false);
    }

};

var keyUpHandler = function (e) {
    // If current font style is italic, reset it when starting a new paragraph
    if (e.keyCode === 13) {
        KZ.trixEditorElement.editor.deactivateAttribute("italic");
    }

    // if before the cursor there is a normal space
    // and more character before there is an arabic character
    // then replace the normal space by a non-breaking space
    // in order to avoid breaking arabic composed words

    var range = KZ.trixEditorElement.editor.getSelectedRange(); // returns a array of indexes, not a DOM Range

    // if there is no selection
    // and if there are at least 2 characters before the cursor
    // (one space and one arabic for the autocorrect to apply)
    if(range[0] > 1 && range[0] === range[1]) {
        var sel = window.getSelection();
        var originalDomRange = sel.getRangeAt(0);

        // The actual DOM range might be at the beginning of a node,
        // For instance right after inserting a dash '-' character inside a word.
        if (originalDomRange.startOffset > 1) {
            var extendedDomRange = document.createRange();
            extendedDomRange.setStart(originalDomRange.startContainer, originalDomRange.startOffset - 2);
            extendedDomRange.setEnd(originalDomRange.endContainer, originalDomRange.startOffset);

            var clonedSelection = extendedDomRange.cloneContents();
            var div = document.createElement('div');
            div.appendChild(clonedSelection);
            var content = div.innerText;

            var lastCharacterEntered = content[1];

            // If last character was a space, we may need to replace it by a non-breaking one.
            if(/\s/g.test(lastCharacterEntered)) {
                var characterEnteredBeforeSpace = content[0];

                if(KZ.arabicRegex.test(characterEnteredBeforeSpace)) {
                    KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);

                    // Use a narrow no-break space to separate word parts without indicating a word boundary.
                    KZ.trixEditorElement.editor.insertString("\u202F\u202F\u202F");
                }
            }

            // If last character was a simple dash, replace it by a long one
            else if (lastCharacterEntered === '-') {
                KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);
                KZ.trixEditorElement.editor.insertString("—");
            }

            // If last character was arabic, disable italic
            else if (KZ.arabicRegex.test(lastCharacterEntered)) {
                KZ.trixEditorElement.editor.setSelectedRange([range[0] - 1, range[0]]);
                KZ.trixEditorElement.editor.deactivateAttribute("italic");
                KZ.trixEditorElement.editor.setSelectedRange([range[0], range[0]]);
            }
        }
    }
};

// Display the transcription instructions on first use.
jQuery(document).ready(function() {
    if(!Cookies.get('transcriptionInstructionsDismissed')) {
        Cookies.set('transcriptionInstructionsDismissed', true);
        jQuery('#instructions-modal').modal();
    }

    KZ.trixEditor = $('trix-editor');
    KZ.trixEditorElement = KZ.trixEditor[0];

    KZ.trixEditor.on("keypress", keyPressHandler).on("keyup", keyUpHandler);

    // Make the editor toolbar float over the text
    // When the virtual keyboard is shown on iOS
    // In order to make sure the buttons are always accessible.
    var headerSize = 60;
    var trixToolbar;
    var scrollY;

    $(window).scroll(function() {
        if(!trixToolbar) {
            trixToolbar = $('trix-toolbar');
        }

        trixToolbar.hide();
        setTimeout(function() {
            trixToolbar.show();
            scrollY = window.scrollY;
            if(scrollY > headerSize) {
                trixToolbar.css('top', scrollY - headerSize);
                trixToolbar.addClass('floating');
            } else {
                trixToolbar.css('top', '0px');
                trixToolbar.removeClass('floating');
            }
        }, 1000);
    });
});
<|endoftext|>