<commit_before>$(document).on('page:change', function(){

  $('#custom-page-modules-list').sortable({
    axis: 'y',
    cursor: "move",
    opacity: 0.7,
    connectWith: '.nesting-wrapper',
    placeholder: 'ui-state-hover',
    handle: '.icon-bars',
    update: function(e, ui) {
      $.ajax({
        url: $(this).data("url"),
        type: "PATCH",
        data: $(this).sortable('serialize')
      });
    },
    start: function (event, ui) {
      var id_textarea = ui.item.find(".ckeditor textarea").attr('id');
      if(id_textarea) {
        CKEDITOR.instances[id_textarea].destroy();
      }
    },
    stop: function (event, ui) {
      var id_textarea = ui.item.find(".ckeditor textarea").attr('id');
      if(id_textarea) {
        CKEDITOR.replace(id_textarea, {toolbar: 'admin'});
      }
    }
  });

  loadCodeSnippet();
  toggleCustomPageModuleCardSection();
  toggleAllCustomPageModuleCardSection();
  toggleCustomPageMetaTags();
});

function loadCodeSnippet(){
  var snippets = $('.js_snippets').data("snippets")
  if(snippets) {
    for (var snippet of snippets) {
      eval(snippet)
    }
  }
}

function toggleCustomPageModuleCardSection() {
  $('#custom-page-modules-list .card-section').hide()
  $('#custom-page-modules-list .card-divider').on('click', function(){
    $(this).siblings('.card-section').slideToggle();
    var $arrowDown = $(this).find('.icon-arrow-down');
    if($arrowDown.length) {
      $arrowDown.toggleClass('icon-arrow-down icon-arrow-top');
    } else {
      var $arrowTop = $(this).find('.icon-arrow-top');
      if($arrowTop.length){
        $arrowTop.toggleClass('icon-arrow-top icon-arrow-down');
      }
    }
  })
}

function toggleAllCustomPageModuleCardSection() {
  $('#toggle_modules_collapse_button').on('click', function(){
    $(this).toggleClass('toggle-open');
    $(this).find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
    var cardSections = $('#custom-page-modules-list .card-section')
    for(var cardSection of cardSections) {
      var $arrow = $(cardSection).siblings('.card-divider')
      if($(this).hasClass('toggle-open')){
        $(this).find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
        cardSections.show('slow', 'swing');
        $arrow.find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
      } else {
        $(this).find('.icon-arrow-top').toggleClass('icon-arrow-top icon-arrow-down');
        cardSections.hide('slow', 'swing');
        $arrow.find('.icon-arrow-top').toggleClass('icon-arrow-top icon-arrow-down');
      }
    }
    return false;
  })
}

function toggleCustomPageMetaTags() {
  $('#custom_page-meta_tags').hide();
  $('.custom_page-fields .icon-arrow-down').on('click', function(){
    $('#custom_page-meta_tags').slideToggle();
    $('#custom_page-toggle_meta_tags a').toggleClass('icon-arrow-down icon-arrow-top');
  })
}<commit_msg>Fixes undesired final scroll when clicks toggle metatags<commit_after>$(document).on('page:change', function(){

  $('#custom-page-modules-list').sortable({
    axis: 'y',
    cursor: "move",
    opacity: 0.7,
    connectWith: '.nesting-wrapper',
    placeholder: 'ui-state-hover',
    handle: '.icon-bars',
    update: function(e, ui) {
      $.ajax({
        url: $(this).data("url"),
        type: "PATCH",
        data: $(this).sortable('serialize')
      });
    },
    start: function (event, ui) {
      var id_textarea = ui.item.find(".ckeditor textarea").attr('id');
      if(id_textarea) {
        CKEDITOR.instances[id_textarea].destroy();
      }
    },
    stop: function (event, ui) {
      var id_textarea = ui.item.find(".ckeditor textarea").attr('id');
      if(id_textarea) {
        CKEDITOR.replace(id_textarea, {toolbar: 'admin'});
      }
    }
  });

  loadCodeSnippet();
  toggleCustomPageModuleCardSection();
  toggleAllCustomPageModuleCardSection();
  toggleCustomPageMetaTags();
});

function loadCodeSnippet(){
  var snippets = $('.js_snippets').data("snippets")
  if(snippets) {
    for (var snippet of snippets) {
      eval(snippet)
    }
  }
}

function toggleCustomPageModuleCardSection() {
  $('#custom-page-modules-list .card-section').hide()
  $('#custom-page-modules-list .card-divider').on('click', function(){
    $(this).siblings('.card-section').slideToggle();
    var $arrowDown = $(this).find('.icon-arrow-down');
    if($arrowDown.length) {
      $arrowDown.toggleClass('icon-arrow-down icon-arrow-top');
    } else {
      var $arrowTop = $(this).find('.icon-arrow-top');
      if($arrowTop.length){
        $arrowTop.toggleClass('icon-arrow-top icon-arrow-down');
      }
    }
  })
}

function toggleAllCustomPageModuleCardSection() {
  $('#toggle_modules_collapse_button').on('click', function(){
    $(this).toggleClass('toggle-open');
    $(this).find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
    var cardSections = $('#custom-page-modules-list .card-section')
    for(var cardSection of cardSections) {
      var $arrow = $(cardSection).siblings('.card-divider')
      if($(this).hasClass('toggle-open')){
        $(this).find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
        cardSections.show('slow', 'swing');
        $arrow.find('.icon-arrow-down').toggleClass('icon-arrow-down icon-arrow-top');
      } else {
        $(this).find('.icon-arrow-top').toggleClass('icon-arrow-top icon-arrow-down');
        cardSections.hide('slow', 'swing');
        $arrow.find('.icon-arrow-top').toggleClass('icon-arrow-top icon-arrow-down');
      }
    }
    return false;
  })
}

function toggleCustomPageMetaTags() {
  $('#custom_page-meta_tags').hide();
  $('.custom_page-fields .icon-arrow-down').on('click', function(){
    $('#custom_page-meta_tags').slideToggle();
    $('#custom_page-toggle_meta_tags a').toggleClass('icon-arrow-down icon-arrow-top');
    return false;
  })
}<|endoftext|>


<commit_before>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
//= require jquery.autocomplete
//= require forem
//
(function($) {
  $(document).ready(function() {
    group_id = $('#add_member').data('group-id');
    $('#new_member').autocomplete({source: 'http://' + window.location.host + Forem.base_path + '/admin/users/autocomplete'})

    $("#new_member").bind("autocompleteselect", function(event, ui) {
      $("#add_member").attr('disabled', false)
    })

    add_member = function() {
      user = $("#new_member").val()
      $.post(Forem.base_path + '/admin/groups/' + group_id + '/members', { user: user })
      $(this).attr('disabled', true)
      $("#new_member").val("")
      $('#members').append('
' + user + '
')
    }

    $('#add_member').click(add_member)
    $('#new_member').keypress(function(e){
      if (e.which == 13) {
        add_member();
      }
    })
  })
})(jQuery)
<commit_msg>Testing autocomplete remote path<commit_after>// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
//= require jquery.autocomplete
//= require forem
//
(function($) {
  $(document).ready(function() {
    group_id = $('#add_member').data('group-id');
    $('#new_member').autocomplete({source: Forem.base_path + 'admin/users/autocomplete'})

    $("#new_member").bind("autocompleteselect", function(event, ui) {
      $("#add_member").attr('disabled', false)
    })

    add_member = function() {
      user = $("#new_member").val()
      $.post(Forem.base_path + '/admin/groups/' + group_id + '/members', { user: user })
      $(this).attr('disabled', true)
      $("#new_member").val("")
      $('#members').append('
' + user + '
')
    }

    $('#add_member').click(add_member)
    $('#new_member').keypress(function(e){
      if (e.which == 13) {
        add_member();
      }
    })
  })
})(jQuery)
<|endoftext|>


<commit_before>/**
 * Worker serving as main web application
 * Serves web/API requests
 * */
const config = require('../config');
const utility = require('../util/utility');
const redis = require('../store/redis');
const db = require('../store/db');
const queries = require('../store/queries');
const api = require('../routes/api');
const keys = require('../routes/keyManagement');
const request = require('request');
const compression = require('compression');
const session = require('cookie-session');
const moment = require('moment');
const express = require('express');
const passport = require('passport');
const SteamStrategy = require('passport-steam').Strategy;
const cors = require('cors');

const { redisCount } = utility;

const app = express();
const apiKey = config.STEAM_API_KEY.split(',')[0];
const host = config.ROOT_URL;

const sessOptions = {
  domain: config.COOKIE_DOMAIN,
  maxAge: 52 * 7 * 24 * 60 * 60 * 1000,
  secret: config.SESSION_SECRET,
};
// PASSPORT config
passport.serializeUser((user, done) => {
  done(null, user.account_id);
});
passport.deserializeUser((accountId, done) => {
  done(null, {
    account_id: accountId,
  });
});
passport.use(new SteamStrategy({
  returnURL: `${host}/return`,
  realm: host,
  apiKey,
}, (identifier, profile, cb) => {
  const player = profile._json;
  player.last_login = new Date();
  queries.insertPlayer(db, player, (err) => {
    if (err) {
      return cb(err);
    }
    return cb(err, player);
  });
}));
// Compression middleware
app.use(compression());
// Dota 2 images middleware (proxy to Dota 2 CDN to serve over https)
app.use('/apps', (req, res) => {
  request(`http://cdn.dota2.com/${req.originalUrl}`).pipe(res);
});
// Proxy to serve team logos over https
app.use('/ugc', (req, res) => {
  request(`http://cloud-3.steamusercontent.com/${req.originalUrl}`)
    .on('response', (resp) => {
      resp.headers['content-type'] = 'image/png';
    })
    .pipe(res);
});
// Health check
app.route('/healthz').get((req, res) => {
  res.send('ok');
});
// Session/Passport middleware
app.use(session(sessOptions));
app.use(passport.initialize());
app.use(passport.session());

// Dummy User ID for testing
if (config.NODE_ENV === 'test') {
  app.use((req, res, cb) => {
    if (req.query.loggedin) {
      req.user = {
        account_id: 1,
      };
    }

    cb();
  });
}

// Rate limiter and API key middleware
app.use((req, res, cb) => {
  if (config.ENABLE_API_LIMIT && req.query.api_key) {
    redis.sismember('api_keys', req.query.api_key, (err, resp) => {
      if (err) {
        cb(err);
      } else {
        res.locals.isAPIRequest = resp === 1;
        cb();
      }
    });
  } else {
    cb();
  }
});
app.use((req, res, cb) => {
  let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || '';
  [ip] = ip.replace(/^.*:/, '').split(',');

  let identifier = '';
  let rateLimit = '';

  if (res.locals.isAPIRequest) {
    const requestAPIKey = req.query.api_key;
    identifier = `API:${ip}:${requestAPIKey}`;
    rateLimit = config.API_KEY_PER_MIN_LIMIT;
    console.log('[KEY] %s visit %s, ip %s', requestAPIKey, req.originalUrl, ip);
  } else {
    identifier = `USER:${ip}:${req.user ? req.user.account_id : ''}`;
    rateLimit = config.NO_API_KEY_PER_MIN_LIMIT;
    console.log('[USER] %s visit %s, ip %s', req.user ? req.user.account_id : 'anonymous', req.originalUrl, ip);
  }

  redis.multi()
    .hincrby('rate_limit', identifier, 1)
    .expireat('rate_limit', utility.getStartOfBlockMinutes(1, 1))
    .hincrby('usage_count', identifier, 1)
    .expireat('usage_count', utility.getEndOfMonth())
    .exec((err, resp) => {
      if (err) {
        console.log(err);
        return cb();
      }
      if (config.NODE_ENV === 'development') {
        console.log(resp);
      }
      if (resp[0] > rateLimit && config.NODE_ENV !== 'test') {
        return res.status(429).json({
          error: 'rate limit exceeded',
        });
      }
      if (config.ENABLE_API_LIMIT && !res.locals.isAPIRequest && resp[2] > config.API_FREE_LIMIT) {
        return res.status(429).json({
          error: 'monthly api limit exeeded',
        });
      }

      return cb();
    });
});
// Telemetry middleware
app.use((req, res, cb) => {
  const timeStart = new Date();
  res.once('finish', () => {
    const timeEnd = new Date();
    const elapsed = timeEnd - timeStart;
    if (elapsed > 1000 || config.NODE_ENV === 'development') {
      console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed);
    }
    if (req.originalUrl.indexOf('/api') === 0) {
      redisCount(redis, 'api_hits');
      if (req.headers.origin === 'https://www.opendota.com') {
        redisCount(redis, 'api_hits_ui');
      }
      redis.zincrby('api_paths', 1, req.path.split('/')[1] || '');
      redis.expireat('api_paths', moment().startOf('hour').add(1, 'hour').format('X'));
    }
    if (req.user && req.user.account_id) {
      redis.zadd('visitors', moment().format('X'), req.user.account_id);
    }
    if (req.user && req.user.account_id) {
      redis.zadd('tracked', moment().add(config.UNTRACK_DAYS, 'days').format('X'), req.user.account_id);
    }
    redis.lpush('load_times', elapsed);
    redis.ltrim('load_times', 0, 9999);
  });
  cb();
});
// CORS headers
app.use(cors({
  origin: true,
  credentials: true,
}));
app.route('/login').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}));
app.route('/return').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}), (req, res) => {
  if (config.UI_HOST) {
    return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`);
  }
  return res.redirect('/api');
});
app.route('/logout').get((req, res) => {
  req.logout();
  req.session = null;
  if (config.UI_HOST) {
    return res.redirect(config.UI_HOST);
  }
  return res.redirect('/api');
});
app.use('/api', api);
// CORS Preflight for API keys
app.options('/keys', cors());
app.use('/keys', keys);
// 404 route
app.use((req, res) =>
  res.status(404).json({
    error: 'Not Found',
  }));
// 500 route
app.use((err, req, res, cb) => {
  if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') {
    // default express handler
    return cb(err);
  }
  return res.status(500).json({
    error: 'Internal Server Error',
  });
});
const port = config.PORT || config.FRONTEND_PORT;
const server = app.listen(port, () => {
  console.log('[WEB] listening on %s', port);
});
/**
 * Wait for connections to end, then shut down
 * */
function gracefulShutdown() {
  console.log('Received kill signal, shutting down gracefully.');
  server.close(() => {
    console.log('Closed out remaining connections.');
    process.exit();
  });
  // if after
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
}
// listen for TERM signal .e.g. kill
process.once('SIGTERM', gracefulShutdown);
// listen for INT signal e.g. Ctrl-C
process.once('SIGINT', gracefulShutdown);
module.exports = app;
<commit_msg>decrement API usage if we don't send a good request back<commit_after>/**
 * Worker serving as main web application
 * Serves web/API requests
 * */
const config = require('../config');
const utility = require('../util/utility');
const redis = require('../store/redis');
const db = require('../store/db');
const queries = require('../store/queries');
const api = require('../routes/api');
const keys = require('../routes/keyManagement');
const request = require('request');
const compression = require('compression');
const session = require('cookie-session');
const moment = require('moment');
const express = require('express');
const passport = require('passport');
const SteamStrategy = require('passport-steam').Strategy;
const cors = require('cors');

const { redisCount } = utility;

const app = express();
const apiKey = config.STEAM_API_KEY.split(',')[0];
const host = config.ROOT_URL;

const sessOptions = {
  domain: config.COOKIE_DOMAIN,
  maxAge: 52 * 7 * 24 * 60 * 60 * 1000,
  secret: config.SESSION_SECRET,
};
// PASSPORT config
passport.serializeUser((user, done) => {
  done(null, user.account_id);
});
passport.deserializeUser((accountId, done) => {
  done(null, {
    account_id: accountId,
  });
});
passport.use(new SteamStrategy({
  returnURL: `${host}/return`,
  realm: host,
  apiKey,
}, (identifier, profile, cb) => {
  const player = profile._json;
  player.last_login = new Date();
  queries.insertPlayer(db, player, (err) => {
    if (err) {
      return cb(err);
    }
    return cb(err, player);
  });
}));
// Compression middleware
app.use(compression());
// Dota 2 images middleware (proxy to Dota 2 CDN to serve over https)
app.use('/apps', (req, res) => {
  request(`http://cdn.dota2.com/${req.originalUrl}`).pipe(res);
});
// Proxy to serve team logos over https
app.use('/ugc', (req, res) => {
  request(`http://cloud-3.steamusercontent.com/${req.originalUrl}`)
    .on('response', (resp) => {
      resp.headers['content-type'] = 'image/png';
    })
    .pipe(res);
});
// Health check
app.route('/healthz').get((req, res) => {
  res.send('ok');
});
// Session/Passport middleware
app.use(session(sessOptions));
app.use(passport.initialize());
app.use(passport.session());

// Dummy User ID for testing
if (config.NODE_ENV === 'test') {
  app.use((req, res, cb) => {
    if (req.query.loggedin) {
      req.user = {
        account_id: 1,
      };
    }

    cb();
  });
}

// Rate limiter and API key middleware
let usageIdentifier = '';
app.use((req, res, cb) => {
  if (config.ENABLE_API_LIMIT && req.query.api_key) {
    redis.sismember('api_keys', req.query.api_key, (err, resp) => {
      if (err) {
        cb(err);
      } else {
        res.locals.isAPIRequest = resp === 1;
        cb();
      }
    });
  } else {
    cb();
  }
});
app.use((req, res, cb) => {
  let ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress || '';
  [ip] = ip.replace(/^.*:/, '').split(',');

  let rateLimit = '';
  if (res.locals.isAPIRequest) {
    const requestAPIKey = req.query.api_key;
    usageIdentifier = `API:${ip}:${requestAPIKey}`;
    rateLimit = config.API_KEY_PER_MIN_LIMIT;
    console.log('[KEY] %s visit %s, ip %s', requestAPIKey, req.originalUrl, ip);
  } else {
    usageIdentifier = `USER:${ip}:${req.user ? req.user.account_id : ''}`;
    rateLimit = config.NO_API_KEY_PER_MIN_LIMIT;
    console.log('[USER] %s visit %s, ip %s', req.user ? req.user.account_id : 'anonymous', req.originalUrl, ip);
  }

  redis.multi()
    .hincrby('rate_limit', usageIdentifier, 1)
    .expireat('rate_limit', utility.getStartOfBlockMinutes(1, 1))
    .hincrby('usage_count', usageIdentifier, 1)
    .expireat('usage_count', utility.getEndOfMonth())
    .exec((err, resp) => {
      if (err) {
        console.log(err);
        return cb(err);
      }
      if (config.NODE_ENV === 'development') {
        console.log(resp);
      }
      if (resp[0] > rateLimit && config.NODE_ENV !== 'test') {
        return res.status(429).json({
          error: 'rate limit exceeded',
        });
      }
      if (config.ENABLE_API_LIMIT && !res.locals.isAPIRequest && resp[2] > config.API_FREE_LIMIT) {
        return res.status(429).json({
          error: 'monthly api limit exeeded',
        });
      }

      return cb();
    });
});
// Telemetry middleware
app.use((req, res, cb) => {
  const timeStart = new Date();
  res.once('finish', () => {
    const timeEnd = new Date();
    const elapsed = timeEnd - timeStart;
    if (elapsed > 1000 || config.NODE_ENV === 'development') {
      console.log('[SLOWLOG] %s, %s', req.originalUrl, elapsed);
    }
    if (req.originalUrl.indexOf('/api') === 0) {
      redisCount(redis, 'api_hits');
      if (req.headers.origin === 'https://www.opendota.com') {
        redisCount(redis, 'api_hits_ui');
      }
      redis.zincrby('api_paths', 1, req.path.split('/')[1] || '');
      redis.expireat('api_paths', moment().startOf('hour').add(1, 'hour').format('X'));
    }
    if (req.user && req.user.account_id) {
      redis.zadd('visitors', moment().format('X'), req.user.account_id);
    }
    if (req.user && req.user.account_id) {
      redis.zadd('tracked', moment().add(config.UNTRACK_DAYS, 'days').format('X'), req.user.account_id);
    }
    redis.lpush('load_times', elapsed);
    redis.ltrim('load_times', 0, 9999);
  });
  cb();
});
// CORS headers
app.use(cors({
  origin: true,
  credentials: true,
}));
app.route('/login').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}));
app.route('/return').get(passport.authenticate('steam', {
  failureRedirect: '/api',
}), (req, res) => {
  if (config.UI_HOST) {
    return res.redirect(`${config.UI_HOST}/players/${req.user.account_id}`);
  }
  return res.redirect('/api');
});
app.route('/logout').get((req, res) => {
  req.logout();
  req.session = null;
  if (config.UI_HOST) {
    return res.redirect(config.UI_HOST);
  }
  return res.redirect('/api');
});
app.use('/api', api);
// CORS Preflight for API keys
app.options('/keys', cors());
app.use('/keys', keys);
// These routes get hit if there was a 404 or 500.
// Decrement usage since we didn't give a good resposne.
app.use((req, res, cb) => {
  redis.multi()
    .hincrby('usage_count', usageIdentifier, -1)
    .expireat('usage_count', utility.getEndOfMonth())
    .exec(err => (err ? cb(err) : cb()));
});
// 404 route
app.use((req, res) =>
  res.status(404).json({
    error: 'Not Found',
  }));
// 500 route
app.use((err, req, res, cb) => {
  if (config.NODE_ENV === 'development' || config.NODE_ENV === 'test') {
    // default express handler
    return cb(err);
  }
  return res.status(500).json({
    error: 'Internal Server Error',
  });
});
const port = config.PORT || config.FRONTEND_PORT;
const server = app.listen(port, () => {
  console.log('[WEB] listening on %s', port);
});
/**
 * Wait for connections to end, then shut down
 * */
function gracefulShutdown() {
  console.log('Received kill signal, shutting down gracefully.');
  server.close(() => {
    console.log('Closed out remaining connections.');
    process.exit();
  });
  // if after
  setTimeout(() => {
    console.error('Could not close connections in time, forcefully shutting down');
    process.exit();
  }, 10 * 1000);
}
// listen for TERM signal .e.g. kill
process.once('SIGTERM', gracefulShutdown);
// listen for INT signal e.g. Ctrl-C
process.once('SIGINT', gracefulShutdown);
module.exports = app;
<|endoftext|>


<commit_before>/**
 * Borra registros anteriores y carga los nuevos
 * @param url
 */
var cargaRegistrosSnib = function(url)
{
    snibLayer = L.markerClusterGroup({ chunkedLoading: true, spiderfyDistanceMultiplier: 2,
        spiderLegPolylineOptions: { weight: 1.5, color: 'white', opacity: 0.5 }
    });

    borraRegistrosAnterioresSnib();
    geojsonSnib(url);
};

/**
 * Borra registros anteriores
 */
var borraRegistrosAnterioresSnib = function()
{
    if (map.hasLayer(snibLayer))
    {
        map.removeControl(legend_control);
        map.removeLayer(markersLayer);
        snibLayer = L.markerClusterGroup({ chunkedLoading: true, spiderfyDistanceMultiplier: 2,
            spiderLegPolylineOptions: { weight: 1.5, color: 'white', opacity: 0.5 }
        });
    } else {
        snibLayer = L.markerClusterGroup({
            chunkedLoading: true, spiderfyDistanceMultiplier: 2,
            spiderLegPolylineOptions: {weight: 1.5, color: 'white', opacity: 0.5}
        });
    }
};

/**
 * La simbologia dentro del mapa
 * @param con_conteo
 */
var leyendaSnib = function()
{
    legend_control = L.control.layers({}, {}, {collapsed: false, position: 'bottomleft'}).addTo(map);

    legend_control.addOverlay(snibLayer,
        'Registros del SNIB
(museos, colectas y proyectos) ' + registros_conteo + ''
    );

    legend_control.addOverlay(coleccionesLayer,
        'Especímenes en colecciones ' + colecciones_conteo + ''
    );

    legend_control.addOverlay(observacionesLayer,
        'Observaciones de aVerAves ' + observaciones_conteo + ''
    );

    legend_control.addOverlay(fosilesLayer,
        'Fósiles ' + fosiles_conteo + ''
    );

    legend_control.addOverlay(noCampoLayer,
        'Localidad no de campo ' + no_campo_conteo + ''
    );
};

/**
 * Añade los puntos en forma de fuentes
 */
var aniadePuntosSnib = function()
{
    var geojsonFeature =  { "type": "FeatureCollection", "features": allowedPoints.values()};

    var colecciones = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 1)  // Este campos quiere decir que es el deafult de la coleccion
                {
                    colecciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            } else {
                if (!feature.properties.d.coleccion.toLowerCase().includes('averaves') && !feature.properties.d.coleccion.toLowerCase().includes('ebird')
                && feature.properties.d.ejemplarfosil != 'SI')
                {
                    colecciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var observaciones = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 2)  // Este campos quiere decir que es de aves aves
                {
                    observaciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            } else {
                if (feature.properties.d.coleccion.toLowerCase().includes('averaves') || feature.properties.d.coleccion.toLowerCase().includes('ebird'))
                {
                    observaciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var fosiles = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 3)  // Este campos quiere decir que es de fosiles
                {
                    fosiles_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            } else {
                if (feature.properties.d.ejemplarfosil == 'SI')
                {
                    fosiles_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var noCampo = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 4)  // Este campos quiere decir que es de locacion no de campo
                {
                    no_campo_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            } else {
                if (feature.properties.d.ejemplarfosil == 'SI')
                {
                    no_campo_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    coleccionesLayer = L.featureGroup.subGroup(snibLayer, colecciones);
    coleccionesLayer.addLayer(colecciones);
    observacionesLayer = L.featureGroup.subGroup(snibLayer, observaciones);
    observacionesLayer.addLayer(observaciones);
    fosilesLayer = L.featureGroup.subGroup(snibLayer, fosiles);
    fosilesLayer.addLayer(fosiles);
    noCampoLayer = L.featureGroup.subGroup(snibLayer, noCampo);
    noCampoLayer.addLayer(noCampo);

    map.addLayer(snibLayer);
    map.addLayer(coleccionesLayer);
    map.addLayer(observacionesLayer);
    map.addLayer(fosilesLayer);
    leyendaSnib(true);
};

/**
 * Hace una ajax request para la obtener la información de un taxon, esto es más rapido para muchos registros
 * */
var ejemplarSnibGeojson = function(layer, id)
{
    $.ajax({
        url: "/especies/" + opciones.taxon + "/ejemplar-snib/" + id,
        dataType : "json",
        success : function (res){
            if (res.estatus)
            {
                var contenido = ejemplarSnib(res.ejemplar);
                layer.bindPopup(contenido);
                layer.openPopup();
            }
            else
                console.log("Hubo un error al retraer el ejemplar: " + res.msg);
        },
        error: function( jqXHR ,  textStatus,  errorThrown ){
            console.log("error: " + textStatus);
            console.log(errorThrown);
            console.log(jqXHR.responseText);
        }
    });  // termina ajax
};

/**
 * Lanza el pop up con la inforamcion del taxon, ya esta cargado; este metodo es lento con muchos registros
 * */
var ejemplarSnib = function(prop)
{
    // Sustituye las etiquetas h5 por h4 y centra el texto
    var nombre_f = $('<textarea/>').html(opciones.nombre).text().replace(/<h5/g, "<h4 class='text-center'").replace(/<\/h5/g, "</h4");
    var contenido = "";

    contenido += "" + nombre_f + "";
    contenido += "
Localidad:

" + prop.localidad + "
";
    contenido += "
Municipio:

" + prop.municipiomapa + "
";
    contenido += "
Estado:

" + prop.estadomapa + "
";
    contenido += "
País:

" + prop.paismapa + "
";
    contenido += "
Fecha:

" + prop.fechacolecta + "
";
    contenido += "
Colector:

" + prop.colector + "
";
    contenido += "
Colección:

" + prop.coleccion + "
";
    contenido += "
Institución:

" + prop.institucion + "
";
    contenido += "
País de la colección:

" + prop.paiscoleccion + "
";

    if (prop.proyecto.length > 0 && prop.urlproyecto.length > 0)
        contenido += "
Proyecto:

" + prop.proyecto + "
";

    contenido += "
Más información:

consultar
";

    //Para enviar un comentario acerca de un registro en particular
    contenido += "
¿Tienes un comentario?:

redactar
";

    return "
" + contenido + "
" + "ID SNIB: " + prop.idejemplar;
};

/**
 * Carga el geojson para iterarlo
 * @param url
 */
var geojsonSnib = function(url)
{
    $.ajax({
        url: url,
        dataType : "json",
        success : function (d){
            registros_conteo = d.length;
            colecciones_conteo = 0;
            observaciones_conteo = 0;
            fosiles_conteo = 0;
            no_campo_conteo = 0;

            allowedPoints = d3.map([]);

            for(var i=0;i<d.length;i++)
            {
                var item_id = 'geo-' + i.toString();

                if (opciones.solo_coordenadas)
                {
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: [d[i][2], d[i][3]]},
                        "geometry"  : {coordinates: [d[i][0], d[i][1]], type: "Point"}
                    });
                } else {
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : JSON.parse(d[i].json_geom)
                    });
                }
            }

            aniadePuntosSnib();
        },
        error: function( jqXHR ,  textStatus,  errorThrown ){
            console.log("error: " + textStatus);
            console.log(errorThrown);
            console.log(jqXHR.responseText);
        }
    });
};<commit_msg>ligero bug<commit_after>/**
 * Borra registros anteriores y carga los nuevos
 * @param url
 */
var cargaRegistrosSnib = function(url)
{
    snibLayer = L.markerClusterGroup({ chunkedLoading: true, spiderfyDistanceMultiplier: 2,
        spiderLegPolylineOptions: { weight: 1.5, color: 'white', opacity: 0.5 }
    });

    borraRegistrosAnterioresSnib();
    geojsonSnib(url);
};

/**
 * Borra registros anteriores
 */
var borraRegistrosAnterioresSnib = function()
{
    if (map.hasLayer(snibLayer))
    {
        map.removeControl(legend_control);
        map.removeLayer(markersLayer);
        snibLayer = L.markerClusterGroup({ chunkedLoading: true, spiderfyDistanceMultiplier: 2,
            spiderLegPolylineOptions: { weight: 1.5, color: 'white', opacity: 0.5 }
        });
    } else {
        snibLayer = L.markerClusterGroup({
            chunkedLoading: true, spiderfyDistanceMultiplier: 2,
            spiderLegPolylineOptions: {weight: 1.5, color: 'white', opacity: 0.5}
        });
    }
};

/**
 * La simbologia dentro del mapa
 * @param con_conteo
 */
var leyendaSnib = function()
{
    legend_control = L.control.layers({}, {}, {collapsed: false, position: 'bottomleft'}).addTo(map);

    legend_control.addOverlay(snibLayer,
        'Registros del SNIB
(museos, colectas y proyectos) ' + registros_conteo + ''
    );

    legend_control.addOverlay(coleccionesLayer,
        'Especímenes en colecciones ' + colecciones_conteo + ''
    );

    legend_control.addOverlay(observacionesLayer,
        'Observaciones de aVerAves ' + observaciones_conteo + ''
    );

    legend_control.addOverlay(fosilesLayer,
        'Fósiles ' + fosiles_conteo + ''
    );

    legend_control.addOverlay(noCampoLayer,
        'Localidad no de campo ' + no_campo_conteo + ''
    );
};

/**
 * Añade los puntos en forma de fuentes
 */
var aniadePuntosSnib = function()
{
    var geojsonFeature =  { "type": "FeatureCollection", "features": allowedPoints.values()};

    var colecciones = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 1)  // Este campos quiere decir que es el deafult de la coleccion
                {
                    colecciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            } else {
                if (!feature.properties.d.coleccion.toLowerCase().includes('averaves') && !feature.properties.d.coleccion.toLowerCase().includes('ebird')
                && feature.properties.d.ejemplarfosil.toLowerCase() != 'si' && feature.properties.d.probablelocnodecampo.toLowerCase() != 'si')
                {
                    colecciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var observaciones = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 2)  // Este campos quiere decir que es de aves aves
                {
                    observaciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            } else {
                if (feature.properties.d.coleccion.toLowerCase().includes('averaves') || feature.properties.d.coleccion.toLowerCase().includes('ebird'))
                {
                    observaciones_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var fosiles = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 3)  // Este campos quiere decir que es de fosiles
                {
                    fosiles_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            } else {
                if (feature.properties.d.ejemplarfosil.toLowerCase() == 'si')
                {
                    fosiles_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    var noCampo = L.geoJson(geojsonFeature, {
        pointToLayer: function (feature, latlng) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                if (feature.properties.d[1] == 4)  // Este campos quiere decir que es de locacion no de campo
                {
                    no_campo_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            } else {
                if (feature.properties.d.probablelocnodecampo.toLowerCase() == 'si')
                {
                    no_campo_conteo++;
                    return L.marker(latlng, {icon: L.divIcon({className: 'div-icon-snib-default', html: ''})});
                }
            }
        },
        onEachFeature: function (feature, layer) {
            // Para distinguir si son solo las coordenadas
            if (opciones.solo_coordenadas)
            {
                layer.on("click", function () {
                    ejemplarSnibGeojson(layer, feature.properties.d[0]);
                });
            } else
                layer.bindPopup(ejemplarSnib(feature.properties.d));
        }
    });

    coleccionesLayer = L.featureGroup.subGroup(snibLayer, colecciones);
    coleccionesLayer.addLayer(colecciones);
    observacionesLayer = L.featureGroup.subGroup(snibLayer, observaciones);
    observacionesLayer.addLayer(observaciones);
    fosilesLayer = L.featureGroup.subGroup(snibLayer, fosiles);
    fosilesLayer.addLayer(fosiles);
    noCampoLayer = L.featureGroup.subGroup(snibLayer, noCampo);
    noCampoLayer.addLayer(noCampo);

    map.addLayer(snibLayer);
    map.addLayer(coleccionesLayer);
    map.addLayer(observacionesLayer);
    map.addLayer(fosilesLayer);
    leyendaSnib(true);
};

/**
 * Hace una ajax request para la obtener la información de un taxon, esto es más rapido para muchos registros
 * */
var ejemplarSnibGeojson = function(layer, id)
{
    $.ajax({
        url: "/especies/" + opciones.taxon + "/ejemplar-snib/" + id,
        dataType : "json",
        success : function (res){
            if (res.estatus)
            {
                var contenido = ejemplarSnib(res.ejemplar);
                layer.bindPopup(contenido);
                layer.openPopup();
            }
            else
                console.log("Hubo un error al retraer el ejemplar: " + res.msg);
        },
        error: function( jqXHR ,  textStatus,  errorThrown ){
            console.log("error: " + textStatus);
            console.log(errorThrown);
            console.log(jqXHR.responseText);
        }
    });  // termina ajax
};

/**
 * Lanza el pop up con la inforamcion del taxon, ya esta cargado; este metodo es lento con muchos registros
 * */
var ejemplarSnib = function(prop)
{
    // Sustituye las etiquetas h5 por h4 y centra el texto
    var nombre_f = $('<textarea/>').html(opciones.nombre).text().replace(/<h5/g, "<h4 class='text-center'").replace(/<\/h5/g, "</h4");
    var contenido = "";

    contenido += "" + nombre_f + "";
    contenido += "
Localidad:

" + prop.localidad + "
";
    contenido += "
Municipio:

" + prop.municipiomapa + "
";
    contenido += "
Estado:

" + prop.estadomapa + "
";
    contenido += "
País:

" + prop.paismapa + "
";
    contenido += "
Fecha:

" + prop.fechacolecta + "
";
    contenido += "
Colector:

" + prop.colector + "
";
    contenido += "
Colección:

" + prop.coleccion + "
";
    contenido += "
Institución:

" + prop.institucion + "
";
    contenido += "
País de la colección:

" + prop.paiscoleccion + "
";

    if (prop.proyecto.length > 0 && prop.urlproyecto.length > 0)
        contenido += "
Proyecto:

" + prop.proyecto + "
";

    contenido += "
Más información:

consultar
";

    //Para enviar un comentario acerca de un registro en particular
    contenido += "
¿Tienes un comentario?:

redactar
";

    return "
" + contenido + "
" + "ID SNIB: " + prop.idejemplar;
};

/**
 * Carga el geojson para iterarlo
 * @param url
 */
var geojsonSnib = function(url)
{
    $.ajax({
        url: url,
        dataType : "json",
        success : function (d){
            registros_conteo = d.length;
            colecciones_conteo = 0;
            observaciones_conteo = 0;
            fosiles_conteo = 0;
            no_campo_conteo = 0;

            allowedPoints = d3.map([]);

            for(var i=0;i<d.length;i++)
            {
                var item_id = 'geo-' + i.toString();

                if (opciones.solo_coordenadas)
                {
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: [d[i][2], d[i][3]]},
                        "geometry"  : {coordinates: [d[i][0], d[i][1]], type: "Point"}
                    });
                } else {
                    allowedPoints.set(item_id, {
                        "type"      : "Feature",
                        "properties": {d: d[i]},
                        "geometry"  : JSON.parse(d[i].json_geom)
                    });
                }
            }

            aniadePuntosSnib();
        },
        error: function( jqXHR ,  textStatus,  errorThrown ){
            console.log("error: " + textStatus);
            console.log(errorThrown);
            console.log(jqXHR.responseText);
        }
    });
};<|endoftext|>


<commit_before>// SVG Path transformations library
//
// Usage:
//
//    SvgPath('...')
//      .translate(-150, -100)
//      .scale(0.5)
//      .translate(-150, -100)
//      .toFixed(1)
//      .toString()
//

'use strict';

// Class constructor
//
function SvgPath(pathString) {
  if (!(this instanceof SvgPath)) { return new SvgPath(pathString); }

  // Array of path segments.
  // Each segment is array [command, param1, param2, ...]
  this.segments = this.parsePath(pathString);
}

var pathCommands = /[MmZzLlHhVvCcSsQqTtAa][^MmZzLlHhVvCcSsQqTtAa]*/gm;
var pathValues = /-?[^\s,-]+/gm;

// Parser code is shamelessly borrowed from Raphael
// https://github.com/DmitryBaranovskiy/raphael/
//
SvgPath.prototype.parsePath = function(pathString) {

  if (!pathString) { return []; }

  var data = [];
  var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };

  pathString.replace(pathCommands, function(segmentString) {
    var cmd = segmentString[0]; // first character of segment is a command name
    var paramsStr = segmentString.substr(1); // get command parameters - all except command name
    var cmdLowerCase = cmd.toLowerCase();
    var params = [];

    paramsStr.replace(pathValues, function(paramStr) {
      params.push(+paramStr);
    });

    if (cmdLowerCase === "m" && params.length > 2) {
      data.push([cmd].concat(params.splice(0, 2)));
      cmdLowerCase = "l";
      cmd = (cmd === "m") ? "l" : "L";
    }

    if (cmdLowerCase === "r") {
      data.push([cmd].concat(params));
    } else {

      while (params.length >= paramCounts[cmdLowerCase]) {
        data.push([cmd].concat(params.splice(0, paramCounts[cmdLowerCase])));
        if (!paramCounts[cmdLowerCase]) {
          break;
        }
      }
    }
  });

  // First command MUST be always M or m.
  // Make it always M, to avoid unnecesary checks in translate/abs
  if (data[0][0].toLowerCase() !== 'm') {
    data = [];
  } else {
    data[0][0] = 'M';
  }

  return data;
};

// Convert processed SVG Path back to string
//
SvgPath.prototype.toString = function() {
  return [].concat.apply([], this.segments).join(' ')
    // Optimizations
    .replace(/ ?([achlmqrstvxz]) ?/gi, '$1')
    // workaround for Fontforge importing bug
    .replace(/zm/g, 'z m')
    .replace(/ \-/g, '-');
};

// Translate coords to (x [, y])
//
SvgPath.prototype.translate = function(x, y) {
  y = y || 0;

  this.segments.forEach(function(segment) {

    var cmd = segment[0];

    // Shift coords only for commands with absolute values
    if ('ACHLMRQSTVZ'.indexOf(cmd) === -1) { return; }

    var name   = cmd.toLowerCase();

    // V is the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] += y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'a') {
      segment[6] += x;
      segment[7] += y;
      return;
    }

    // All other commands have [cmd, x1, y1, x2, y2, x3, y3, ...] format
    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val + x : val + y;
    });
  });

  return this;
};

// Scale coords to (sx [, sy])
// sy = sx if not defined
//
SvgPath.prototype.scale = function(sx, sy) {
  sy = (!sy && (sy !== 0)) ? sx : sy;

  this.segments.forEach(function(segment) {

    var name   = segment[0].toLowerCase();

    // V & v are the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] *= sy;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch rx, ry, x, y only
    if (name === 'a') {
      segment[1] *= sx;
      segment[2] *= sy;
      segment[6] *= sx;
      segment[7] *= sy;
      return;
    }

    // All other commands have [cmd, x1, y1, x2, y2, x3, y3, ...] format
    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] *= i % 2 ? sx : sy;
    });
  });

  return this;
};

// Round coords with given decimal precition.
// 0 by default (to integers)
//
SvgPath.prototype.round = function(d) {
  d = d || 0;

  this.segments.forEach(function(segment) {

    // Special processing for ARC:
    // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // don't touch flags and rotation
    if (segment[0].toLowerCase() === 'a') {
      segment[1] = segment[1].toFixed(d);
      segment[2] = segment[2].toFixed(d);
      segment[6] = segment[6].toFixed(d);
      segment[7] = segment[7].toFixed(d);
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; }
      segment[i] = +segment[i].toFixed(d);
    });

  });

  return this;
};

// Apply iterator function to all segments. If function returns result,
// current segment will be replaced to array of returned segments.
// If empty array is returned, current regment will be deleted.
//
SvgPath.prototype.iterate = function(iterator) {
  var segments = this.segments
    , replaceQueue = []
    , lastX = 0
    , lastY = 0
    , countourStartX = 0
    , countourStartY = 0;
  var index, isRelative;

  segments.forEach(function(segment, index) {

    var res = iterator(segment, index, lastX, lastY);
    if (Array.isArray(res)) {
      replaceQueue.push({ index:index, segments:res });
    }

    // all relative commands except Z
    isRelative = 'achlmqrstv'.indexOf(segment[0]) >= 0;
    var name = segment[0].toLowerCase();

    // calculate absolute X and Y
    if (name === 'm') {
      lastX = segment[1] + (isRelative ? lastX : 0);
      lastY = segment[2] + (isRelative ? lastY : 0);
      countourStartX = lastX;
      countourStartY = lastY;
      return;
    }

    if (name === 'h') {
      lastX = segment[1] + (isRelative ? lastX : 0);
      return;
    }

    if (name === 'v') {
      lastY = segment[1] + (isRelative ? lastY : 0);
      return;
    }

    if (name === 'z') {
      lastX = countourStartX;
      lastY = countourStartY;
      return;
    }

    lastX = segment[segment.length - 2] + (isRelative ? lastX : 0);
    lastY = segment[segment.length - 1] + (isRelative ? lastY : 0);
  });

  // Replace segments if iterator return results
  var len = replaceQueue.length;
  for (index = len - 1; index >= 0 ; index--) {
    segments.splice(replaceQueue[index].index, 1);

    // FIXME: Replace cycle with `apply`
    var newSegLen = replaceQueue[index].segments.length;
    for (var newSegIndex = newSegLen - 1; newSegIndex >= 0 ; newSegIndex--) {
      segments.splice(replaceQueue[index].index, 0, replaceQueue[index].segments[newSegIndex]);
    }
  }

  return this;
};

// Converts segments from relative to absolute
//
SvgPath.prototype.abs = function () {

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    // Skip absolute commands
    if ('ACHLMRQSTVZ'.indexOf(name) >= 0) { return; }

    // absolute commands has uppercase names
    segment[0] = name.toUpperCase();

    // V is the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] += y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'a') {
      segment[6] += x;
      segment[7] += y;
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val + x : val + y; // odd values are X, even - Y
    });

  });

  return this;
};

// Converts segments from absolute to relative
//
SvgPath.prototype.rel = function () {

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    // Skip relative commands
    if ('ACHLMRQSTVZ'.indexOf(name) === -1) { return; }

    // relative commands has lowercase names
    segment[0] = name.toLowerCase();

    // V is the only command, with shifted coords parity
    if (name === 'V') {
      segment[1] -= y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'A') {
      segment[6] -= x;
      segment[7] -= y;
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val - x : val - y; // odd values are X, even - Y
    });

  });

  return this;
};

// Converts smooth curves (with missed control point) to generic curves
//
SvgPath.prototype.unshort = function() {
  var self = this;
  var prevControlX, prevControlY;
  var curControlX, curControlY;

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    if (name === 'T') { // qubic curve
      segment[0] = 'Q';
      prevControlX = self.segments[index - 1][0] === 'Q' ? self.segments[index - 1][1] : x;
      curControlX = 2 * (x || 0) - (prevControlX || 0);
      prevControlY = self.segments[index - 1][0] === 'Q' ? self.segments[index - 1][2] : y;
      curControlY = 2 * (y || 0) - (prevControlY || 0);
      segment.splice(1, 0, curControlX, curControlY);
    } else if (name === 'S') { // quadratic curve

      segment[0] = 'C';
      prevControlX = self.segments[index - 1][0] === 'C' ? self.segments[index - 1][3] : x;
      curControlX = 2 * (x || 0) - (prevControlX || 0);
      prevControlY = self.segments[index - 1][0] === 'C' ? self.segments[index - 1][4] : y;
      curControlY = 2 * (y || 0) - (prevControlY || 0);
      segment.splice(1, 0, curControlX, curControlY);
    }
  });

  return this;
};

module.exports = SvgPath;
<commit_msg>Fixed parse 29.5.5 -> 29.5, 0.5<commit_after>// SVG Path transformations library
//
// Usage:
//
//    SvgPath('...')
//      .translate(-150, -100)
//      .scale(0.5)
//      .translate(-150, -100)
//      .toFixed(1)
//      .toString()
//

'use strict';

// Class constructor
//
function SvgPath(pathString) {
  if (!(this instanceof SvgPath)) { return new SvgPath(pathString); }

  // Array of path segments.
  // Each segment is array [command, param1, param2, ...]
  this.segments = this.parsePath(pathString);
}

var pathCommands = /[MmZzLlHhVvCcSsQqTtAa][^MmZzLlHhVvCcSsQqTtAa]*/gm;
var pathValues = /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ig;

// Parser code is shamelessly borrowed from Raphael
// https://github.com/DmitryBaranovskiy/raphael/
//
SvgPath.prototype.parsePath = function(pathString) {

  if (!pathString) { return []; }

  var data = [];
  var paramCounts = { a: 7, c: 6, h: 1, l: 2, m: 2, r: 4, q: 4, s: 4, t: 2, v: 1, z: 0 };

  pathString.replace(pathCommands, function(segmentString) {
    var cmd = segmentString[0]; // first character of segment is a command name
    var paramsStr = segmentString.substr(1); // get command parameters - all except command name
    var cmdLowerCase = cmd.toLowerCase();
    var params = [];

    paramsStr.replace(pathValues, function(paramStr) {
      params.push(+paramStr);
    });

    if (cmdLowerCase === "m" && params.length > 2) {
      data.push([cmd].concat(params.splice(0, 2)));
      cmdLowerCase = "l";
      cmd = (cmd === "m") ? "l" : "L";
    }

    if (cmdLowerCase === "r") {
      data.push([cmd].concat(params));
    } else {

      while (params.length >= paramCounts[cmdLowerCase]) {
        data.push([cmd].concat(params.splice(0, paramCounts[cmdLowerCase])));
        if (!paramCounts[cmdLowerCase]) {
          break;
        }
      }
    }
  });

  // First command MUST be always M or m.
  // Make it always M, to avoid unnecesary checks in translate/abs
  if (data[0][0].toLowerCase() !== 'm') {
    data = [];
  } else {
    data[0][0] = 'M';
  }

  return data;
};

// Convert processed SVG Path back to string
//
SvgPath.prototype.toString = function() {
  return [].concat.apply([], this.segments).join(' ')
    // Optimizations
    .replace(/ ?([achlmqrstvxz]) ?/gi, '$1')
    // workaround for Fontforge importing bug
    .replace(/zm/g, 'z m')
    .replace(/ \-/g, '-');
};

// Translate coords to (x [, y])
//
SvgPath.prototype.translate = function(x, y) {
  y = y || 0;

  this.segments.forEach(function(segment) {

    var cmd = segment[0];

    // Shift coords only for commands with absolute values
    if ('ACHLMRQSTVZ'.indexOf(cmd) === -1) { return; }

    var name   = cmd.toLowerCase();

    // V is the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] += y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'a') {
      segment[6] += x;
      segment[7] += y;
      return;
    }

    // All other commands have [cmd, x1, y1, x2, y2, x3, y3, ...] format
    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val + x : val + y;
    });
  });

  return this;
};

// Scale coords to (sx [, sy])
// sy = sx if not defined
//
SvgPath.prototype.scale = function(sx, sy) {
  sy = (!sy && (sy !== 0)) ? sx : sy;

  this.segments.forEach(function(segment) {

    var name   = segment[0].toLowerCase();

    // V & v are the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] *= sy;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch rx, ry, x, y only
    if (name === 'a') {
      segment[1] *= sx;
      segment[2] *= sy;
      segment[6] *= sx;
      segment[7] *= sy;
      return;
    }

    // All other commands have [cmd, x1, y1, x2, y2, x3, y3, ...] format
    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] *= i % 2 ? sx : sy;
    });
  });

  return this;
};

// Round coords with given decimal precition.
// 0 by default (to integers)
//
SvgPath.prototype.round = function(d) {
  d = d || 0;

  this.segments.forEach(function(segment) {

    // Special processing for ARC:
    // [cmd, rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // don't touch flags and rotation
    if (segment[0].toLowerCase() === 'a') {
      segment[1] = segment[1].toFixed(d);
      segment[2] = segment[2].toFixed(d);
      segment[6] = segment[6].toFixed(d);
      segment[7] = segment[7].toFixed(d);
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; }
      segment[i] = +segment[i].toFixed(d);
    });

  });

  return this;
};

// Apply iterator function to all segments. If function returns result,
// current segment will be replaced to array of returned segments.
// If empty array is returned, current regment will be deleted.
//
SvgPath.prototype.iterate = function(iterator) {
  var segments = this.segments
    , replaceQueue = []
    , lastX = 0
    , lastY = 0
    , countourStartX = 0
    , countourStartY = 0;
  var index, isRelative;

  segments.forEach(function(segment, index) {

    var res = iterator(segment, index, lastX, lastY);
    if (Array.isArray(res)) {
      replaceQueue.push({ index:index, segments:res });
    }

    // all relative commands except Z
    isRelative = 'achlmqrstv'.indexOf(segment[0]) >= 0;
    var name = segment[0].toLowerCase();

    // calculate absolute X and Y
    if (name === 'm') {
      lastX = segment[1] + (isRelative ? lastX : 0);
      lastY = segment[2] + (isRelative ? lastY : 0);
      countourStartX = lastX;
      countourStartY = lastY;
      return;
    }

    if (name === 'h') {
      lastX = segment[1] + (isRelative ? lastX : 0);
      return;
    }

    if (name === 'v') {
      lastY = segment[1] + (isRelative ? lastY : 0);
      return;
    }

    if (name === 'z') {
      lastX = countourStartX;
      lastY = countourStartY;
      return;
    }

    lastX = segment[segment.length - 2] + (isRelative ? lastX : 0);
    lastY = segment[segment.length - 1] + (isRelative ? lastY : 0);
  });

  // Replace segments if iterator return results
  var len = replaceQueue.length;
  for (index = len - 1; index >= 0 ; index--) {
    segments.splice(replaceQueue[index].index, 1);

    // FIXME: Replace cycle with `apply`
    var newSegLen = replaceQueue[index].segments.length;
    for (var newSegIndex = newSegLen - 1; newSegIndex >= 0 ; newSegIndex--) {
      segments.splice(replaceQueue[index].index, 0, replaceQueue[index].segments[newSegIndex]);
    }
  }

  return this;
};

// Converts segments from relative to absolute
//
SvgPath.prototype.abs = function () {

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    // Skip absolute commands
    if ('ACHLMRQSTVZ'.indexOf(name) >= 0) { return; }

    // absolute commands has uppercase names
    segment[0] = name.toUpperCase();

    // V is the only command, with shifted coords parity
    if (name === 'v') {
      segment[1] += y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'a') {
      segment[6] += x;
      segment[7] += y;
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val + x : val + y; // odd values are X, even - Y
    });

  });

  return this;
};

// Converts segments from absolute to relative
//
SvgPath.prototype.rel = function () {

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    // Skip relative commands
    if ('ACHLMRQSTVZ'.indexOf(name) === -1) { return; }

    // relative commands has lowercase names
    segment[0] = name.toLowerCase();

    // V is the only command, with shifted coords parity
    if (name === 'V') {
      segment[1] -= y;
      return;
    }

    // ARC is: ['A', rx, ry, x-axis-rotation, large-arc-flag, sweep-flag, x, y]
    // touch x, y only
    if (name === 'A') {
      segment[6] -= x;
      segment[7] -= y;
      return;
    }

    segment.forEach(function(val, i) {
      if (!i) { return; } // skip command
      segment[i] = i % 2 ? val - x : val - y; // odd values are X, even - Y
    });

  });

  return this;
};

// Converts smooth curves (with missed control point) to generic curves
//
SvgPath.prototype.unshort = function() {
  var self = this;
  var prevControlX, prevControlY;
  var curControlX, curControlY;

  this.iterate(function(segment, index, x, y) {
    var name = segment[0];

    if (name === 'T') { // qubic curve
      segment[0] = 'Q';
      prevControlX = self.segments[index - 1][0] === 'Q' ? self.segments[index - 1][1] : x;
      curControlX = 2 * (x || 0) - (prevControlX || 0);
      prevControlY = self.segments[index - 1][0] === 'Q' ? self.segments[index - 1][2] : y;
      curControlY = 2 * (y || 0) - (prevControlY || 0);
      segment.splice(1, 0, curControlX, curControlY);
    } else if (name === 'S') { // quadratic curve

      segment[0] = 'C';
      prevControlX = self.segments[index - 1][0] === 'C' ? self.segments[index - 1][3] : x;
      curControlX = 2 * (x || 0) - (prevControlX || 0);
      prevControlY = self.segments[index - 1][0] === 'C' ? self.segments[index - 1][4] : y;
      curControlY = 2 * (y || 0) - (prevControlY || 0);
      segment.splice(1, 0, curControlX, curControlY);
    }
  });

  return this;
};

module.exports = SvgPath;
<|endoftext|>


<commit_before>var express = require('express'),
  mongodb = require('mongodb'),
  app = express(),
  bodyParser = require('body-parser'),
  validator = require('express-validator'),
  logger = require('morgan'),
  errorHandler = require('errorhandler'),
  compression = require('compression'),
  url = 'mongodb://localhost:27017/board'

mongodb.MongoClient.connect(url, function(err, db) {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  app.use(compression())
  app.use(logger('combined'))
  app.use(errorHandler())
  app.use(bodyParser.urlencoded({extended: true}))
  app.use(bodyParser.json())
  app.use(validator())
  app.use(express.static('public'))

  app.use(function(req, res, next){
    req.messages = db.collection('messages')
    return next()
  })

  app.get('/messages', function(req, res, next) {
    req.messages.find({}, {sort: {_id: -1}}).toArray(function(err, docs){
      if (err) return next(err)
      return res.json(docs)
    })
  })
  app.post('/messages', function(req, res, next){
    console.log(req.body)
    req.checkBody('message', 'Invalid message in body').notEmpty()
    req.checkBody('name', 'Invalid name in body').notEmpty()
    var errors = req.validationErrors()

    if (errors) return next(errors)
    req.messages.insert(req.body, function (err, result) {
      if (err) return next(err)
      return res.json(result.ops[0])
    })
  })

  app.get('*', function(req, res, next){
    res.send('Server provides two endpoints GET /messages and POST /messages.\n Use Postman, curl or another client to make HTTP requests.')
  })

  app.listen(3000)
})
<commit_msg>add changes :arrow_heading_up:<commit_after>var express = require('express'),
  mongodb = require('mongodb'),
  app = express(),
  bodyParser = require('body-parser'),
  validator = require('express-validator'),
  logger = require('morgan'),
  errorHandler = require('errorhandler'),
  compression = require('compression'),
  url = 'mongodb://localhost:27017/board'

mongodb.MongoClient.connect(url, function(err, db) {
  if (err) {
    console.error(err)
    process.exit(1)
  }
  app.use(compression())
  app.use(logger('combined'))
  app.use(errorHandler())
  app.use(bodyParser.urlencoded({extended: true}))
  app.use(bodyParser.json())
  app.use(validator())
  app.use(express.static('public'))

  app.use(function(req, res, next){
    req.messages = db.collection('messages')
    return next()
  })

  app.get('/messages', function(req, res, next) {
    req.messages.find({}, {sort: {_id: -1}}).toArray(function(err, docs){
      if (err) return next(err)
      return res.json(docs)
    })
  })
  app.post('/messages', function(req, res, next){
    console.log(req.body)
    req.checkBody('message', 'Invalid message in body').notEmpty()
    req.checkBody('name', 'Invalid name in body').notEmpty()
    var errors = req.validationErrors()
    if (errors) return next(errors)
    req.messages.insert(req.body, function (err, result) {
      if (err) return next(err)
      return res.json(result.ops[0])
    })
  })

  app.get('*', function(req, res, next){
    res.send('Server provides two endpoints GET /messages and POST /messages.\n Use Postman, curl or another client to make HTTP requests.')
  })

  app.listen(3000)
})
<|endoftext|>


<commit_before>/**
 * swapper.js v1.0
 * UI navigation and transition utility
 * Copyright (c) 2012 Kik Interactive, http://kik.com
 * Released under the MIT license
 *
 * 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.
 */

var Swapper = function (window, document, Zepto, jQuery) {
	var NO_TRANSFORM = 'translate3d(0,0,0)';

	var transitions = {
			'fade' : [
				{ fade : true },
				{ fade : true }
			],
			'scale-in' : [
				{ transform : 'scale(0.01)' },
				{ fade      : true          }
			],
			'scale-out' : [
				{ fade      : true          },
				{ transform : 'scale(0.01)' }
			],
			'rotate-left' : [
				{ transform : 'rotateY(-180deg) perspective(360px)' , fade : true },
				{ transform : 'rotateY( 180deg) perspective(360px)' , fade : true }
			],
			'rotate-right' : [
				{ transform : 'rotateY( 180deg) perspective(360px)' , fade : true },
				{ transform : 'rotateY(-180deg) perspective(360px)' , fade : true }
			],
			'cube-left' : [
				{ transform : 'translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)' },
				{ transform : 'translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)' }
			],
			'cube-right' : [
				{ transform : 'translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)' },
				{ transform : 'translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)' }
			],
			'swap-left' : [
				{ transform : 'translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)' },
				{ transform : 'translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)' }
			],
			'swap-right' : [
				{ transform : 'translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)' },
				{ transform : 'translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)' }
			],
			'explode-in' : [
				{ transform : 'scale(1.33)' , fade : true },
				{ transform : 'scale(0.75)' , fade : true }
			],
			'explode-out' : [
				{ transform : 'scale(0.75)' , fade : true },
				{ transform : 'scale(1.33)' , fade : true }
			],
			'slide-left'  : [
				{ transform : 'translate3d( 100%,0,0)' },
				{ transform : 'translate3d(-100%,0,0)' }
			],
			'slide-right'  : [
				{ transform : 'translate3d(-100%,0,0)' },
				{ transform : 'translate3d( 100%,0,0)' }
			],
			'slide-up'  : [
				{ transform : 'translate3d(0, 100%,0)' },
				{ transform : 'translate3d(0,-100%,0)' }
			],
			'slide-down'  : [
				{ transform : 'translate3d(0,-100%,0)' },
				{ transform : 'translate3d(0, 100%,0)' }
			]
		},
		easings     = {
			'linear'      : 'linear'      ,
			'ease'        : 'ease'        ,
			'ease-in'     : 'ease-in'     ,
			'ease-out'    : 'ease-out'    ,
			'ease-in-out' : 'ease-in-out' ,
			'step-start'  : 'step-start'  ,
			'step-end'    : 'step-end'
		};

	function isNode (elem) {
		if ( !elem ) {
			return false;
		}

		try {
			return (elem instanceof Node) || (elem instanceof HTMLElement);
		} catch (err) {}

		if (typeof elem !== 'object') {
			return false;
		}

		if (typeof elem.nodeType !== 'number') {
			return false;
		}

		if (typeof elem.nodeName !== 'string') {
			return false;
		}

		return true;
	}

	function isInDOM (elem, skipNodeCheck) {
		if (!skipNodeCheck && !isNode(elem)) {
			throw TypeError('element must be a DOM node, got ' + elem);
		}

		while (elem = elem.parentNode) {
			if (elem === document) {
				return true;
			}
		}

		return false;
	}

	function insertAfter (newElem, targetElem) {
		var parent = targetElem.parentNode;

		if (parent.lastchild === targetElem) {
			parent.appendChild(newElem);
		}
		else {
			parent.insertBefore(newElem, targetElem.nextSibling);
		}
	}

	function removeNode (elem) {
		if (elem.parentNode) {
			elem.parentNode.removeChild(elem);
		}
	}

	function setTransform (elem, transform) {
		elem.style['-webkit-transform'] = transform;
		elem.style[   '-moz-transform'] = transform;
		elem.style[    '-ms-transform'] = transform;
		elem.style[     '-o-transform'] = transform;
		elem.style[        'transform'] = transform;
	}

	function setTransition (elem, transition) {
		if (transition) {
			elem.style['-webkit-transition'] = '-webkit-'+transition;
			elem.style[   '-moz-transition'] =    '-moz-'+transition;
			elem.style[    '-ms-transition'] =     '-ms-'+transition;
			elem.style[     '-o-transition'] =      '-o-'+transition;
			elem.style[        'transition'] =            transition;
		}
		else {
			elem.style['-webkit-transition'] = '';
			elem.style[   '-moz-transition'] = '';
			elem.style[    '-ms-transition'] = '';
			elem.style[     '-o-transition'] = '';
			elem.style[        'transition'] = '';
		}
	}

	function getStyles (elem, notComputed) {
		var styles;

		if (notComputed) {
			styles = elem.style;
		}
		else {
			styles = document.defaultView.getComputedStyle(elem, null);
		}

		return {
			opacity  : styles.opacity  ,
			top      : styles.top      ,
			left     : styles.left     ,
			height   : styles.height   ,
			width    : styles.width    ,
			position : styles.position
		};
	}

	function validateElement (elem) {
		if ( !isNode(elem) ) {
			throw TypeError('element must be a DOM node, got ' + elem);
		}
	}

	function validateOptions (options) {
		switch (typeof options) {
			case 'string':
				options = { transition: options };
				break;

			case 'undefined':
				options = {};
				break;

			case 'object':
				break;

			default:
				throw TypeError('options must be an object if defined, got ' + options);
		}

		switch (typeof options.transition) {
			case 'string':
				if ( !(options.transition in transitions) ) {
					throw TypeError(options.transition + ' is not a valid transition');
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('transition must be a string if defined, got ' + options.transition);
		}

		switch (typeof options.duration) {
			case 'number':
				if (options.duration < 0) {
					throw TypeError('duration must be a non-negative integer, got ' + options.duration);
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('duration must be a number if defined, got ' + options.duration);
		}

		switch (typeof options.easing) {
			case 'string':
				if ( !(options.easing in easings) ) {
					throw TypeError(options.easing + ' is not a valid easing');
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('easing must be a string if defined, got ' + options.easing);
		}

		return options;
	}

	function validateCallback (callback) {
		switch (typeof callback) {
			case 'undefined':
				callback = function () {};
				// fall through

			case 'function':
				break;

			default:
				throw TypeError('callback must be a function if defined, got ' + callback);
		}

		return callback;
	}

	function swapper (elem1, elem2, options, callback) {
		validateElement(elem1);
		validateElement(elem2);

		if (typeof options === 'function') {
			callback = options;
			options  = {};
		}

		options  = validateOptions(options);
		callback = validateCallback(callback);

		if (elem1._swapper) {
			throw Error('elem1 is currently being swapped');
		}

		if (elem2._swapper) {
			throw Error('elem2 is currently being swapped');
		}

		if ( !isInDOM(elem1, true) ) {
			throw Error('elem1 must be in the DOM to be swapped');
		}

		elem1._swapper = true;
		elem2._swapper = true;

		removeNode(elem2);

		var transition = transitions[options.transition || 'fade'],
			easing     = easings[options.easing || 'ease-in-out'],
			duration   = options.duration || 300;

		insertAfter(elem2, elem1);

		var bounds          = elem1.getBoundingClientRect(),
			computedStyles1 = getStyles(elem1),
			styles1         = getStyles(elem1, true);
			styles2         = getStyles(elem2, true);

		elem2.style.position = 'fixed';
		elem2.style.top      = bounds.top  + 'px';
		elem2.style.left     = bounds.left + 'px';
		elem2.style.height   = computedStyles1.height;
		elem2.style.width    = computedStyles1.width;

		setTransform(elem1, NO_TRANSFORM );
		setTransform(elem2, transition[0].transform || NO_TRANSFORM);

		if ( transition[0].fade ) {
			elem2.style.opacity = '0';
		}

		if ( transition[1].fade ) {
			elem1.style.opacity = '1';
		}

		setTimeout(function () {
			var cssTransition = 'transform '+(duration/1000)+'s '+easing+', opacity '+(duration/1000)+'s '+easing;
			setTransition(elem1, cssTransition);
			setTransition(elem2, cssTransition);

			setTimeout(function () {
				setTransform(elem1, transition[1].transform || NO_TRANSFORM);
				setTransform(elem2, NO_TRANSFORM );

				if ( transition[0].fade ) {
					elem2.style.opacity = '1';
				}

				if ( transition[1].fade ) {
					elem1.style.opacity = '0';
				}

				setTimeout(cleanupElems, duration)
			}, 0);
		}, 0);

		function cleanupElems () {
			removeNode(elem1);

			setTransition(elem1, '');
			setTransition(elem2, '');

			setTimeout(function () {
				setTransform(elem1, '');
				setTransform(elem2, '');

				if ( transition[0].fade ) {
					elem2.style.opacity = styles2.opacity;
				}

				if ( transition[1].fade ) {
					elem1.style.opacity = styles1.opacity;
				}

				elem2.style.position = styles2.position;
				elem2.style.top      = styles2.top;
				elem2.style.left     = styles2.left;
				elem2.style.height   = styles2.height;
				elem2.style.width    = styles2.width;

				elem1._swapper = false;
				elem2._swapper = false;

				callback();
			}, 0);
		}
	}

	if (Zepto) {
		Zepto.extend(Zepto.fn, {
			swapper : function (elem2, options, callback) {
				elem2 = Zepto(elem2)[0];

				this.forEach(function (elem1) {
					swapper(elem1, elem2, options, callback);
				});

				return this;
			}
		});
	}

	if (jQuery) {
		jQuery.fn.swapper = function (elem2, options, callback) {
			elem2 = jQuery(elem2)[0];

			this.each(function () {
				swapper(this, elem2, options, callback);
			});

			return this;
		};
	}

	return swapper;
}(window, document, window.Zepto, window.jQuery);
<commit_msg>instant transition, style var bug, explode is more androidy<commit_after>/**
 * swapper.js v1.0
 * UI navigation and transition utility
 * Copyright (c) 2012 Kik Interactive, http://kik.com
 * Released under the MIT license
 *
 * 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.
 */

var Swapper = function (window, document, Zepto, jQuery) {
	var NO_TRANSFORM = 'translate3d(0,0,0)';

	var transitions = {
			'fade' : [
				{ fade : true },
				{ fade : true }
			],
			'scale-in' : [
				{ transform : 'scale(0.01)' },
				{ fade      : true          }
			],
			'scale-out' : [
				{ fade      : true          },
				{ transform : 'scale(0.01)' }
			],
			'rotate-left' : [
				{ transform : 'rotateY(-180deg) perspective(360px)' , fade : true },
				{ transform : 'rotateY( 180deg) perspective(360px)' , fade : true }
			],
			'rotate-right' : [
				{ transform : 'rotateY( 180deg) perspective(360px)' , fade : true },
				{ transform : 'rotateY(-180deg) perspective(360px)' , fade : true }
			],
			'cube-left' : [
				{ transform : 'translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)' },
				{ transform : 'translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)' }
			],
			'cube-right' : [
				{ transform : 'translate3d(-50%,0,0) rotateY( 90deg) perspective(360px)' },
				{ transform : 'translate3d( 50%,0,0) rotateY(-90deg) perspective(360px)' }
			],
			'swap-left' : [
				{ transform : 'translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)' },
				{ transform : 'translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)' }
			],
			'swap-right' : [
				{ transform : 'translate3d(-65%,0,0) rotateY(-90deg) perspective(360px)' },
				{ transform : 'translate3d( 65%,0,0) rotateY( 90deg) perspective(360px)' }
			],
			'explode-in' : [
				{ fade : true , transform : 'scale(1.25)' },
				{ fade : true }
			],
			'explode-out' : [
				{ fade : true },
				{ fade : true , transform : 'scale(1.25)' }
			],
			'slide-left' : [
				{ transform : 'translate3d( 100%,0,0)' },
				{ transform : 'translate3d(-100%,0,0)' }
			],
			'slide-right' : [
				{ transform : 'translate3d(-100%,0,0)' },
				{ transform : 'translate3d( 100%,0,0)' }
			],
			'slide-up' : [
				{ transform : 'translate3d(0, 100%,0)' },
				{ transform : 'translate3d(0,-100%,0)' }
			],
			'slide-down' : [
				{ transform : 'translate3d(0,-100%,0)' },
				{ transform : 'translate3d(0, 100%,0)' }
			]
		},
		easings     = {
			'linear'      : 'linear'      ,
			'ease'        : 'ease'        ,
			'ease-in'     : 'ease-in'     ,
			'ease-out'    : 'ease-out'    ,
			'ease-in-out' : 'ease-in-out' ,
			'step-start'  : 'step-start'  ,
			'step-end'    : 'step-end'
		};

	function isNode (elem) {
		if ( !elem ) {
			return false;
		}

		try {
			return (elem instanceof Node) || (elem instanceof HTMLElement);
		} catch (err) {}

		if (typeof elem !== 'object') {
			return false;
		}

		if (typeof elem.nodeType !== 'number') {
			return false;
		}

		if (typeof elem.nodeName !== 'string') {
			return false;
		}

		return true;
	}

	function isInDOM (elem, skipNodeCheck) {
		if (!skipNodeCheck && !isNode(elem)) {
			throw TypeError('element must be a DOM node, got ' + elem);
		}

		while (elem = elem.parentNode) {
			if (elem === document) {
				return true;
			}
		}

		return false;
	}

	function insertAfter (newElem, targetElem) {
		var parent = targetElem.parentNode;

		if (parent.lastchild === targetElem) {
			parent.appendChild(newElem);
		}
		else {
			parent.insertBefore(newElem, targetElem.nextSibling);
		}
	}

	function removeNode (elem) {
		if (elem.parentNode) {
			elem.parentNode.removeChild(elem);
		}
	}

	function setTransform (elem, transform) {
		elem.style['-webkit-transform'] = transform;
		elem.style[   '-moz-transform'] = transform;
		elem.style[    '-ms-transform'] = transform;
		elem.style[     '-o-transform'] = transform;
		elem.style[        'transform'] = transform;
	}

	function setTransition (elem, transition) {
		if (transition) {
			elem.style['-webkit-transition'] = '-webkit-'+transition;
			elem.style[   '-moz-transition'] =    '-moz-'+transition;
			elem.style[    '-ms-transition'] =     '-ms-'+transition;
			elem.style[     '-o-transition'] =      '-o-'+transition;
			elem.style[        'transition'] =            transition;
		}
		else {
			elem.style['-webkit-transition'] = '';
			elem.style[   '-moz-transition'] = '';
			elem.style[    '-ms-transition'] = '';
			elem.style[     '-o-transition'] = '';
			elem.style[        'transition'] = '';
		}
	}

	function getStyles (elem, notComputed) {
		var styles;

		if (notComputed) {
			styles = elem.style;
		}
		else {
			styles = document.defaultView.getComputedStyle(elem, null);
		}

		return {
			opacity  : styles.opacity  ,
			top      : styles.top      ,
			left     : styles.left     ,
			height   : styles.height   ,
			width    : styles.width    ,
			position : styles.position
		};
	}

	function validateElement (elem) {
		if ( !isNode(elem) ) {
			throw TypeError('element must be a DOM node, got ' + elem);
		}
	}

	function validateOptions (options) {
		switch (typeof options) {
			case 'string':
				options = { transition: options };
				break;

			case 'undefined':
				options = {};
				break;

			case 'object':
				break;

			default:
				throw TypeError('options must be an object if defined, got ' + options);
		}

		switch (typeof options.transition) {
			case 'string':
				if (!(options.transition in transitions) && (options.transition !== 'instant')) {
					throw TypeError(options.transition + ' is not a valid transition');
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('transition must be a string if defined, got ' + options.transition);
		}

		switch (typeof options.duration) {
			case 'number':
				if (options.duration < 0) {
					throw TypeError('duration must be a non-negative integer, got ' + options.duration);
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('duration must be a number if defined, got ' + options.duration);
		}

		switch (typeof options.easing) {
			case 'string':
				if ( !(options.easing in easings) ) {
					throw TypeError(options.easing + ' is not a valid easing');
				}
				break;

			case 'undefined':
				break;

			default:
				throw TypeError('easing must be a string if defined, got ' + options.easing);
		}

		return options;
	}

	function validateCallback (callback) {
		switch (typeof callback) {
			case 'undefined':
				callback = function () {};
				// fall through

			case 'function':
				break;

			default:
				throw TypeError('callback must be a function if defined, got ' + callback);
		}

		return callback;
	}

	function swapper (elem1, elem2, options, callback) {
		validateElement(elem1);
		validateElement(elem2);

		if (typeof options === 'function') {
			callback = options;
			options  = {};
		}

		options  = validateOptions(options);
		callback = validateCallback(callback);

		if (elem1._swapper) {
			throw Error('elem1 is currently being swapped');
		}

		if (elem2._swapper) {
			throw Error('elem2 is currently being swapped');
		}

		if ( !isInDOM(elem1, true) ) {
			throw Error('elem1 must be in the DOM to be swapped');
		}

		elem1._swapper = true;
		elem2._swapper = true;

		removeNode(elem2);

		if (options.transition === 'instant') {
			insertAfter(elem2, elem1);
			removeNode(elem1);
			elem1._swapper = false;
			elem2._swapper = false;
			setTimeout(function () {
				callback();
			}, 0);
			return;
		}

		var transition = transitions[options.transition || 'fade'],
			easing     = easings[options.easing || 'ease-in-out'],
			duration   = options.duration || 300;

		insertAfter(elem2, elem1);

		var bounds          = elem1.getBoundingClientRect(),
			computedStyles1 = getStyles(elem1),
			styles1         = getStyles(elem1, true),
			styles2         = getStyles(elem2, true);

		elem2.style.position = 'fixed';
		elem2.style.top      = bounds.top  + 'px';
		elem2.style.left     = bounds.left + 'px';
		elem2.style.height   = computedStyles1.height;
		elem2.style.width    = computedStyles1.width;

		setTransform(elem1, NO_TRANSFORM );
		setTransform(elem2, transition[0].transform || NO_TRANSFORM);

		if ( transition[0].fade ) {
			elem2.style.opacity = '0';
		}

		if ( transition[1].fade ) {
			elem1.style.opacity = '1';
		}

		setTimeout(function () {
			var cssTransition = 'transform '+(duration/1000)+'s '+easing+', opacity '+(duration/1000)+'s '+easing;
			setTransition(elem1, cssTransition);
			setTransition(elem2, cssTransition);

			setTimeout(function () {
				setTransform(elem1, transition[1].transform || NO_TRANSFORM);
				setTransform(elem2, NO_TRANSFORM );

				if ( transition[0].fade ) {
					elem2.style.opacity = '1';
				}

				if ( transition[1].fade ) {
					elem1.style.opacity = '0';
				}

				setTimeout(cleanupElems, duration)
			}, 0);
		}, 0);

		function cleanupElems () {
			removeNode(elem1);

			setTransition(elem1, '');
			setTransition(elem2, '');

			setTimeout(function () {
				setTransform(elem1, '');
				setTransform(elem2, '');

				if ( transition[0].fade ) {
					elem2.style.opacity = styles2.opacity;
				}

				if ( transition[1].fade ) {
					elem1.style.opacity = styles1.opacity;
				}

				elem2.style.position = styles2.position;
				elem2.style.top      = styles2.top;
				elem2.style.left     = styles2.left;
				elem2.style.height   = styles2.height;
				elem2.style.width    = styles2.width;

				elem1._swapper = false;
				elem2._swapper = false;

				callback();
			}, 0);
		}
	}

	if (Zepto) {
		Zepto.extend(Zepto.fn, {
			swapper : function (elem2, options, callback) {
				elem2 = Zepto(elem2)[0];

				this.forEach(function (elem1) {
					swapper(elem1, elem2, options, callback);
				});

				return this;
			}
		});
	}

	if (jQuery) {
		jQuery.fn.swapper = function (elem2, options, callback) {
			elem2 = jQuery(elem2)[0];

			this.each(function () {
				swapper(this, elem2, options, callback);
			});

			return this;
		};
	}

	return swapper;
}(window, document, window.Zepto, window.jQuery);
<|endoftext|>


<commit_before>export default (url, options = {}) => new Promise((resolve, reject) => {
  let {body} = options
  const request = new window.XMLHttpRequest()
  const headers = options.headers || {}

  // Stringify the body if necessary.
  if (typeof body === 'object' && !(body instanceof window.FormData)) {
    body = JSON.stringify(body)
    headers['content-type'] = 'application/json'
  }

  // Set accept header.
  headers.accept = 'application/json'

  // Open the request.
  request.open(options.method || 'get', url)

  // Include credentials (must be after open for IE11 and below).
  request.withCredentials = true

  // Set headers.
  for (const key in headers) request.setRequestHeader(key, headers[key])

  // Send it!
  request.send(body)

  // Complete
  request.addEventListener('load', () => {
    const {status, responseText} = request
    let response = responseText

    // Parse the response.
    if (/json/.test(request.getResponseHeader('content-type'))) {
      response = JSON.parse(responseText)
    }

    if (status >= 200 && status < 400) {
      resolve(response)
    } else {
      const error = new Error('XHR Error')
      error.url = url
      error.status = status
      error.response = response
      reject(error)
    }
  })

  // Error
  request.addEventListener('error', (event) => {
    // Aborted requests are neither resolved nor rejected.
    if (request.status === 0) return
    const error = new Error('XHR Error')
    error.url = url
    error.status = request.status
    reject(error)
  })

  // Timeout
  request.addEventListener('timeout', (event) => {
    const error = new Error('XHR Timeout')
    error.url = url
    reject(error)
  })
})
.catch((error) => {
  if (error.status !== 422) {
    window.Bugsnag.notifyException(error, 'XHR Error', {
      response: error.response,
      status: error.status,
      url: error.url
    })
  }
  throw error
})
<commit_msg>Update the xhr api.<commit_after>export default (method = 'get', url, body, headers = {}) => new Promise((resolve, reject) => {
  const request = new window.XMLHttpRequest()

  // Open the request.
  request.open(method, url)

  // Include credentials (must be after open for IE11 and below).
  request.withCredentials = true

  // Set headers.
  for (const key in headers) request.setRequestHeader(key, headers[key])

  // Send it!
  request.send(body)

  // Complete
  request.addEventListener('load', () => {
    const {status, responseText} = request
    let response = responseText

    // Parse the response.
    if (/json/.test(request.getResponseHeader('content-type'))) {
      response = JSON.parse(responseText)
    }

    if (status >= 200 && status < 400) {
      resolve(response)
    } else {
      const error = new Error('XHR Error')
      error.url = url
      error.status = status
      error.response = response
      reject(error)
    }
  })

  // Error
  request.addEventListener('error', (event) => {
    // Aborted requests are neither resolved nor rejected.
    if (request.status === 0) return
    const error = new Error('XHR Error')
    error.url = url
    error.status = request.status
    reject(error)
  })

  // Timeout
  request.addEventListener('timeout', (event) => {
    const error = new Error('XHR Timeout')
    error.url = url
    reject(error)
  })
})
.catch((error) => {
  if (error.status !== 422) {
    window.Bugsnag.notifyException(error, 'XHR Error', {
      response: error.response,
      status: error.status,
      url: error.url
    })
  }
  throw error
})
<|endoftext|>