<commit_before>/*
 Copyright 2017 Aleksandar Jankovic

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict';

const express = require('express');
const appConfig = require('../../config/defaults/config').app;
const databasePopulation = require('../../config/defaults/config').databasePopulation;
const jwt = require('jsonwebtoken');
const objectValidator = require('../../utils/objectValidator');
const dbCalibrationMission = require('../../db/connectors/calibrationMission');
const dbWallet = require('../../db/connectors/wallet');
const dbUser = require('../../db/connectors/user');
const dbTransaction = require('../../db/connectors/transaction');

const router = new express.Router();

/**
 * @param {Object} io Socket.io
 * @returns {Object} Router
 */
function handle(io) {
  /**
   * @api {get} /calibrationMissions Get all of user's active calibration missions
   * @apiVersion 5.1.0
   * @apiName GetCalibrationMissions
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {String} Authorization Your JSON Web Token
   *
   * @apiDescription Get all of user's active calibration missions
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.missions Missions found. Can be empty
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "missions": [{
   *        owner: 'raz',
   *        stationId: 1,
   *        code: 81855211
   *        completed: false,
   *      }],
   *    }
   *  }
   */
  router.get('/', (req, res) => {
    // noinspection JSUnresolvedVariable
    jwt.verify(req.headers.authorization || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      dbCalibrationMission.getActiveMission(decoded.data.userName, (err, activeMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        }

        const missions = [];

        if (activeMission) {
          missions.push(activeMission);
        }

        res.json({ data: { missions } });
      });
    });
  });

  /**
   * @api {post} /calibrationMissions/completeMission Set a mission to completed
   * @apiVersion 5.1.0
   * @apiName CompleteCalibrationMission
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {String} Authorization Your JSON Web Token
   *
   * @apiDescription Set a mission to completed
   *
   * @apiParam {Object} data
   * @apiParam {Object} data.mission Mission
   * @apiParam {String} data.mission.code Mission code (8 numbers)
   * @apiParam {String} data.mission.stationId Number of the station
   * @apiParamExample {json} Request-Example:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1
   *      }
   *    }
   *  }
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.mission Mission completed
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1,
   *        "completed": true,
   *        "timeCompleted": "2016-10-14T11:13:03.555Z"
   *      },
   *      "transaction": {
   *        "to": "raz",
   *        "from": "SYSTEM",
   *        "amount": 50
   *        "time": "2016-10-14T11:13:03.555Z"
   *      }
   *    }
   *  }
   */
  router.post('/completeMission', (req, res) => {
    if (!objectValidator.isValidData(req.body, { data: { mission: { code: true, stationId: true } } })) {
      res.status(400).json({
        errors: [{
          status: 400,
          title: 'Missing data',
          detail: 'Unable to parse data',
        }],
      });

      return;
    }

    // noinspection JSUnresolvedVariable
    const auth = req.headers.authorization;

    jwt.verify(auth || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded || decoded.data.accessLevel < databasePopulation.commands.updateCalibrationMission.accessLevel) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      const { code, stationId } = req.body.data.mission;

      dbCalibrationMission.setMissionCompleted(code, stationId, (err, completedMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        } else if (!completedMission) {
          res.status(404).json({
            errors: [{
              status: 404,
              title: 'Not found',
              detail: 'Mission not found',
            }],
          });

          return;
        }

        const transaction = {
          to: completedMission.owner,
          from: 'SYSTEM',
          amount: 50,
          time: new Date(),
          note: `CALIBRATION OF STATION ${completedMission.stationId}`,
        };

        dbTransaction.createTransaction(transaction, (transErr) => {
          if (transErr) {
            res.status(500).json({
              errors: [{
                status: 500,
                title: 'Internal Server Error',
                detail: 'Internal Server Error',
              }],
            });

            return;
          }

          dbWallet.increaseAmount(completedMission.owner, transaction.amount, (walletErr, increasedWallet) => {
            if (walletErr) {
              res.status(500).json({
                errors: [{
                  status: 500,
                  title: 'Internal Server Error',
                  detail: 'Internal Server Error',
                }],
              });

              return;
            }

            dbUser.getUserByAlias(transaction.to, (aliasErr, receiver) => {
              if (aliasErr) {
                return;
              }

              if (receiver.socketId !== '') {
                io.to(receiver.socketId).emit('transaction', { transaction, wallet: increasedWallet });
                io.to(receiver.socketId).emit('terminal', { mission: { missionType: 'calibrationMission', completed: true } });
              }
            });

            res.json({
              data: {
                mission: completedMission,
                transaction,
              },
            });
          });
        });
      });
    });
  });

  /**
   * @api {post} /calibrationMissions/cancelMission Cancel a calibration mission
   * @apiVersion 5.1.0
   * @apiName CancelCalibrationMission
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {String} Authorization Your JSON Web Token
   *
   * @apiDescription Cancel a calibration mission. Mission will still be set to completed, but user will receive no reward
   *
   * @apiParam {Object} data
   * @apiParam {Object} data.mission Mission
   * @apiParam {String} data.mission.code Mission code (8 numbers)
   * @apiParam {String} data.mission.stationId Number of the station
   * @apiParamExample {json} Request-Example:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1
   *      }
   *    }
   *  }
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.mission Mission completed
   * @apiSuccess {Boolean} data.cancelled Mission was cancelled
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1,
   *        "completed": true,
   *        "timeCompleted": "2016-10-14T11:13:03.555Z"
   *      },
   *      "cancelled": true
   *    }
   *  }
   */
  router.post('/cancelMission', (req, res) => {
    if (!objectValidator.isValidData(req.body, { data: { mission: { code: true, stationId: true } } })) {
      res.status(400).json({
        errors: [{
          status: 400,
          title: 'Missing data',
          detail: 'Unable to parse data',
        }],
      });

      return;
    }

    // noinspection JSUnresolvedVariable
    const auth = req.headers.authorization;

    jwt.verify(auth || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded || decoded.data.accessLevel < databasePopulation.commands.updateCalibrationMission.accessLevel) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      const { code, stationId } = req.body.data.mission;

      dbCalibrationMission.setMissionCompleted(code, stationId, (err, completedMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        } else if (!completedMission) {
          res.status(404).json({
            errors: [{
              status: 404,
              title: 'Not found',
              detail: 'Mission not found',
            }],
          });

          return;
        }

        dbUser.getUserByAlias(completedMission.owner, (aliasErr, user) => {
          if (aliasErr) {
            return;
          }

          if (user.socketId !== '') {
            io.to(user.socketId).emit('terminal', { mission: { missionType: 'calibrationMission', cancelled: true } });
          }
        });

        res.json({ data: { mission: completedMission, cancelled: true } });
      });
    });
  });

  return router;
}

module.exports = handle;
<commit_msg>Mission completeion and failed got a better structure<commit_after>/*
 Copyright 2017 Aleksandar Jankovic

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

'use strict';

const express = require('express');
const appConfig = require('../../config/defaults/config').app;
const databasePopulation = require('../../config/defaults/config').databasePopulation;
const jwt = require('jsonwebtoken');
const objectValidator = require('../../utils/objectValidator');
const dbCalibrationMission = require('../../db/connectors/calibrationMission');
const dbWallet = require('../../db/connectors/wallet');
const dbUser = require('../../db/connectors/user');
const dbTransaction = require('../../db/connectors/transaction');

const router = new express.Router();

/**
 * @param {Object} io Socket.io
 * @returns {Object} Router
 */
function handle(io) {
  /**
   * @api {get} /calibrationMissions Get all of user's active calibration missions
   * @apiVersion 5.1.0
   * @apiName GetCalibrationMissions
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {String} Authorization Your JSON Web Token
   *
   * @apiDescription Get all of user's active calibration missions
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.missions Missions found. Can be empty
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "missions": [{
   *        owner: 'raz',
   *        stationId: 1,
   *        code: 81855211
   *        completed: false,
   *      }],
   *    }
   *  }
   */
  router.get('/', (req, res) => {
    // noinspection JSUnresolvedVariable
    jwt.verify(req.headers.authorization || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      dbCalibrationMission.getActiveMission(decoded.data.userName, (err, activeMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        }

        const missions = [];

        if (activeMission) {
          missions.push(activeMission);
        }

        res.json({ data: { missions } });
      });
    });
  });

  /**
   * @api {put} /calibrationMissions/:id/complete Set a mission to completed
   * @apiVersion 5.1.0
   * @apiName CompleteCalibrationMission
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {string} Authorization Your JSON Web Token
   *
   * @apiDescription Set a mission to completed
   *
   * @apiParam {string} id Mission code
   *
   * @apiParam {Object} data
   * @apiParam {Object} data.mission Mission
   * @apiParam {string} data.mission.code Mission code (8 numbers)
   * @apiParam {string} data.mission.stationId Number of the station
   * @apiParamExample {json} Request-Example:
   *   {
   *    "data": {
   *      "mission": {
   *        "stationId": 1
   *      }
   *    }
   *  }
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.mission Mission completed
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1,
   *        "completed": true,
   *        "timeCompleted": "2016-10-14T11:13:03.555Z"
   *      },
   *      "transaction": {
   *        "to": "raz",
   *        "from": "SYSTEM",
   *        "amount": 50
   *        "time": "2016-10-14T11:13:03.555Z"
   *      }
   *    }
   *  }
   */
  router.put('/:id/complete', (req, res) => {
    if (!objectValidator.isValidData(req.body, { data: { mission: { stationId: true } } })) {
      res.status(400).json({
        errors: [{
          status: 400,
          title: 'Missing data',
          detail: 'Unable to parse data',
        }],
      });

      return;
    }

    // noinspection JSUnresolvedVariable
    const auth = req.headers.authorization;

    jwt.verify(auth || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded || decoded.data.accessLevel < databasePopulation.commands.updateCalibrationMission.accessLevel) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      const mission = req.body.data.mission;
      mission.code = req.params.id;

      dbCalibrationMission.setMissionCompleted(mission.code, mission.stationId, (err, completedMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        } else if (!completedMission) {
          res.status(404).json({
            errors: [{
              status: 404,
              title: 'Not found',
              detail: 'Mission not found',
            }],
          });

          return;
        }

        const transaction = {
          to: completedMission.owner,
          from: 'SYSTEM',
          amount: 50,
          time: new Date(),
          note: `CALIBRATION OF STATION ${completedMission.stationId}`,
        };

        dbTransaction.createTransaction(transaction, (transErr) => {
          if (transErr) {
            res.status(500).json({
              errors: [{
                status: 500,
                title: 'Internal Server Error',
                detail: 'Internal Server Error',
              }],
            });

            return;
          }

          dbWallet.increaseAmount(completedMission.owner, transaction.amount, (walletErr, increasedWallet) => {
            if (walletErr) {
              res.status(500).json({
                errors: [{
                  status: 500,
                  title: 'Internal Server Error',
                  detail: 'Internal Server Error',
                }],
              });

              return;
            }

            dbUser.getUserByAlias(transaction.to, (aliasErr, receiver) => {
              if (aliasErr) {
                return;
              }

              if (receiver.socketId !== '') {
                io.to(receiver.socketId).emit('transaction', { transaction, wallet: increasedWallet });
                io.to(receiver.socketId).emit('terminal', { mission: { missionType: 'calibrationMission', completed: true } });
              }
            });

            res.json({
              data: {
                mission: completedMission,
                transaction,
              },
            });
          });
        });
      });
    });
  });

  /**
   * @api {post} /calibrationMissions/:id/cancel Cancel a calibration mission
   * @apiVersion 5.1.0
   * @apiName CancelCalibrationMission
   * @apiGroup CalibrationMissions
   *
   * @apiHeader {String} Authorization Your JSON Web Token
   *
   * @apiDescription Cancel a calibration mission. Mission will still be set to completed, but user will receive no reward
   *
   * @apiParam {string} id Mission code
   *
   * @apiParam {Object} data
   * @apiParam {Object} data.mission Mission
   * @apiParam {String} data.mission.code Mission code (8 numbers)
   * @apiParam {String} data.mission.stationId Number of the station
   * @apiParamExample {json} Request-Example:
   *   {
   *    "data": {
   *      "mission": {
   *        "stationId": 1
   *      }
   *    }
   *  }
   *
   * @apiSuccess {Object} data
   * @apiSuccess {Object[]} data.mission Mission completed
   * @apiSuccess {Boolean} data.cancelled Mission was cancelled
   * @apiSuccessExample {json} Success-Response:
   *   {
   *    "data": {
   *      "mission": {
   *        "code": 12345678,
   *        "stationId": 1,
   *        "completed": true,
   *        "timeCompleted": "2016-10-14T11:13:03.555Z"
   *      },
   *      "cancelled": true
   *    }
   *  }
   */
  router.put('/:id/cancel', (req, res) => {
    if (!objectValidator.isValidData(req.body, { data: { mission: { stationId: true } } })) {
      res.status(400).json({
        errors: [{
          status: 400,
          title: 'Missing data',
          detail: 'Unable to parse data',
        }],
      });

      return;
    }

    // noinspection JSUnresolvedVariable
    const auth = req.headers.authorization;

    jwt.verify(auth || '', appConfig.jsonKey, (jwtErr, decoded) => {
      if (jwtErr) {
        res.status(500).json({
          errors: [{
            status: 500,
            title: 'Internal Server Error',
            detail: 'Internal Server Error',
          }],
        });

        return;
      } else if (!decoded || decoded.data.accessLevel < databasePopulation.commands.updateCalibrationMission.accessLevel) {
        res.status(401).json({
          errors: [{
            status: 401,
            title: 'Unauthorized',
            detail: 'Invalid token',
          }],
        });

        return;
      }

      const mission = req.body.data.mission;
      mission.code = req.params.id;

      dbCalibrationMission.setMissionCompleted(mission.code, mission.stationId, (err, completedMission) => {
        if (err) {
          res.status(500).json({
            errors: [{
              status: 500,
              title: 'Internal Server Error',
              detail: 'Internal Server Error',
            }],
          });

          return;
        } else if (!completedMission) {
          res.status(404).json({
            errors: [{
              status: 404,
              title: 'Not found',
              detail: 'Mission not found',
            }],
          });

          return;
        }

        dbUser.getUserByAlias(completedMission.owner, (aliasErr, user) => {
          if (aliasErr) {
            return;
          }

          if (user.socketId !== '') {
            io.to(user.socketId).emit('terminal', { mission: { missionType: 'calibrationMission', cancelled: true } });
          }
        });

        res.json({ data: { mission: completedMission, cancelled: true } });
      });
    });
  });

  return router;
}

module.exports = handle;
<|endoftext|>


<commit_before>/**
 * DataPicker component.
 */
((exports) => {
  class DataPicker {
    constructor(pickers) {
      this.modal = this._createModalContainer();
      this.modal.appendTo($("body"));
      this.current = null;

      pickers.each((_index, picker) => {
        this.activate(picker);
      });
    }

    activate(picker) {
      let $picker = $(picker);

      this._setCurrentPicker($picker, null);

      let input = "hidden",
          name = this.current.name,
          values = this.current.values;

      if (this.current.multiple) {
        name += "[]";
      }

      $("div", values).each((_index2, div) => {
        let value = $("a", div).data("picker-value");
        $(div).prepend($(`<input type="${input}" checked name="${name}" value="${value}"/>`));
      });

      $picker.on("click", "a", (event) => {
        event.preventDefault();
        if ($picker.hasClass("disabled")) {
          return;
        }
        const isMultiPicker = $picker.hasClass("picker-multiple");

        if ($(this._targetFromEvent(event)).hasClass("picker-prompt") || !isMultiPicker) {
          this._openPicker($picker, this._targetFromEvent(event));
        } else {
          this._removeValue($picker, this._targetFromEvent(event));
        }
      });

      $picker.on("click", "input", (event) => {
        this._removeValue($picker, this._targetFromEvent(event));
      });

      if (this.current.autosort) {
        this._sort();
      }
    }

    enabled(picker, value) {
      $(picker).toggleClass("disabled", !value);
      $("input", picker).attr("disabled", !value);
    }

    clear(picker) {
      $(".picker-values", picker).html("");
    }

    save(picker) {
      return $(".picker-values div:has(input:checked)", picker).map((_index, div) => {
        let $link = $("a", div);
        return {
          value: $("input", div).val(),
          text: $link.text(),
          url: $link.attr("href")
        };
      }).get();
    }

    load(picker, savedData) {
      this._setCurrentPicker($(picker), null);
      $.each(savedData, (_index, data) => {
        this._choose(data, { interactive: false, modify: false });
      });
      if (this.current.autosort) {
        this._sort();
      }
    }

    _createModalContainer() {
      // Add a header because we are referencing the title element with
      // `aria-labelledby`. If the title doesn't exist, the "labelled by"
      // reference is incorrect.
      const headerHtml = '

';

      return $(`

${headerHtml}

                <button class="close-button" data-close type="button" data-reveal-id="data_picker-modal">×</button>

`);
    }

    _openPicker($picker, target) {
      this._setCurrentPicker($picker, target);
      this._load($("a", target).attr("href"));
    }

    _setCurrentPicker($picker, target) {
      let $target = false;
      if (target && !$(target).hasClass("picker-prompt")) {
        $target = $(target);
      }

      this.current = {
        picker: $picker,
        name: $picker.data("picker-name"),
        values: $picker.find(".picker-values"),
        multiple: $picker.hasClass("picker-multiple"),
        autosort: $picker.hasClass("picker-multiple") && $picker.hasClass("picker-autosort"),
        target: $target
      };
    }

    _load(url) {
      $.ajax(url).done((resp) => {
        let modalContent = $(".data_picker-modal-content", this.modal);
        modalContent.html(resp);
        this._handleLinks(modalContent);
        this._handleCheckboxes(modalContent);
        this.modal.foundation("open");
      });
    }

    _handleLinks(content) {
      $("a", content).each((_index, link) => {
        let $link = $(link);
        $link.click((event) => {
          event.preventDefault();
          if ($link.data("data-close")) {
            return;
          }

          let chooseUrl = $link.attr("href");
          if (chooseUrl) {
            if (typeof $link.data("picker-choose") === "undefined") {
              this._load(chooseUrl);
            } else {
              this._choose(
                { url: chooseUrl, value: $link.data("picker-value") || "", text: $link.data("picker-text") || "" }
              );
            }
          }
        });
      });
    }

    _handleCheckboxes(content) {
      $("input[type=checkbox][data-picker-choose]", content).each((_index, checkbox) => {
        const $checkbox = $(checkbox);
        checkbox.checked = this._targetFromValue($checkbox.val()) !== null;
      }).change((event) => {
        const $checkbox = $(event.target);
        if (event.target.checked) {
          this._choose(
            { url: $checkbox.data("picker-url"), value: $checkbox.val() || "", text: $checkbox.data("picker-text") || "" },
            { modify: false, close: false }
          );
        }
        else {
          this._removeValue(this.current.picker, this._targetFromValue($checkbox.val()));
        }
      });
    }

    _choose(data, opts = {}) {
      const options = Object.assign({ interactive: true, modify: true, close: true }, opts);

      let dataText = this._escape(data.text);
      let choosenOption = null;

      if (!this.current.target && options.modify) {
        this.current.target = this._targetFromValue(data.value);
      }

      // Add or update value appearance
      if (this.current.target && options.modify) {
        let link = $("a", this.current.target);
        link.data("picker-value", data.value);
        link.attr("href", data.url);
        choosenOption = this.current.target;
        if (this.current.multiple) {
          link.html(`× ${dataText}`);
        } else {
          link.text(dataText);
        }
      } else {
        let input = "hidden",
            name = this.current.name;

        if (this.current.multiple) {
          name += "[]";
          choosenOption = $(`<div><input type="${input}" checked name="${name}"/><a href="${data.url}" data-picker-value="${data.value}" class="label primary">× ${dataText}</a></div>`);
        } else {
          choosenOption = $(`<div><input type="${input}" checked name="${name}"/><a href="${data.url}" data-picker-value="${data.value}">${dataText}</a></div>`);
        }
        choosenOption.appendTo(this.current.values);

        if (!this.current.target) {
          this.current.target = choosenOption;
        }
      }

      // Set input value
      let $input = $("input", choosenOption);
      $input.attr("value", data.value);

      if (this.current.autosort) {
        this._sort();
      }

      if (options.interactive) {
        // Raise changed event
        $input.trigger("change");
        this._removeErrors();

        if (options.close) {
          this._close();
        }
      }
    }

    _sort() {
      const values = $(".picker-values", this.current.picker);
      values.children().sort((item1, item2) => $("input", item1).val() - $("input", item2).val()).detach().appendTo(values);
    }

    _close() {
      // Close modal and unset target element
      this.modal.foundation("close");
      this.current.target = null;
    }

    _removeValue($picker, target) {
      if (target) {
        this._setCurrentPicker($picker, target);
        this.current.target.fadeOut(500, () => {
          this.current.target.remove();
          this.current.target = null;
        });
      }
    }

    _removeErrors() {
      let parent = this.current.picker.parent();
      $(".is-invalid-input", parent).removeClass("is-invalid-input");
      $(".is-invalid-label", parent).removeClass("is-invalid-label");
      $(".form-error.is-visible", parent).removeClass("is-visible");
    }

    _escape(str) {
      return str.replace(/[\u00A0-\u9999<>&]/gim, function (char) {
        return `&#${char.charCodeAt(0)};`;
      });
    }

    _targetFromEvent(event) {
      return event.target.parentNode;
    }

    _targetFromValue(value) {
      return $(`[data-picker-value=${value}]`, this.current.picker).parent()[0] || null;
    }
  }

  exports.Decidim = exports.Decidim || {};
  exports.Decidim.DataPicker = DataPicker;
})(window);
<commit_msg>Fix canceling scope select doesnt open reveal (#7805)<commit_after>/**
 * DataPicker component.
 */
((exports) => {
  class DataPicker {
    constructor(pickers) {
      this.modal = this._createModalContainer();
      this.modal.appendTo($("body"));
      this.current = null;

      pickers.each((_index, picker) => {
        this.activate(picker);
      });
    }

    activate(picker) {
      let $picker = $(picker);

      this._setCurrentPicker($picker, null);

      let input = "hidden",
          name = this.current.name,
          values = this.current.values;

      if (this.current.multiple) {
        name += "[]";
      }

      $("div", values).each((_index2, div) => {
        let value = $("a", div).data("picker-value");
        $(div).prepend($(`<input type="${input}" checked name="${name}" value="${value}"/>`));
      });

      $picker.on("click", "a", (event) => {
        event.preventDefault();
        if ($picker.hasClass("disabled")) {
          return;
        }
        const isMultiPicker = $picker.hasClass("picker-multiple");

        if ($(this._targetFromEvent(event)).hasClass("picker-prompt") || !isMultiPicker) {
          this._openPicker($picker, this._targetFromEvent(event));
        } else {
          this._removeValue($picker, this._targetFromEvent(event));
        }
      });

      $picker.on("click", "input", (event) => {
        this._removeValue($picker, this._targetFromEvent(event));
      });

      if (this.current.autosort) {
        this._sort();
      }
    }

    enabled(picker, value) {
      $(picker).toggleClass("disabled", !value);
      $("input", picker).attr("disabled", !value);
    }

    clear(picker) {
      $(".picker-values", picker).html("");
    }

    save(picker) {
      return $(".picker-values div:has(input:checked)", picker).map((_index, div) => {
        let $link = $("a", div);
        return {
          value: $("input", div).val(),
          text: $link.text(),
          url: $link.attr("href")
        };
      }).get();
    }

    load(picker, savedData) {
      this._setCurrentPicker($(picker), null);
      $.each(savedData, (_index, data) => {
        this._choose(data, { interactive: false, modify: false });
      });
      if (this.current.autosort) {
        this._sort();
      }
    }

    _createModalContainer() {
      // Add a header because we are referencing the title element with
      // `aria-labelledby`. If the title doesn't exist, the "labelled by"
      // reference is incorrect.
      const headerHtml = '

';

      return $(`

${headerHtml}

                <button class="close-button" data-close type="button" data-reveal-id="data_picker-modal">×</button>

`);
    }

    _openPicker($picker, target) {
      this._setCurrentPicker($picker, target);
      this._load($("a", target).attr("href"));
    }

    _setCurrentPicker($picker, target) {
      let $target = false;
      if (target && !$(target).hasClass("picker-prompt")) {
        $target = $(target);
      }

      this.current = {
        picker: $picker,
        name: $picker.data("picker-name"),
        values: $picker.find(".picker-values"),
        multiple: $picker.hasClass("picker-multiple"),
        autosort: $picker.hasClass("picker-multiple") && $picker.hasClass("picker-autosort"),
        target: $target
      };
    }

    _load(url) {
      $.ajax(url).done((resp) => {
        let modalContent = $(".data_picker-modal-content", this.modal);
        modalContent.html(resp);
        this._handleLinks(modalContent);
        this._handleCheckboxes(modalContent);
        this.modal.foundation("open");
      });
    }

    _handleLinks(content) {
      $("a", content).each((_index, link) => {
        let $link = $(link);
        $link.click((event) => {
          event.preventDefault();
          if ($link.data("close") || $link.data("close") === "") {
            return;
          }

          let chooseUrl = $link.attr("href");
          if (chooseUrl) {
            if (typeof $link.data("picker-choose") === "undefined") {
              this._load(chooseUrl);
            } else {
              this._choose(
                { url: chooseUrl, value: $link.data("picker-value") || "", text: $link.data("picker-text") || "" }
              );
            }
          }
        });
      });
    }

    _handleCheckboxes(content) {
      $("input[type=checkbox][data-picker-choose]", content).each((_index, checkbox) => {
        const $checkbox = $(checkbox);
        checkbox.checked = this._targetFromValue($checkbox.val()) !== null;
      }).change((event) => {
        const $checkbox = $(event.target);
        if (event.target.checked) {
          this._choose(
            { url: $checkbox.data("picker-url"), value: $checkbox.val() || "", text: $checkbox.data("picker-text") || "" },
            { modify: false, close: false }
          );
        }
        else {
          this._removeValue(this.current.picker, this._targetFromValue($checkbox.val()));
        }
      });
    }

    _choose(data, opts = {}) {
      const options = Object.assign({ interactive: true, modify: true, close: true }, opts);

      let dataText = this._escape(data.text);
      let choosenOption = null;

      if (!this.current.target && options.modify) {
        this.current.target = this._targetFromValue(data.value);
      }

      // Add or update value appearance
      if (this.current.target && options.modify) {
        let link = $("a", this.current.target);
        link.data("picker-value", data.value);
        link.attr("href", data.url);
        choosenOption = this.current.target;
        if (this.current.multiple) {
          link.html(`× ${dataText}`);
        } else {
          link.text(dataText);
        }
      } else {
        let input = "hidden",
            name = this.current.name;

        if (this.current.multiple) {
          name += "[]";
          choosenOption = $(`<div><input type="${input}" checked name="${name}"/><a href="${data.url}" data-picker-value="${data.value}" class="label primary">× ${dataText}</a></div>`);
        } else {
          choosenOption = $(`<div><input type="${input}" checked name="${name}"/><a href="${data.url}" data-picker-value="${data.value}">${dataText}</a></div>`);
        }
        choosenOption.appendTo(this.current.values);

        if (!this.current.target) {
          this.current.target = choosenOption;
        }
      }

      // Set input value
      let $input = $("input", choosenOption);
      $input.attr("value", data.value);

      if (this.current.autosort) {
        this._sort();
      }

      if (options.interactive) {
        // Raise changed event
        $input.trigger("change");
        this._removeErrors();

        if (options.close) {
          this._close();
        }
      }
    }

    _sort() {
      const values = $(".picker-values", this.current.picker);
      values.children().sort((item1, item2) => $("input", item1).val() - $("input", item2).val()).detach().appendTo(values);
    }

    _close() {
      // Close modal and unset target element
      this.modal.foundation("close");
      this.current.target = null;
    }

    _removeValue($picker, target) {
      if (target) {
        this._setCurrentPicker($picker, target);
        this.current.target.fadeOut(500, () => {
          this.current.target.remove();
          this.current.target = null;
        });
      }
    }

    _removeErrors() {
      let parent = this.current.picker.parent();
      $(".is-invalid-input", parent).removeClass("is-invalid-input");
      $(".is-invalid-label", parent).removeClass("is-invalid-label");
      $(".form-error.is-visible", parent).removeClass("is-visible");
    }

    _escape(str) {
      return str.replace(/[\u00A0-\u9999<>&]/gim, function (char) {
        return `&#${char.charCodeAt(0)};`;
      });
    }

    _targetFromEvent(event) {
      return event.target.parentNode;
    }

    _targetFromValue(value) {
      return $(`[data-picker-value=${value}]`, this.current.picker).parent()[0] || null;
    }
  }

  exports.Decidim = exports.Decidim || {};
  exports.Decidim.DataPicker = DataPicker;
})(window);
<|endoftext|>


<commit_before>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * Node eval plugin
 * For creating and configuring evaluators that evaluate Javascript code on
 *   a remote node server and update code cell results.
 */
define(function(require, exports, module) {
    'use strict';
    var PLUGIN_NAME = "Node";
    var COMMAND = "node/nodePlugin";

    var serviceBase = null;

    var nodeProto = {
        pluginName: PLUGIN_NAME,
        cmMode: "javascript",
        background: "#dbecb5",
        bgColor: "#8EC453",
        fgColor: "#FFFFFF",
        borderColor: "",
        shortName: "N",
        newShell: function (shellID, cb) {
            var self = this;

            if (!shellID) {
                shellID = "";
            }
            //verify server is up and running before a new shell call is attempted
            function checkNodeServerRunning() {
                $.ajax({
                    type: "GET",
                    datatype: "json",
                    url: serviceBase + "/pulse"
                }).fail(function(){
                    setTimeout(function () {
                        checkNodeServerRunning();
                    }, 2000)
                }).done(function(){
                    $.ajax({
                        type: "POST",
                        datatype: "json",
                        url: serviceBase + "/shell",
                        data: {shellid: shellID}
                    }).done(function(response){
                        shellID = response.shellID;
                        cb(shellID);
                    }).fail(function () {
                        console.log("failed to create shell", arguments);
                    });
                })
            }
            checkNodeServerRunning();
        },
        evaluate: function (code, modelOutput) {
            var self = this;
            var progressObj = {
                type: "BeakerDisplay",
                innertype: "Progress",
                object: {
                    message: "submitting ...",
                    startTime: new Date().getTime()
                }
            };
            modelOutput.result = progressObj;
            $.ajax({
                type: "POST",
                datatype: "json",
                url: serviceBase + "/evaluate",
                data: {shellID: self.settings.shellID, code: code}
            }).done(function(ret) {
                modelOutput.result = ret;
                bkHelper.refreshRootScope();
            }).fail(function(xhr, textStatus, error) {
                modelOutput.result = {
                    type: "BeakerDisplay",
                    innertype: "Error",
                    object: xhr.responseText
                };
            });
        },
        autocomplete: function (code, cpos, cb) {
            console.log("Autocomplete Called: Not implemented");
        },
        exit: function (cb) {
            console.log("Exit Called");
            var self = this;
            $.ajax({
                type: "POST",
                datatype: "json",
                url: serviceBase + "/rest/node/exit",
                data: { shellID: self.settings.shellID }
            }).done(cb);
        },
        spec: {}
    };

    var shellReadyDeferred = bkHelper.newDeferred();
    var init = function () {
      bkHelper.locatePluginService(PLUGIN_NAME, {
        command: COMMAND,
        startedIndicator: "Server Starting",
        recordOutput: "true"
        }).success(function (ret) {
            serviceBase = ret;
            var NodeShell = function (settings, doneCB) {
                var self = this;
                var setShellIdCB = function (id) {
                    if (id !== settings.shellID) {
                        console.log("A new Node shell was created.");
                    }
                    settings.shellID = id;
                    self.settings = settings;
                    if (doneCB) {
                      doneCB(self);
                    }
                };
                if (!settings.shellID) {
                    settings.shellID = "";
                }
                this.newShell(settings.shellID, setShellIdCB);
                this.perform = function (what) {
                    var action = this.spec[what].action;
                    this[action]();
                };
            };
            NodeShell.prototype = nodeProto;
            shellReadyDeferred.resolve(NodeShell);
        }).error(function () {
            alert('fail');
            console.log("process start failed", arguments);
        });
    };
    init();

  exports.getEvaluatorFactory = function() {
    return shellReadyDeferred.promise.then(function(Shell) {
      return {
        create: function(settings) {
          var deferred = bkHelper.newDeferred();
          new Shell(settings, function(shell) {
            deferred.resolve(shell);
          });
          return deferred.promise;
        }
      };
    });
  };

    exports.name = PLUGIN_NAME;
});
<commit_msg>fix node plugin by adding missing Q.defer object to evaluate method<commit_after>/*
 *  Copyright 2014 TWO SIGMA OPEN SOURCE, LLC
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/**
 * Node eval plugin
 * For creating and configuring evaluators that evaluate Javascript code on
 *   a remote node server and update code cell results.
 */
define(function(require, exports, module) {
    'use strict';
    var PLUGIN_NAME = "Node";
    var COMMAND = "node/nodePlugin";

    var serviceBase = null;

    var nodeProto = {
        pluginName: PLUGIN_NAME,
        cmMode: "javascript",
        background: "#dbecb5",
        bgColor: "#8EC453",
        fgColor: "#FFFFFF",
        borderColor: "",
        shortName: "N",
        newShell: function (shellID, cb) {
            var self = this;

            if (!shellID) {
                shellID = "";
            }
            //verify server is up and running before a new shell call is attempted
            function checkNodeServerRunning() {
                $.ajax({
                    type: "GET",
                    datatype: "json",
                    url: serviceBase + "/pulse"
                }).fail(function(){
                    setTimeout(function () {
                        checkNodeServerRunning();
                    }, 2000)
                }).done(function(){
                    $.ajax({
                        type: "POST",
                        datatype: "json",
                        url: serviceBase + "/shell",
                        data: {shellid: shellID}
                    }).done(function(response){
                        shellID = response.shellID;
                        cb(shellID);
                    }).fail(function () {
                        console.log("failed to create shell", arguments);
                    });
                })
            }
            checkNodeServerRunning();
        },
        evaluate: function (code, modelOutput) {
          var deferred = Q.defer();
            var self = this;
            var progressObj = {
                type: "BeakerDisplay",
                innertype: "Progress",
                object: {
                    message: "submitting ...",
                    startTime: new Date().getTime()
                }
            };
            modelOutput.result = progressObj;
            $.ajax({
                type: "POST",
                datatype: "json",
                url: serviceBase + "/evaluate",
                data: {shellID: self.settings.shellID, code: code}
            }).done(function(ret) {
                modelOutput.result = ret;
                bkHelper.refreshRootScope();
                deferred.resolve();
            }).fail(function(xhr, textStatus, error) {
                modelOutput.result = {
                    type: "BeakerDisplay",
                    innertype: "Error",
                    object: xhr.responseText
                };
                deferred.resolve();
            });
          return deferred.promise;
        },
        autocomplete: function (code, cpos, cb) {
            console.log("Autocomplete Called: Not implemented");
        },
        exit: function (cb) {
            console.log("Exit Called");
            var self = this;
            $.ajax({
                type: "POST",
                datatype: "json",
                url: serviceBase + "/rest/node/exit",
                data: { shellID: self.settings.shellID }
            }).done(cb);
        },
        spec: {}
    };

    var shellReadyDeferred = bkHelper.newDeferred();
    var init = function () {
      bkHelper.locatePluginService(PLUGIN_NAME, {
        command: COMMAND,
        startedIndicator: "Server Starting",
        recordOutput: "true"
        }).success(function (ret) {
            serviceBase = ret;
            var NodeShell = function (settings, doneCB) {
                var self = this;
                var setShellIdCB = function (id) {
                    if (id !== settings.shellID) {
                        console.log("A new Node shell was created.");
                    }
                    settings.shellID = id;
                    self.settings = settings;
                    if (doneCB) {
                      doneCB(self);
                    }
                };
                if (!settings.shellID) {
                    settings.shellID = "";
                }
                this.newShell(settings.shellID, setShellIdCB);
                this.perform = function (what) {
                    var action = this.spec[what].action;
                    this[action]();
                };
            };
            NodeShell.prototype = nodeProto;
            shellReadyDeferred.resolve(NodeShell);
        }).error(function () {
            alert('fail');
            console.log("process start failed", arguments);
        });
    };
    init();

  exports.getEvaluatorFactory = function() {
    return shellReadyDeferred.promise.then(function(Shell) {
      return {
        create: function(settings) {
          var deferred = bkHelper.newDeferred();
          new Shell(settings, function(shell) {
            deferred.resolve(shell);
          });
          return deferred.promise;
        }
      };
    });
  };

    exports.name = PLUGIN_NAME;
});
<|endoftext|>


<commit_before>/* navbar animation */
$("#toggle").click( function(event){
    event.preventDefault();
    if ($(this).hasClass("isDown") ) {
        $( ".navbar-fixed-top" ).animate({ "margin-top": "-62px" }, "fast" );
        $( "#content" ).animate({ "margin-top": "5px" }, "fast" );
        $(this).removeClass("isDown");
    } else {
        $( ".navbar-fixed-top" ).animate({ "margin-top": "0px" }, "fast" );
        $( "#content" ).animate({ "margin-top": "80px" }, "fast" );
        $(this).addClass("isDown");
    }
    return false;
});

/* gallery card animation */
$('.all-content').hide();

var bgColor;
var effect = 'animated bounceInLeft'; /* bounceIn, bounceInUp, bounceInDown, bounceInLeft,
										 bounceInRight, rotateIn, rotateInUpLeft, rotateInDownLeft,
										 rotateInUpRight, rotateInDownRight  */

$('.content li').click(function(){
	$('.card-front, .card-back').hide();
	$('.content li').removeClass('active').hide().css('border','none');
	$(this).addClass('active').show();
	bgColor = $('.active .card-back').css('background-color');
	$('.content').css('background-color',bgColor);
	$('.close, .all-content').show();
	$('.responsive').append('close').addClass(effect);
});

$('.responsive').on('click', '.close', function(){

	$('.close').remove();
	bgColor = $('.active .card-front').css('background-color');
	$('.responsive').removeClass(effect);
	$('.all-content').hide();
	$('.content li').removeClass('active').show().css({ 'border-bottom':'1px solid #2c2c2c',
													    'border-left':'1px solid #2c2c2c' });
	$('.card-front, .card-back').show();
	$('.content').css('background-color',bgColor);
});

/* favorite animation */
$('.favorite').click(function(){
    $(this).toggleClass('favorited');
    //alert($(this).attr("value"));
    var name = $(".favorited").val();
    alert(name);

    $.ajax({
      url: "/api/about",
      success: function (result) {
          var data = JSON.parse(result);
           //alert(result.city);
          $("#bj h2").html(data.city1);
          $("#sh h2").html(data.city2);
          $("#xa h2").html(data.city3);
          $("#cd h2").html(data.city4);
          $("#hk h2").html(data.city5);
          $("#tp h2").html(data.city6);
          $("#bj p").html(data.description);
      }});

});

/* city info */
// $(function () {
//     $.ajax({
//         url: "/api/about",
//         success: function (result) {
//             var data = JSON.parse(result);
//              //alert(result.city);
//             $("#bj h2").html(data.city1);
//             $("#sh h2").html(data.city2);
//             $("#xa h2").html(data.city3);
//             $("#cd h2").html(data.city4);
//             $("#hk h2").html(data.city5);
//             $("#tp h2").html(data.city6);
//             $("#bj p").html(data.description);
//         }});
// });
<commit_msg><Mar29><commit_after>/* navbar animation */
$("#toggle").click( function(event){
    event.preventDefault();
    if ($(this).hasClass("isDown") ) {
        $( ".navbar-fixed-top" ).animate({ "margin-top": "-62px" }, "fast" );
        $( "#content" ).animate({ "margin-top": "5px" }, "fast" );
        $(this).removeClass("isDown");
    } else {
        $( ".navbar-fixed-top" ).animate({ "margin-top": "0px" }, "fast" );
        $( "#content" ).animate({ "margin-top": "80px" }, "fast" );
        $(this).addClass("isDown");
    }
    return false;
});

/* gallery card animation */
$('.all-content').hide();

var bgColor;
var effect = 'animated bounceInLeft'; /* bounceIn, bounceInUp, bounceInDown, bounceInLeft,
										 bounceInRight, rotateIn, rotateInUpLeft, rotateInDownLeft,
										 rotateInUpRight, rotateInDownRight  */

$('.content li').click(function(){
	$('.card-front, .card-back').hide();
	$('.content li').removeClass('active').hide().css('border','none');
	$(this).addClass('active').show();
	bgColor = $('.active .card-back').css('background-color');
	$('.content').css('background-color',bgColor);
	$('.close, .all-content').show();
	$('.responsive').append('close').addClass(effect);
});

$('.responsive').on('click', '.close', function(){

	$('.close').remove();
	bgColor = $('.active .card-front').css('background-color');
	$('.responsive').removeClass(effect);
	$('.all-content').hide();
	$('.content li').removeClass('active').show().css({ 'border-bottom':'1px solid #2c2c2c',
													    'border-left':'1px solid #2c2c2c' });
	$('.card-front, .card-back').show();
	$('.content').css('background-color',bgColor);
});

/* favorite animation */
$('.favorite').click(function(){
    $(this).toggleClass('favorited');
    //alert($(this).attr("value"));
    var name = $(".favorited").val();
    alert(name);

    $.ajax({
      type: "GET",
      url: "/api/mapInfo/"+name,
      data: name,
      dataType: "xml",

      success: function (result) {

                console.log(result);
                var cname = $(result).find("name").first().text();
                if(cname !="information is not available yet")
                {
                var population = $(result).find("population").first().text();
                var location = $(result).find("location").first().text();
                var area = $(result).find("area").first().text();
                var language = $(result).find("language").first().text();
                var currency = $(result).find("currency").first().text();
                var time = $(result).find("time").first().text();
                var poi1 = $(result).find("poi1").first().text();
                var poi2 = $(result).find("poi2").first().text();
                var poi3 = $(result).find("poi3").first().text();
                $("#sh p").html("Description:
City Name: "+cname+";
"+
                                      "
Population: "+population+";
"+
                                      "
Location: "+location+";
"+
                                      "
Area: "+area+";
"+
                                      "
Language: "+language+";
"+
                                      "
Currency: "+currency+";
"+
                                      "
Time zone: "+time+";
"+
                                      "
Point of Interest: "+poi1+";
"+
                                      "
Point of Interest: "+poi2+";
"+
                                      "
Point of Interest: "+poi3+";
");
                }else{
                  $("#sh p").html("Description:
"+cname+"
");

                }

                            },

      //If there was no resonse from the server
      error: function(jqXHR, textStatus, errorThrown){
           console.log("Something really bad happened " + textStatus);
            $("#countrySum").html(jqXHR.responseText);
      }

  });

});

/* city info */
$(function () {
    $.ajax({
        url: "/api/about",
        success: function (result) {
            var data = JSON.parse(result);
             //alert(result.city);
            $("#bj h2").html(data.city1);
            $("#sh h2").html(data.city2);
            $("#xa h2").html(data.city3);
            $("#cd h2").html(data.city4);
            $("#hk h2").html(data.city5);
            $("#tp h2").html(data.city6);
            $("#bj p").html(data.description);
        }});
});
<|endoftext|>


<commit_before>/* MIT License
 *
 * Copyright (c) 2017 Bryce Wilson
 *
 * 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.
 */

/**
 * @callback hasher
 * @param {String} input - The string to be hashed
 * @return {String} A string in hexadecimal of some hash
 */

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @return {Object} An SVG element object
 */
function squareIdenticonSVG(size, hash){
	var bytes = _string2ByteArray(hash);

	var svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	// crispEdges prevents seams between blocks (we know everything is 90 degree angles anyway)
	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);
	svg.setAttribute('shape-rendering', 'crispEdges');

	// Inner loop stops at 3 for symmetry
	var pixelMap = [[], [], [], [], []];
	for(var i = 0; i < 5; ++i){
		for(var j = 0; j < 3; ++j){
			pixelMap[i][j] = !_getBit((i * 3) + j, bytes);
		}
		pixelMap[i][3] = pixelMap[i][1];
		pixelMap[i][4] = pixelMap[i][0];
	}

	var fillColor = '#' + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);
	var boxSize = Math.floor(size / 6);
	var marginSize = Math.floor((boxSize / 2) + ((size % 6) / 2));

	for(var i = 0; i < 5; ++i){
		for(var j = 0; j < 5; ++j){
			if(pixelMap[i][j]){
				var rect = document.createElementNS(svgNS, 'rect');

				rect.setAttribute('x', marginSize + (j * boxSize));
				rect.setAttribute('y', marginSize + (i * boxSize));
				rect.setAttribute('width', boxSize);
				rect.setAttribute('height', boxSize);
				rect.setAttribute('fill', fillColor);

				svg.appendChild(rect);
			}
		}
	}

	return svg;
}

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @param {Object} [options] - An object containing options for customizing the identicon
 * @param {Number} [options.shells=4] - The number of shells to generate (min 1, max 8)
 * @param {Number} [options.segments=Infinity] - The number of segments of equal angle to snap the arcs to
 * @param {Number} [options.symmetryAxisTilt] - If defined, makes the SVG symmetric about the line defined here in polar coordinates (degrees)
 * @return {Object} An SVG element object
 */
function circularIdenticonSVG(size, hash, options){
	var bytes = _string2ByteArray(hashFunction(id));

	var fillColor = "#" + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);

	var shells = (options && options['shells']) || 4;
	shells = Math.min(shells, 8);
	var segments = (options && options['segments']) || Infinity;
	var symmetryAxisTilt = options && Number(options['symmetryAxisTilt']) % 180;
	var innerRadius = Math.floor(size / ((shells * 2) + 1));
	var centerx = Math.floor(size / 2);
	var centery = Math.floor(size / 2);

	var svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);

	// The inner circle always exists, so we create it outside the loop with just a circle element
	var innerCircle = document.createElementNS(svgNS, 'circle');

	innerCircle.setAttribute('cx', centerx);
	innerCircle.setAttribute('cy', centery);
	innerCircle.setAttribute('r', innerRadius + 1);
	innerCircle.setAttribute('fill', fillColor);

	svg.appendChild(innerCircle);

	for(var i = 1; i < shells; ++i){
		/**
		 * Generates the path attribute for an arc given a shell and two angles.
		 * @param {Number} shell - The shell to draw the arc for (affects radii)
		 * @param {Number} theta1 - The starting angle for drawing the arc (0, 360) (degrees)
		 * @param {Number} theta1 - The end angle for drawing the arc, theta2 > theta1 (0, 360) (degrees)
		 * @return {String} A string containing the value of the d attribute for the path element
		 */
		var getArcPath = function (shell, theta1, theta2){
			var r1 = innerRadius * shell;
			var r2 = innerRadius * (i + 1) + 1;

			var largeArcFlag = (theta2 - theta1) < 180 ? 0 : 1;

			return `M ${centerx + _polar2CartesianX(r2, theta1)} ${centery + _polar2CartesianY(r2, theta1)} ` +
			       `A ${r2} ${r2} 0 ${largeArcFlag} 1 ${centerx + _polar2CartesianX(r2, theta2)} ${centery + _polar2CartesianY(r2, theta2)} ` +
			       `L ${centerx + _polar2CartesianX(r1, theta2)} ${centery + _polar2CartesianY(r1, theta2)} ` +
			       `A ${r1} ${r1} 0 ${largeArcFlag} 0 ${centerx + _polar2CartesianX(r1, theta1)} ${centery + _polar2CartesianY(r1, theta1)} ` +
			       'Z';
		}

		// Using _floorToMultiple snaps the angles for segmenting
		var theta1 = _floorToMultiple(360 * (bytes[(i * 2) + 0] / 0xFF), 360 / segments);
		var theta2 = _floorToMultiple(360 * (bytes[(i * 2) + 1] / 0xFF), 360 / segments);

		if(theta2 < theta1){
			var temp = theta1;
			theta1 = theta2;
			theta2 = temp;
		}

		var arc = document.createElementNS(svgNS, 'path');
		arc.setAttribute('d', getArcPath(i, theta1, theta2));
		arc.setAttribute('fill', fillColor);
		svg.appendChild(arc);

		if(isFinite(symmetryAxisTilt)){
			var temp = theta1;
			theta1 = (symmetryAxisTilt * 2) - theta2;
			theta2 = (symmetryAxisTilt * 2) - temp;

			arc = document.createElementNS(svgNS, 'path');
			arc.setAttribute('d', getArcPath(i, theta1, theta2));
			arc.setAttribute('fill', fillColor);
			svg.appendChild(arc);
		}
	}

	return svg;
}

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @param {Object} [options] - An object containing options for customizing the identicon
 * @param {Number} [options.shells=4] - The number of shells to generate (min 1, max 8)
 * @param {Number} [options.edges=5] - The number of edges (i.e. 6 makes a regular hexagon shape)
 * @return {Object} An SVG element object
 */
function polygonalIdenticonSVG(size, hash, options){
	var bytes = _string2ByteArray(hashFunction(id));

	var fillColor = "#" + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);

	var edges = (options && options['edges']) || 5;
	var shells = (options && options['shells']) || 4;
	var innerRadius = Math.floor(size / ((shells * 2) + 1));
	var centerx = Math.floor(size / 2);
	var centery = Math.floor(size / 2);

	svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	// Using crispEdges removes seams but also anti-aliasing
	// At the moment, seams are more acceptable than aliasing
	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);
	//svg.setAttribute('shape-rendering', 'crispEdges');

	// TODO: Draw "arc" instead of each edge to avoid seams
	// Loop fills pieces for each shell
	for(var i = 0; i < shells; ++i){
		for(var j = 0; j < edges; ++j){
			if(!i || _getBit(((i * shells) + j) % bytes.length, bytes)){
				// Angles are used to determine the locations of the vertices
				var theta1 = (360 / edges) * j;
				var theta2 = (360 / edges) * (j + 1);

				if(theta2 < theta1){
					var temp = theta1;
					theta1 = theta2;
					theta2 = temp;
				}

				var r1 = innerRadius * i;
				var r2 = innerRadius * (i + 1);
				r2 -= (r2 - r1) / 10;

				// See MDN documentation on the SVG path element's d attribute
				var d = `M ${centerx + _polar2CartesianX(r2, theta1)} ${centery + _polar2CartesianY(r2, theta1)} ` +
				        `L ${centerx + _polar2CartesianX(r2, theta2)} ${centery + _polar2CartesianY(r2, theta2)} ` +
				        `L ${centerx + _polar2CartesianX(r1, theta2)} ${centery + _polar2CartesianY(r1, theta2)} ` +
				        `L ${centerx + _polar2CartesianX(r1, theta1)} ${centery + _polar2CartesianY(r1, theta1)} ` +
				        'Z';

				var edge = document.createElementNS(svgNS, 'path');

				edge.setAttribute('d', d);
				edge.setAttribute('fill', fillColor);

				svg.appendChild(edge);
			}
		}
	}

	return svg;
}

/**
 * Turns a string of hexadecimal into an array of byte-sized numbers
 * @access private
 * @param {String} string - The input to convert
 * @return {Number[]} An array with size half the length of the input string
 */
function _string2ByteArray(string){
	var bytes = [];
	for(var i = 0; i < string.length / 2; ++i){
		bytes[i] = parseInt(string.substr(i * 2, 2), 16);
	}
	return bytes;
}

/**
 * Gets the x-coordinate in Cartesian coordinates from polar coordinates
 * @access private
 * @param {Number} r - The distance from the origin
 * @param {Number} theta - The angle from the reference axis in degrees
 * @return {Number}
 */
function _polar2CartesianX(r, theta){
	var radians = Math.PI * (theta - 90) / 180;
	return r * Math.cos(radians);
}

/**
 * Gets the y-coordinate in Cartesian coordinates from polar coordinates
 * @access private
 * @param {Number} r - The distance from the origin
 * @param {Number} theta - The angle from the reference axis in degrees
 * @return {Number}
 */
function _polar2CartesianY(r, theta){
	var radians = Math.PI * (theta - 90) / 180;
	return r * Math.sin(radians);
}

/**
 * Used to segment arcs. Reduces a number to a multiple of some other number
 * @access private
 * @param {Number} n - The number to floor
 * @param {Number} m - The number to floor to a multiple of
 * @return {Number} Multiple of m closest to but not greater than n
 */
function _floorToMultiple(n, m){
	if(!m){
		return n;
	}
	n /= m;
	n = Math.floor(n);
	return n * m;
}

/**
 * Gets the nth bit from an array of bytes
 * @access private
 * @param {Number} n - The index of the bit (0 is the lowest-order bit, 8 is the first bit of the second byte, etc...)
 * @param {Number[]} bytes - The array of bytes to look through
 * @return {Number} 0 or 1
 */
function _getBit(n, bytes){
	var byteIndex = Math.floor(n / 8) % bytes.length;
	var bitIndex = 7 - ((n % (8 * bytes.length)) - (byteIndex * 8));

	return (bytes[byteIndex] & (0x01 << bitIndex)) >> bitIndex;
}

/**
 * Turns a number into a string padded on the left with zeroes until it's at least some length
 * @param {Number} base - Convert the number to this base
 * @param {Number} size - The width of the output
 * @return {String}
 */
Number.prototype.padFront = function (base, size){
	var s = this.toString(base);
	while(s.length < size){
		s = '0' + s;
	}
	return s;
}
<commit_msg>Remove unnecessary tags<commit_after>/* MIT License
 *
 * Copyright (c) 2017 Bryce Wilson
 *
 * 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.
 */

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @return {Object} An SVG element object
 */
function squareIdenticonSVG(size, hash){
	var bytes = _string2ByteArray(hash);

	var svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	// crispEdges prevents seams between blocks (we know everything is 90 degree angles anyway)
	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);
	svg.setAttribute('shape-rendering', 'crispEdges');

	// Inner loop stops at 3 for symmetry
	var pixelMap = [[], [], [], [], []];
	for(var i = 0; i < 5; ++i){
		for(var j = 0; j < 3; ++j){
			pixelMap[i][j] = !_getBit((i * 3) + j, bytes);
		}
		pixelMap[i][3] = pixelMap[i][1];
		pixelMap[i][4] = pixelMap[i][0];
	}

	var fillColor = '#' + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);
	var boxSize = Math.floor(size / 6);
	var marginSize = Math.floor((boxSize / 2) + ((size % 6) / 2));

	for(var i = 0; i < 5; ++i){
		for(var j = 0; j < 5; ++j){
			if(pixelMap[i][j]){
				var rect = document.createElementNS(svgNS, 'rect');

				rect.setAttribute('x', marginSize + (j * boxSize));
				rect.setAttribute('y', marginSize + (i * boxSize));
				rect.setAttribute('width', boxSize);
				rect.setAttribute('height', boxSize);
				rect.setAttribute('fill', fillColor);

				svg.appendChild(rect);
			}
		}
	}

	return svg;
}

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @param {Object} [options] - An object containing options for customizing the identicon
 * @param {Number} [options.shells=4] - The number of shells to generate (min 1, max 8)
 * @param {Number} [options.segments=Infinity] - The number of segments of equal angle to snap the arcs to
 * @param {Number} [options.symmetryAxisTilt] - If defined, makes the SVG symmetric about the line defined here in polar coordinates (degrees)
 * @return {Object} An SVG element object
 */
function circularIdenticonSVG(size, hash, options){
	var bytes = _string2ByteArray(hashFunction(id));

	var fillColor = "#" + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);

	var shells = (options && options['shells']) || 4;
	shells = Math.min(shells, 8);
	var segments = (options && options['segments']) || Infinity;
	var symmetryAxisTilt = options && Number(options['symmetryAxisTilt']) % 180;
	var innerRadius = Math.floor(size / ((shells * 2) + 1));
	var centerx = Math.floor(size / 2);
	var centery = Math.floor(size / 2);

	var svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);

	// The inner circle always exists, so we create it outside the loop with just a circle element
	var innerCircle = document.createElementNS(svgNS, 'circle');

	innerCircle.setAttribute('cx', centerx);
	innerCircle.setAttribute('cy', centery);
	innerCircle.setAttribute('r', innerRadius + 1);
	innerCircle.setAttribute('fill', fillColor);

	svg.appendChild(innerCircle);

	for(var i = 1; i < shells; ++i){
		/**
		 * Generates the path attribute for an arc given a shell and two angles.
		 * @param {Number} shell - The shell to draw the arc for (affects radii)
		 * @param {Number} theta1 - The starting angle for drawing the arc (0, 360) (degrees)
		 * @param {Number} theta1 - The end angle for drawing the arc, theta2 > theta1 (0, 360) (degrees)
		 * @return {String} A string containing the value of the d attribute for the path element
		 */
		var getArcPath = function (shell, theta1, theta2){
			var r1 = innerRadius * shell;
			var r2 = innerRadius * (i + 1) + 1;

			var largeArcFlag = (theta2 - theta1) < 180 ? 0 : 1;

			return `M ${centerx + _polar2CartesianX(r2, theta1)} ${centery + _polar2CartesianY(r2, theta1)} ` +
			       `A ${r2} ${r2} 0 ${largeArcFlag} 1 ${centerx + _polar2CartesianX(r2, theta2)} ${centery + _polar2CartesianY(r2, theta2)} ` +
			       `L ${centerx + _polar2CartesianX(r1, theta2)} ${centery + _polar2CartesianY(r1, theta2)} ` +
			       `A ${r1} ${r1} 0 ${largeArcFlag} 0 ${centerx + _polar2CartesianX(r1, theta1)} ${centery + _polar2CartesianY(r1, theta1)} ` +
			       'Z';
		}

		// Using _floorToMultiple snaps the angles for segmenting
		var theta1 = _floorToMultiple(360 * (bytes[(i * 2) + 0] / 0xFF), 360 / segments);
		var theta2 = _floorToMultiple(360 * (bytes[(i * 2) + 1] / 0xFF), 360 / segments);

		if(theta2 < theta1){
			var temp = theta1;
			theta1 = theta2;
			theta2 = temp;
		}

		var arc = document.createElementNS(svgNS, 'path');
		arc.setAttribute('d', getArcPath(i, theta1, theta2));
		arc.setAttribute('fill', fillColor);
		svg.appendChild(arc);

		if(isFinite(symmetryAxisTilt)){
			var temp = theta1;
			theta1 = (symmetryAxisTilt * 2) - theta2;
			theta2 = (symmetryAxisTilt * 2) - temp;

			arc = document.createElementNS(svgNS, 'path');
			arc.setAttribute('d', getArcPath(i, theta1, theta2));
			arc.setAttribute('fill', fillColor);
			svg.appendChild(arc);
		}
	}

	return svg;
}

/**
 * Turns an id and a hashing function into a unique square identicon SVG element
 * @param {Number} - Side length in pixels of the image
 * @param {String} hash - A string representing a hexadecimal number. Ideally, this is the output of a hash function such as MD5 or SHA-1.
 * @param {Object} [options] - An object containing options for customizing the identicon
 * @param {Number} [options.shells=4] - The number of shells to generate (min 1, max 8)
 * @param {Number} [options.edges=5] - The number of edges (i.e. 6 makes a regular hexagon shape)
 * @return {Object} An SVG element object
 */
function polygonalIdenticonSVG(size, hash, options){
	var bytes = _string2ByteArray(hashFunction(id));

	var fillColor = "#" + bytes[bytes.length - 3].padFront(16, 2) +
	                      bytes[bytes.length - 2].padFront(16, 2) +
	                      bytes[bytes.length - 1].padFront(16, 2);

	var edges = (options && options['edges']) || 5;
	var shells = (options && options['shells']) || 4;
	var innerRadius = Math.floor(size / ((shells * 2) + 1));
	var centerx = Math.floor(size / 2);
	var centery = Math.floor(size / 2);

	svgNS = 'http://www.w3.org/2000/svg';
	var svg = document.createElementNS(svgNS, 'svg');

	// Using crispEdges removes seams but also anti-aliasing
	// At the moment, seams are more acceptable than aliasing
	svg.setAttribute('width', size);
	svg.setAttribute('height', size);
	svg.setAttribute('xmlns', svgNS);
	//svg.setAttribute('shape-rendering', 'crispEdges');

	// TODO: Draw "arc" instead of each edge to avoid seams
	// Loop fills pieces for each shell
	for(var i = 0; i < shells; ++i){
		for(var j = 0; j < edges; ++j){
			if(!i || _getBit(((i * shells) + j) % bytes.length, bytes)){
				// Angles are used to determine the locations of the vertices
				var theta1 = (360 / edges) * j;
				var theta2 = (360 / edges) * (j + 1);

				if(theta2 < theta1){
					var temp = theta1;
					theta1 = theta2;
					theta2 = temp;
				}

				var r1 = innerRadius * i;
				var r2 = innerRadius * (i + 1);
				r2 -= (r2 - r1) / 10;

				// See MDN documentation on the SVG path element's d attribute
				var d = `M ${centerx + _polar2CartesianX(r2, theta1)} ${centery + _polar2CartesianY(r2, theta1)} ` +
				        `L ${centerx + _polar2CartesianX(r2, theta2)} ${centery + _polar2CartesianY(r2, theta2)} ` +
				        `L ${centerx + _polar2CartesianX(r1, theta2)} ${centery + _polar2CartesianY(r1, theta2)} ` +
				        `L ${centerx + _polar2CartesianX(r1, theta1)} ${centery + _polar2CartesianY(r1, theta1)} ` +
				        'Z';

				var edge = document.createElementNS(svgNS, 'path');

				edge.setAttribute('d', d);
				edge.setAttribute('fill', fillColor);

				svg.appendChild(edge);
			}
		}
	}

	return svg;
}

/**
 * Turns a string of hexadecimal into an array of byte-sized numbers
 * @access private
 * @param {String} string - The input to convert
 * @return {Number[]} An array with size half the length of the input string
 */
function _string2ByteArray(string){
	var bytes = [];
	for(var i = 0; i < string.length / 2; ++i){
		bytes[i] = parseInt(string.substr(i * 2, 2), 16);
	}
	return bytes;
}

/**
 * Gets the x-coordinate in Cartesian coordinates from polar coordinates
 * @access private
 * @param {Number} r - The distance from the origin
 * @param {Number} theta - The angle from the reference axis in degrees
 * @return {Number}
 */
function _polar2CartesianX(r, theta){
	var radians = Math.PI * (theta - 90) / 180;
	return r * Math.cos(radians);
}

/**
 * Gets the y-coordinate in Cartesian coordinates from polar coordinates
 * @access private
 * @param {Number} r - The distance from the origin
 * @param {Number} theta - The angle from the reference axis in degrees
 * @return {Number}
 */
function _polar2CartesianY(r, theta){
	var radians = Math.PI * (theta - 90) / 180;
	return r * Math.sin(radians);
}

/**
 * Used to segment arcs. Reduces a number to a multiple of some other number
 * @access private
 * @param {Number} n - The number to floor
 * @param {Number} m - The number to floor to a multiple of
 * @return {Number} Multiple of m closest to but not greater than n
 */
function _floorToMultiple(n, m){
	if(!m){
		return n;
	}
	n /= m;
	n = Math.floor(n);
	return n * m;
}

/**
 * Gets the nth bit from an array of bytes
 * @access private
 * @param {Number} n - The index of the bit (0 is the lowest-order bit, 8 is the first bit of the second byte, etc...)
 * @param {Number[]} bytes - The array of bytes to look through
 * @return {Number} 0 or 1
 */
function _getBit(n, bytes){
	var byteIndex = Math.floor(n / 8) % bytes.length;
	var bitIndex = 7 - ((n % (8 * bytes.length)) - (byteIndex * 8));

	return (bytes[byteIndex] & (0x01 << bitIndex)) >> bitIndex;
}

/**
 * Turns a number into a string padded on the left with zeroes until it's at least some length
 * @param {Number} base - Convert the number to this base
 * @param {Number} size - The width of the output
 * @return {String}
 */
Number.prototype.padFront = function (base, size){
	var s = this.toString(base);
	while(s.length < size){
		s = '0' + s;
	}
	return s;
}
<|endoftext|>


<commit_before>module.exports = function(grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		src_dir: 'src',
		template_dir: 'grunt/templates',
		build_dir: 'build',
		docs_dir: 'build_docs',
		dist_dir: 'dist',

		modules: grunt.file.expand({ cwd: 'src' }, 'mm-*'),
		shared: grunt.file.expand({ cwd: 'src' }, 'shared/**/*.html'),

		clean: {
			build: '<%= build_dir %>',
			docs: '<%= docs_dir %>',
			dist: '<%= dist_dir %>',
			src: ['src/mm-*/*.+(css|min.js)', 'src/mm-*/template.html']
		},

		copy: {
			build: {
				expand: true,
				cwd: '<%= src_dir %>',
				src: ['**/*.+(html|js|woff)', '!**/example.html'],
				dest: '<%= build_dir %>'
			},
			dist: {
				expand: true,
				cwd: '<%= build_dir %>',
				src: ['**/*.html', '!mm-*/index.html'],
				dest: '<%= dist_dir %>'
			}
		},

		sass: {
			options: {
				includePaths: ['bower_components/bourbon/app/assets/stylesheets/', '<%= src_dir %>/shared/sass/']
			},

			dev: {
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			},

			docs: {
				files: {
					'<%= docs_dir %>/docs.css':'docs/docs.scss'
				}
			},

			dist: {
				options: {
					outputStyle: 'compressed'
				},
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			}
		},

		postcss: {
			options: {
				processors: [
					require('autoprefixer')({browsers: 'last 2 versions'})
				]
			},
			dist: {
				src: '<%= build_dir %>/**/*.css'
			}
		},

		uglify: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/*.js', 'shared/js/*.js'],
					dest: '<%= build_dir %>'
				}]
			}
		},

		cssUrlEmbed: {
			fonts: {
				files: {
					'<%= build_dir %>/shared/fonts/fonts.css' : '<%= build_dir %>/shared/fonts/fonts.css'
				}
			}
		},

		hogan_static: {
			index: {
				options: {
					data: {
						modules: '<%= modules %>'
					}
				},
				files: [{
					src: '<%= template_dir %>/index_template.html',
					dest: '<%= build_dir %>/index.html'
				}]
			},
			colors: {
				options: {
					// delimiters:'<< >>',
					data: {
						colors: {}
					}
				},
				files: {
					'<%= src_dir %>/lib/Colors.js': '<%= template_dir %>/lib_color.js'
				}
			},
			lib: {
				options:{
					data: {
						modules: '<%= modules %>',
						shared: '<%= shared %>',
					}
				},
				files: [{
					src: '<%= template_dir %>/lib_template.html',
					dest: '<%= build_dir %>/<%= pkg.name %>.html'
				}]
			},
			fonts:{
				options:{
					data:{}
				},
				files:{
					'<%= build_dir %>/shared/fonts/fonts.html': "<%= template_dir %>/font_template.html"
				}
			}
		},

		vulcanize: {
			options: {
				inlineScripts:true,
				inlineCss:true,
				stripExcludes:false
			},
			shared: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['shared/**/*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			modules: {
				options:{
					excludes: {
						imports: ['.*\.html']
					}
				},
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/mm-*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			dist:{
				options:{
					excludes: {
						imports: ['polymer.html']
					}
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		htmlmin: {
			dist: {
				options: {
					customAttrAssign: /\$=/,
					minifyJS: true,
					removeComments: true,
					collapseWhitespace: true
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		replace: {
            bower: {
                src: ['<%= build_dir %>/**/*.html'],
                overwrite: true,
                replacements: [{
                    from: 'bower_components',
                    to: '..'
                }]
            }
        },

		banner: grunt.file.read("BANNER.txt"),

		usebanner: {
			jscss: {
				options: {
					banner: "/**\n<%= banner %>\n*/"
				},
				files: {
					src: [ 'src/**/*.js', 'src/**/*.scss' ]
				}
			},
			html: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ 'src/mm-*/mm-*.html' ]
				}
			},
			dist: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ '<%= build_dir %>/strand.html' ]
				}
			},
		},

		shell: {
			stageRelease: {
				command: 'git add <%= dist_dir %>/*'
			}
		},

		githubChanges: {
			release: {
				options: {
					owner: 'MediaMath',
					repository: 'strand',
					branch: 'develop',
					noMerges: true,
					tagName: 'v<%= pkg.version %>'
				}
			}
		},

		bump: {
			options: {
				files: [ 'package.json', 'bower.json' ],
				commitFiles: [ 'package.json', 'bower.json', 'CHANGELOG.md', 'dist/*' ],
				updateConfigs: ['pkg'],
				push: false,
				pushTo: 'upstream',
				createTag: true
			}
		}

	});

	//Generate template style includes for new polymer 1.1 ext-styling
	grunt.registerTask('style:imports', function() {
		var tasks = [];
		var build = grunt.config('build_dir') + "/";
		var styles = grunt.file.expand({
			cwd: grunt.config('build_dir')
		}, 'mm-**/*.css');

		var styleData = {};
		styles.forEach(function(file) {
			var key = file.split(".css").join("").split("/")[0];
			if (grunt.file.exists(build + file)) {
				var css = grunt.file.read(build + file);
				// styleData[key] = css;
				grunt.config.set("hogan_static.styles_" + key, {
					options:{
						data:{
							module:key,
							style:css
						}
					},
					files:[{
						src:'<%= template_dir %>/style_module_template.html',
						dest:'<%=build_dir%>/'+key+'/style.html'
					}]
				});
				tasks.push("hogan_static:styles_" + key);
			} else {
				grunt.log.writeln("Not Found:"+build+file);
			}
		});

		//set config for the static fonts lib as well
		var fnts = grunt.config.get('build_dir') + '/shared/fonts/fonts.css';
		grunt.config.set("hogan_static.fonts.options.data.style", grunt.file.read(fnts));

		//run batched
		grunt.task.run(tasks);

	});

	//Create HTML imports for JS library
	grunt.registerTask('jslib:imports', function() {
		var files = grunt.file.expand({cwd: grunt.config('src_dir') + '/shared/js/' }, '*.js');

		files.forEach(function(file) {
			var fileName = file.substr(0, file.lastIndexOf('.')) || file;
			grunt.file.write(grunt.config('build_dir') + '/shared/js/' + fileName.toLowerCase() + '.html', '<script src=\"' + file + '\"></script>');
		});

		grunt.log.ok();
	});

	grunt.registerTask('color', function() {
		var path = grunt.config('src_dir') + '/shared/sass/_color.scss';
		var color = grunt.file.read(path);
		color = color.replace(/\$color-(\w\d\d*): (#[\w\d]*);/g, function(a, key, value) {
			return "\t\"" + key + "\":\"" + value + "\",";
		});
		grunt.config.set('hogan_static.colors.options.data.colors', color);
		grunt.task.run(['hogan_static:colors']);
	});

	grunt.registerTask('build:dist', function(arg){
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dist',
			'postcss',
			'cssUrlEmbed',
			'style:imports',
			'hogan_static:fonts',
			// 'hogan_static:lib', //un-comment to build entire library
			'vulcanize:dist',
			'htmlmin:dist',
			'usebanner:dist'
		]);
	});

	grunt.registerTask('build:dev', function() {
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dev',
			'postcss',
			'style:imports',
			'hogan_static:fonts',
			'hogan_static:index'
			// 'hogan_static:lib'
		]);
	});

	grunt.registerTask('default', ['build:dev']);

	grunt.registerTask('link', ["clean:dist", "build:dist", "replace:bower", "copy:dist"]);

	grunt.registerTask('release', function(version){
		version = version || "patch";
		grunt.task.run( "bump-only:" + version, "clean:dist", "build:dist", "replace:bower", "copy:dist", "shell:stageRelease", "githubChanges", "bump-commit");
	});
};
<commit_msg>better build support for debugging in bower link scenarios<commit_after>module.exports = function(grunt) {

	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),
		src_dir: 'src',
		template_dir: 'grunt/templates',
		build_dir: 'build',
		docs_dir: 'build_docs',
		dist_dir: 'dist',

		modules: grunt.file.expand({ cwd: 'src' }, 'mm-*'),
		shared: grunt.file.expand({ cwd: 'src' }, 'shared/**/*.html'),

		clean: {
			build: '<%= build_dir %>',
			docs: '<%= docs_dir %>',
			dist: '<%= dist_dir %>',
			src: ['src/mm-*/*.+(css|min.js)', 'src/mm-*/template.html']
		},

		copy: {
			build: {
				expand: true,
				cwd: '<%= src_dir %>',
				src: ['**/*.+(html|js|woff)', '!**/example.html'],
				dest: '<%= build_dir %>'
			},
			dist: {
				expand: true,
				cwd: '<%= build_dir %>',
				src: ['**/*.html', '!mm-*/index.html'],
				dest: '<%= dist_dir %>'
			},
			link: {
				expand: true,
				cwd: '<%= src_dir %>',
				src: ['strand.html'],
				dest: '<%= dist_dir %>'
			},
			debug: {
				expand: true,
				cwd: '<%= build_dir %>',
				src: ['**/*.+(html|js|woff|css)', '!**/example.html'],
				dest: '<%= dist_dir %>'
			},
		},

		sass: {
			options: {
				includePaths: ['bower_components/bourbon/app/assets/stylesheets/', '<%= src_dir %>/shared/sass/']
			},

			dev: {
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			},

			docs: {
				files: {
					'<%= docs_dir %>/docs.css':'docs/docs.scss'
				}
			},

			dist: {
				options: {
					outputStyle: 'compressed'
				},
				files: [{
					expand: true,
					cwd: '<%= src_dir %>',
					src: ['**/*.scss'],
					dest: '<%= build_dir %>',
					ext: '.css'
				}]
			}
		},

		postcss: {
			options: {
				processors: [
					require('autoprefixer')({browsers: 'last 2 versions'})
				]
			},
			dist: {
				src: '<%= build_dir %>/**/*.css'
			}
		},

		uglify: {
			dist: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/*.js', 'shared/js/*.js'],
					dest: '<%= build_dir %>'
				}]
			}
		},

		cssUrlEmbed: {
			fonts: {
				files: {
					'<%= build_dir %>/shared/fonts/fonts.css' : '<%= build_dir %>/shared/fonts/fonts.css'
				}
			}
		},

		hogan_static: {
			index: {
				options: {
					data: {
						modules: '<%= modules %>'
					}
				},
				files: [{
					src: '<%= template_dir %>/index_template.html',
					dest: '<%= build_dir %>/index.html'
				}]
			},
			colors: {
				options: {
					// delimiters:'<< >>',
					data: {
						colors: {}
					}
				},
				files: {
					'<%= src_dir %>/lib/Colors.js': '<%= template_dir %>/lib_color.js'
				}
			},
			lib: {
				options:{
					data: {
						modules: '<%= modules %>',
						shared: '<%= shared %>',
					}
				},
				files: [{
					src: '<%= template_dir %>/lib_template.html',
					dest: '<%= build_dir %>/<%= pkg.name %>.html'
				}]
			},
			fonts:{
				options:{
					data:{}
				},
				files:{
					'<%= build_dir %>/shared/fonts/fonts.html': "<%= template_dir %>/font_template.html"
				}
			}
		},

		vulcanize: {
			options: {
				inlineScripts:true,
				inlineCss:true,
				stripExcludes:false
			},
			shared: {
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['shared/**/*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			modules: {
				options:{
					excludes: {
						imports: ['.*\.html']
					}
				},
				files: [{
					expand: true,
					cwd: '<%= build_dir %>',
					src: ['mm-*/mm-*.html'],
					dest: '<%= build_dir %>'
				}]
			},
			dist:{
				options:{
					excludes: {
						imports: ['polymer.html']
					}
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		htmlmin: {
			dist: {
				options: {
					customAttrAssign: /\$=/,
					minifyJS: true,
					removeComments: true,
					collapseWhitespace: true
				},
				files: {
					'<%= build_dir %>/<%= pkg.name %>.html' : '<%= build_dir %>/<%= pkg.name %>.html'
				}
			}
		},

		replace: {
            bower: {
                src: ['<%= build_dir %>/**/*.html'],
                overwrite: true,
                replacements: [{
                    from: 'bower_components',
                    to: '..'
                }]
            }
        },

		banner: grunt.file.read("BANNER.txt"),

		usebanner: {
			jscss: {
				options: {
					banner: "/**\n<%= banner %>\n*/"
				},
				files: {
					src: [ 'src/**/*.js', 'src/**/*.scss' ]
				}
			},
			html: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ 'src/mm-*/mm-*.html' ]
				}
			},
			dist: {
				options: {
					banner: "<!--\n<%= banner %>\n-->"
				},
				files: {
					src: [ '<%= build_dir %>/strand.html' ]
				}
			},
		},

		shell: {
			stageRelease: {
				command: 'git add <%= dist_dir %>/*'
			}
		},

		githubChanges: {
			release: {
				options: {
					owner: 'MediaMath',
					repository: 'strand',
					branch: 'develop',
					noMerges: true,
					tagName: 'v<%= pkg.version %>'
				}
			}
		},

		bump: {
			options: {
				files: [ 'package.json', 'bower.json' ],
				commitFiles: [ 'package.json', 'bower.json', 'CHANGELOG.md', 'dist/*' ],
				updateConfigs: ['pkg'],
				push: false,
				pushTo: 'upstream',
				createTag: true
			}
		}

	});

	//Generate template style includes for new polymer 1.1 ext-styling
	grunt.registerTask('style:imports', function() {
		var tasks = [];
		var build = grunt.config('build_dir') + "/";
		var styles = grunt.file.expand({
			cwd: grunt.config('build_dir')
		}, 'mm-**/*.css');

		var styleData = {};
		styles.forEach(function(file) {
			var key = file.split(".css").join("").split("/")[0];
			if (grunt.file.exists(build + file)) {
				var css = grunt.file.read(build + file);
				// styleData[key] = css;
				grunt.config.set("hogan_static.styles_" + key, {
					options:{
						data:{
							module:key,
							style:css
						}
					},
					files:[{
						src:'<%= template_dir %>/style_module_template.html',
						dest:'<%=build_dir%>/'+key+'/style.html'
					}]
				});
				tasks.push("hogan_static:styles_" + key);
			} else {
				grunt.log.writeln("Not Found:"+build+file);
			}
		});

		//set config for the static fonts lib as well
		var fnts = grunt.config.get('build_dir') + '/shared/fonts/fonts.css';
		grunt.config.set("hogan_static.fonts.options.data.style", grunt.file.read(fnts));

		//run batched
		grunt.task.run(tasks);

	});

	//Create HTML imports for JS library
	grunt.registerTask('jslib:imports', function() {
		var files = grunt.file.expand({cwd: grunt.config('src_dir') + '/shared/js/' }, '*.js');

		files.forEach(function(file) {
			var fileName = file.substr(0, file.lastIndexOf('.')) || file;
			grunt.file.write(grunt.config('build_dir') + '/shared/js/' + fileName.toLowerCase() + '.html', '<script src=\"' + file + '\"></script>');
		});

		grunt.log.ok();
	});

	grunt.registerTask('color', function() {
		var path = grunt.config('src_dir') + '/shared/sass/_color.scss';
		var color = grunt.file.read(path);
		color = color.replace(/\$color-(\w\d\d*): (#[\w\d]*);/g, function(a, key, value) {
			return "\t\"" + key + "\":\"" + value + "\",";
		});
		grunt.config.set('hogan_static.colors.options.data.colors', color);
		grunt.task.run(['hogan_static:colors']);
	});

	grunt.registerTask('build:dist', function(arg){
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dist',
			'postcss',
			'cssUrlEmbed',
			'style:imports',
			'hogan_static:fonts',
			// 'hogan_static:lib', //un-comment to build entire library
			'vulcanize:dist',
			'htmlmin:dist',
			'usebanner:dist'
		]);
	});

	grunt.registerTask('build:dev', function() {
		grunt.task.run([
			'clean:build',
			'copy:build',
			'sass:dev',
			'postcss',
			'style:imports',
			'hogan_static:fonts',
			'hogan_static:index'
			// 'hogan_static:lib'
		]);
	});

	grunt.registerTask('default', ['build:dev']);

	grunt.registerTask('link', ["clean:dist", "build:dist", "replace:bower", "copy:dist"]);
	grunt.registerTask('debug', ['build:dev', 'copy:debug', 'copy:link']);

	grunt.registerTask('release', function(version){
		version = version || "patch";
		grunt.task.run( "bump-only:" + version, "clean:dist", "build:dist", "replace:bower", "copy:dist", "shell:stageRelease", "githubChanges", "bump-commit");
	});
};
<|endoftext|>