<commit_before>var logger = (require('../logging/vip-winston')).Logger;
var authService = require("../authentication/services");
var config = require('../config');
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var dataTestingMessageContent = require('./data-testing/content');
var feedProcessingMessageContent = require('./feed-processing/content');
var pg = require('pg');
var slack = require('./slack');

var transporter = nodemailer.createTransport(sesTransport({
  accessKeyId: config.aws.accessKey,
  secretAccessKey: config.aws.secretKey,
  rateLimit: config.email.rateLimit,
  region: config.aws.region
}));

var dataCentralizationOnly = function(user) {
  return user.app_metadata.roles &&
         user.app_metadata.roles.length == 1 &&
         user.app_metadata.roles[0] == 'data-centralization';
};

var messageOptions = {
  // data-testing message options
  testingComplete: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has completed",
      html: dataTestingMessageContent.testingComplete(message)
    }
  },
  errorDuringTesting: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has failed",
      html: dataTestingMessageContent.errorDuringTesting(message)
    }
  },

  // feed-processing message options
  approveFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: "A Feed has been Approved",
        html: feedProcessingMessageContent.approveFeed(message, recipient, fipsCode)
      }
    }
  },
  processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.processedFeed(message, recipient, fipsCode)
      };
    }
  },
  v5processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)){
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.v5processedFeed(message, recipient, fipsCode)
      };
    }
  },
  errorDuringProcessing: function(message, recipient) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Something Went Wrong with a Feed',
        text: feedProcessingMessageContent.errorDuringProcessing(message)
      };
    }
  }
};

var sendMessage = function(messageContent) {
  transporter.sendMail(messageContent, function(error, info) {
    if (error) {
      logger.info('Sending error: ' + error);
      logger.info('Message: ' + JSON.stringify(messageContent));
    }
  });
};

var badFips = function(fips) {
  if ((typeof fips != "string") ||
       (fips.length < 2) ||
       (fips.length > 5)) {
    return true;
  } else {
    return false;
  }
}

var sendEmail = function(message, fips, contentFn) {
  authService.getUsersByFips(fips, function (users) {
    logger.info(`notify flag: ${message.notify}`)
    logger.info(`typeof notify flag: ${typeof message.notify}`)
    if (message.notify !== undefined && !message.notify) {
      return
    }
    for (var i = 0; i < users.length; i++) {
      var recipient = users[i];
      var messageContent = contentFn(message, recipient, fips);

      if (messageContent != null) {
        sendMessage(messageContent);
        logger.info("Sending a message to: " + messageContent.to + " with this subject: " + messageContent.subject);
      }
    };
  });
};

var loadFeed = function(publicId, callback) {
  var vip_id_query = "SELECT vip_id, spec_version, election_date, extract(epoch from (end_time - start_time)) as duration \
                      FROM results \
                      WHERE public_id = $1";

  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if (err) return logger.error('Could not connect to PostgreSQL. Error fetching client from pool: ', err);

    client.query(vip_id_query, [publicId], function(err, result) {
      done();

      if (err || result.rows.length == 0) {
        logger.error('No feed found or connection issue.');
        slack.message("ERROR: No feed found matching public_id in database (public_id follows):\n" + publicId);
      } else {
        callback(result);
      }
    });
  });
}

module.exports = {
  sendUploadNotifications: function(message) {
    logger.info("sending Slack upload message: " + message);
    if (message.uploaded == "true") {
      slack.message((message.isFeed ? "FEED " : "FILE ") + "UPLOAD SUCCESS: " + message.filename + " uploaded to " + message.bucket);
    } else {
      slack.message((message.isFeed ? "FEED " : "FILE ") + "UPLOAD FAILURE: " + message.filename + " failed to upload to " + message.bucket);
    }
  },
  sendFeedProcessingSuccessNotifications: function(message, messageType) {
    loadFeed(message.publicId, (result) => {
      var fips = result.rows[0]['vip_id'];
      var spec_version = new String(result.rows[0]['spec_version']);
      if (badFips(fips)) {
        slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
      } else {
        var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
        var electionDate = result.rows[0]['election_date'] || "Not available";
        var duration = result.rows[0]['duration'] || "Not available";

        var reportSuffix = "/xml/errors/report";
        if (spec_version == "3.0") {
          reportSuffix = "/errors/report";
        }

        var checksum = "";
        if (message.checksum) {
          checksum = "\nChecksum: " + message.checksum
        }

        slack.message("SUCCESS: Feed processed for FIPS " + fips +
                      "\nState Name " + stateName +
                      "\nElection Date " + electionDate +
                      "\nVIP Spec Version " + spec_version +
                      checksum +
                      "\nFull Error Report https://" +
                        process.env.BASE_URI + "/db/feeds/" + message.publicId + reportSuffix +
                      "\nProcessing-Time (sec) " + duration);
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }}});
  },
  sendFeedProcessingFailureNotifications: function(message, messageType) {
    var publicId = message.publicId;

    if (message.stop) {
      logger.info('Feed processing stopped.');
      slack.message("STOPPED: Processing of " + message.filename + " stopped.");
    }

    if (!publicId) {
      logger.error('No Public ID listed.');
      slack.message("ERROR: Feed processed but no public id found (message follows):\n" +
                    (message.stop || JSON.stringify(message)));
    } else {
      loadFeed(publicId, (result) => {
        var fips = result.rows[0]['vip_id'];
        var spec_version = new String(result.rows[0]['spec_version']);
        if (badFips(fips)) {
          slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
        } else {
          var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
          var electionDate = result.rows[0]['election_date'] || "Not available";
          slack.message("EXCEPTION: Feed processed with errors for FIPS " + fips +
                        "\nState Name " + stateName +
                        "\nElection Date " + electionDate +
                        "\nVIP Spec Version " + spec_version +
                        "\nProcessed Message " + JSON.stringify(message));
        }
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }
      });
    }
  },
  sendAddressTestSuccessNotifications: function(message) {
    var fipsCode = message.fipsCode;
    var slackMessage = "";
    if(fipsCode == "undefined") {
      slackMessage += "SUCCESS: Batch Address file processed for an admin, available at: " + message["url"];
    } else {
      slackMessage += "SUCCESS: Batch Address file processed for fips " + fipsCode + ", available at: " + message["url"];
    }
    if(message.stats) {
      slackMessage += "\nMatch Breakdown:";
      var statuses = ["Match", "Possible Mismatch", "Mismatch", "No Result"];
      statuses.map( (key) => { if (message.stats.hasOwnProperty(key)) {
                                 slackMessage += "\n" + key + ": " + message.stats[key];
                               } else {
                                 slackMessage += "\n" + key + ": 0";
                               } });
    }
    slack.message(slackMessage);
    sendEmail(message, fipsCode, messageOptions['testingComplete']);
  },
  sendAddressTestFailureNotifications: function(message) {
    var fipsCode = message.fipsCode;
    if (fipsCode === undefined) {
      logger.warning("No fips code in batch-address.file.complete message.  Can't send batch address testing finished email notification.");
      slack.message("ERROR: Batch Address sent a message we don't understand: " +
                    JSON.stringify(message));
    } else if (fipsCode === "undefined") {
      slack.message("ERROR: Admin submitted Batch Address file FAILED (message follows):\n" +
                    JSON.stringify(message));
    } else if (badFips(fipsCode)) {
      slack.message("ERROR: Batch Address file processed but fips was bad, no emails sent (message follows)\n" +
                    JSON.stringify(message));
    } else {
      slack.message("ERROR: Batch Address file failed (message follows)\n" +
                    JSON.stringify(message));
      sendEmail(message, fipsCode, messageOptions['errorDuringTesting']);
    }
  }
};
<commit_msg>VIP-744: Log public Id when skipping email (#563)<commit_after>var logger = (require('../logging/vip-winston')).Logger;
var authService = require("../authentication/services");
var config = require('../config');
var nodemailer = require('nodemailer');
var sesTransport = require('nodemailer-ses-transport');
var dataTestingMessageContent = require('./data-testing/content');
var feedProcessingMessageContent = require('./feed-processing/content');
var pg = require('pg');
var slack = require('./slack');

var transporter = nodemailer.createTransport(sesTransport({
  accessKeyId: config.aws.accessKey,
  secretAccessKey: config.aws.secretKey,
  rateLimit: config.email.rateLimit,
  region: config.aws.region
}));

var dataCentralizationOnly = function(user) {
  return user.app_metadata.roles &&
         user.app_metadata.roles.length == 1 &&
         user.app_metadata.roles[0] == 'data-centralization';
};

var messageOptions = {
  // data-testing message options
  testingComplete: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has completed",
      html: dataTestingMessageContent.testingComplete(message)
    }
  },
  errorDuringTesting: function(message, recipient) {
    return {
      from: config.email.fromAddress,
      to: recipient.email,
      subject: "A batch address test has failed",
      html: dataTestingMessageContent.errorDuringTesting(message)
    }
  },

  // feed-processing message options
  approveFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: "A Feed has been Approved",
        html: feedProcessingMessageContent.approveFeed(message, recipient, fipsCode)
      }
    }
  },
  processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.processedFeed(message, recipient, fipsCode)
      };
    }
  },
  v5processedFeed: function(message, recipient, fipsCode) {
    if (dataCentralizationOnly(recipient)){
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Your Feed Has Been Processed',
        html: feedProcessingMessageContent.v5processedFeed(message, recipient, fipsCode)
      };
    }
  },
  errorDuringProcessing: function(message, recipient) {
    if (dataCentralizationOnly(recipient)) {
      return null;
    } else {
      return {
        from: config.email.fromAddress,
        to: recipient.email,
        subject: 'Something Went Wrong with a Feed',
        text: feedProcessingMessageContent.errorDuringProcessing(message)
      };
    }
  }
};

var sendMessage = function(messageContent) {
  transporter.sendMail(messageContent, function(error, info) {
    if (error) {
      logger.info('Sending error: ' + error);
      logger.info('Message: ' + JSON.stringify(messageContent));
    }
  });
};

var badFips = function(fips) {
  if ((typeof fips != "string") ||
       (fips.length < 2) ||
       (fips.length > 5)) {
    return true;
  } else {
    return false;
  }
}

var sendEmail = function(message, fips, contentFn) {
  authService.getUsersByFips(fips, function (users) {
    if (message.notify === false) {
      logger.info(`Notify flag disabled, not sending email for message with publicId ${message.publicId}`);
      return
    }
    for (var i = 0; i < users.length; i++) {
      var recipient = users[i];
      var messageContent = contentFn(message, recipient, fips);

      if (messageContent != null) {
        sendMessage(messageContent);
        logger.info("Sending a message to: " + messageContent.to + " with this subject: " + messageContent.subject);
      }
    };
  });
};

var loadFeed = function(publicId, callback) {
  var vip_id_query = "SELECT vip_id, spec_version, election_date, extract(epoch from (end_time - start_time)) as duration \
                      FROM results \
                      WHERE public_id = $1";

  pg.connect(process.env.DATABASE_URL, function(err, client, done) {
    if (err) return logger.error('Could not connect to PostgreSQL. Error fetching client from pool: ', err);

    client.query(vip_id_query, [publicId], function(err, result) {
      done();

      if (err || result.rows.length == 0) {
        logger.error('No feed found or connection issue.');
        slack.message("ERROR: No feed found matching public_id in database (public_id follows):\n" + publicId);
      } else {
        callback(result);
      }
    });
  });
}

module.exports = {
  sendUploadNotifications: function(message) {
    logger.info("sending Slack upload message: " + message);
    if (message.uploaded == "true") {
      slack.message((message.isFeed ? "FEED " : "FILE ") + "UPLOAD SUCCESS: " + message.filename + " uploaded to " + message.bucket);
    } else {
      slack.message((message.isFeed ? "FEED " : "FILE ") + "UPLOAD FAILURE: " + message.filename + " failed to upload to " + message.bucket);
    }
  },
  sendFeedProcessingSuccessNotifications: function(message, messageType) {
    loadFeed(message.publicId, (result) => {
      var fips = result.rows[0]['vip_id'];
      var spec_version = new String(result.rows[0]['spec_version']);
      if (badFips(fips)) {
        slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
      } else {
        var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
        var electionDate = result.rows[0]['election_date'] || "Not available";
        var duration = result.rows[0]['duration'] || "Not available";

        var reportSuffix = "/xml/errors/report";
        if (spec_version == "3.0") {
          reportSuffix = "/errors/report";
        }

        var checksum = "";
        if (message.checksum) {
          checksum = "\nChecksum: " + message.checksum
        }

        slack.message("SUCCESS: Feed processed for FIPS " + fips +
                      "\nState Name " + stateName +
                      "\nElection Date " + electionDate +
                      "\nVIP Spec Version " + spec_version +
                      checksum +
                      "\nFull Error Report https://" +
                        process.env.BASE_URI + "/db/feeds/" + message.publicId + reportSuffix +
                      "\nProcessing-Time (sec) " + duration);
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }}});
  },
  sendFeedProcessingFailureNotifications: function(message, messageType) {
    var publicId = message.publicId;

    if (message.stop) {
      logger.info('Feed processing stopped.');
      slack.message("STOPPED: Processing of " + message.filename + " stopped.");
    }

    if (!publicId) {
      logger.error('No Public ID listed.');
      slack.message("ERROR: Feed processed but no public id found (message follows):\n" +
                    (message.stop || JSON.stringify(message)));
    } else {
      loadFeed(publicId, (result) => {
        var fips = result.rows[0]['vip_id'];
        var spec_version = new String(result.rows[0]['spec_version']);
        if (badFips(fips)) {
          slack.message("ERROR: Feed processed but FIPS was bad, no notifications sent: " + fips);
        } else {
          var stateName = feedProcessingMessageContent.codeToDescription(fips.slice(0,2));
          var electionDate = result.rows[0]['election_date'] || "Not available";
          slack.message("EXCEPTION: Feed processed with errors for FIPS " + fips +
                        "\nState Name " + stateName +
                        "\nElection Date " + electionDate +
                        "\nVIP Spec Version " + spec_version +
                        "\nProcessed Message " + JSON.stringify(message));
        }
        if (fips && spec_version[0] == '5'  && messageType === 'processedFeed') {
          sendEmail(message, fips, messageOptions['v5processedFeed']);
        } else {
          sendEmail(message, fips, messageOptions[messageType]);
        }
      });
    }
  },
  sendAddressTestSuccessNotifications: function(message) {
    var fipsCode = message.fipsCode;
    var slackMessage = "";
    if(fipsCode == "undefined") {
      slackMessage += "SUCCESS: Batch Address file processed for an admin, available at: " + message["url"];
    } else {
      slackMessage += "SUCCESS: Batch Address file processed for fips " + fipsCode + ", available at: " + message["url"];
    }
    if(message.stats) {
      slackMessage += "\nMatch Breakdown:";
      var statuses = ["Match", "Possible Mismatch", "Mismatch", "No Result"];
      statuses.map( (key) => { if (message.stats.hasOwnProperty(key)) {
                                 slackMessage += "\n" + key + ": " + message.stats[key];
                               } else {
                                 slackMessage += "\n" + key + ": 0";
                               } });
    }
    slack.message(slackMessage);
    sendEmail(message, fipsCode, messageOptions['testingComplete']);
  },
  sendAddressTestFailureNotifications: function(message) {
    var fipsCode = message.fipsCode;
    if (fipsCode === undefined) {
      logger.warning("No fips code in batch-address.file.complete message.  Can't send batch address testing finished email notification.");
      slack.message("ERROR: Batch Address sent a message we don't understand: " +
                    JSON.stringify(message));
    } else if (fipsCode === "undefined") {
      slack.message("ERROR: Admin submitted Batch Address file FAILED (message follows):\n" +
                    JSON.stringify(message));
    } else if (badFips(fipsCode)) {
      slack.message("ERROR: Batch Address file processed but fips was bad, no emails sent (message follows)\n" +
                    JSON.stringify(message));
    } else {
      slack.message("ERROR: Batch Address file failed (message follows)\n" +
                    JSON.stringify(message));
      sendEmail(message, fipsCode, messageOptions['errorDuringTesting']);
    }
  }
};
<|endoftext|>


<commit_before>function columnsFromRows(rows) {
  const columns = [];

  rows.forEach((row) => {
    row.forEach((n, index) => {
      columns[index] = columns[index] || [];
      columns[index].push(n);
    });
  });

  return columns;
}

function parseRows(description) {
  return description.split('\n').map(row => row.split(' ').map(char => parseInt(char, 10)));
}

export class Matrix {
  constructor(description) {
    this.rows = parseRows(description);
    this.columns = columnsFromRows(this.rows);
  }
}
<commit_msg>Update example.js (#713)<commit_after>function columnsFromRows(rows) {
  const columns = [];

  rows.forEach((row) => {
    row.forEach((n, index) => {
      columns[index] = columns[index] || [];
      columns[index].push(n);
    });
  });

  return columns;
}

function parseRows(description) {
  return description.split('\n').map(row => row.split(' ').map(char => Number(char)));
}

export class Matrix {
  constructor(description) {
    this.rows = parseRows(description);
    this.columns = columnsFromRows(this.rows);
  }
}
<|endoftext|>


<commit_before>module.exports = function (str) {
  var palindromes = [];

  var walkPalindrome = function (result, str, leftIndex, rightIndex) {
    while (str[leftIndex - 1] === str[rightIndex + 1]) {
      result = str[leftIndex -= 1] + result + str[rightIndex += 1];
    }
    palindromes.push(result);
  };

  for (var i = 0; i < str.length; i++) {
    if (str[i] === str[i - 1]) {
      walkPalindrome(str[i] + str[i - 1], str, i - 1, i);
    } else if (str[i] === str[i + 1]) {
      walkPalindrome(str[i] + str[i + 1], str, i, i + 1);
    } else if (str[i - 1] === str[i + 1]) {
      walkPalindrome(str[i - 1] + str[i] + str[i + 1], str, i - 1, i + 1);
    }
  }

  return palindromes.reduce(function (memo, str) {
    return str.length > memo.length ? str : memo;
  }, '');
};
<commit_msg>Improve the palindrome solution (way better now!)<commit_after>function isPalindrome (str) {
  const mid = Math.floor(str.length / 2)

  for (let i = 0; i < mid; i++) {
    if (str[i] !== str[str.length - 1 - i]) {
      return false
    }
  }

  return true
}

module.exports = function longestPalindrome (str) {
  let longest = ''

  for (let i = 0; i < str.length; i++) {
    let len = str.length

    while (len > i && len - i > longest.length) {
      const substring = str.substring(i, len--)

      if (isPalindrome(substring)) {
        longest = substring
      }
    }
  }

  return longest
}
<|endoftext|>


<commit_before>// -------------------------------------------------------------------------- \\
// File: StyleAnimation.js                                                    \\
// Module: Animation                                                          \\
// Requires: Animation.js                                                     \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var matrix = /matrix\((.*?)\)/;
var styleAnimators = {
    display: {
        calcDelta: function ( startValue, endValue ) {
            return endValue === 'none' ? startValue : endValue;
        },
        calcValue: function ( position, deltaValue, startValue, endValue ) {
            return position ? position < 1 ?
                deltaValue : endValue : startValue;
        }
    },
    transform: {
        calcDelta: function ( startValue, endValue ) {
            if ( !startValue ) { startValue = 'matrix(1,0,0,1,0,0)'; }
            if ( !endValue ) { endValue = 'matrix(1,0,0,1,0,0)'; }
            var start = matrix.exec( startValue )[1].split( ',' ).map( Number ),
                end = matrix.exec( endValue )[1].split( ',' ).map( Number );
            return {
                start: start,
                delta: end.map( function ( value, index ) {
                    return value - start[ index ];
                })
            };
        },
        calcValue: function ( position, deltaValue ) {
            var start = deltaValue.start, delta = deltaValue.delta;
            return 'matrix(' + start.map( function ( value, index ) {
                return value + ( position * delta[ index ] );
            }).join( ',' ) + ')';
        }
    }
};

var supported = {
    display: 1,

    top: 1,
    right: 1,
    bottom: 1,
    left: 1,

    width: 1,
    height: 1,

    transform: 1,

    opacity: 1
};

/**
    Class: O.StyleAnimation

    Extends: O.Animation

    Animates the CSS styles of an element without using CSS transitions. This is
    used in browsers that don't support CSS transitions, but could also be
    useful if you want to animate an element using an easing method not
    supported by CSS transitions.

    Note, only the following CSS properties are currently supported by this
    class (all others will be set immediately without transition):

    * top
    * right
    * bottom
    * left
    * width
    * height
    * transform (values must be in matrix form)
    * opacity
*/
var StyleAnimation = NS.Class({

    Extends: NS.Animation,

    /**
        Method (protected): O.StyleAnimation#prepare

        Goes through the new styles for the element, works out which of these
        can be animated, and caches the delta value (difference between end and
        start value) for each one to save duplicated calculation when drawing a
        frame.

        Parameters:
            styles - {Object} A map of style name to desired value.

        Returns:
            {Boolean} True if any of the styles are going to be animated.
    */
    prepare: function ( styles ) {
        var animated = this.animated = [],
            from = this.startValue = this.current,
            current = this.current = NS.clone( from ),
            delta = this.deltaValue = {},
            units = this.units = {},

            property, start, end, animator;

        this.endValue = styles;

        for ( property in styles ) {
            start = from[ property ] || 0;
            end = styles[ property ] || 0;
            if ( start !== end ) {
                // We only support animating key layout properties.
                if ( supported[ property ] ) {
                    animated.push( property );
                    animator = styleAnimators[ property ];
                    if ( animator ) {
                        delta[ property ] = animator.calcDelta( start, end );
                    } else {
                        units[ property ] =
                            ( typeof start === 'string' &&
                                start.replace( /[\.\-\d]/g, '' ) ) ||
                            ( typeof end === 'string' &&
                                end.replace( /[\.\-\d]/g, '' ) ) ||
                            'px';
                        start = from[ property ] = parseInt( start, 10 );
                        delta[ property ] = parseInt( end, 10 ) - start;
                    }
                } else {
                    current[ property ] = end;
                    NS.Element.setStyle( this.element, property, end );
                }
            }
        }
        return !!animated.length;
    },

    /**
        Method (protected): O.StyleAnimation#drawFrame

        Updates the animating styles on the element to the interpolated values
        at the position given.

        Parameters:
            position - {Number} The position in the animation.
    */
    drawFrame: function ( position ) {
        var animated = this.animated,
            l = animated.length,

            from = this.startValue,
            to = this.endValue,
            difference = this.deltaValue,
            units = this.units,
            current = this.current,

            el = this.element,
            setStyle = NS.Element.setStyle,
            property, value, start, end, delta, unit, animator;

        while ( l-- ) {
            property = animated[l];

            // Calculate new value.
            start = from[ property ] || 0;
            end = to[ property ] || 0;
            delta = difference[ property ];
            unit = units[ property ];

            animator = styleAnimators[ property ];

            value = current[ property ] = animator ?
                animator.calcValue( position, delta, start, end ) :
                position < 1 ? ( start + ( position * delta ) ) + unit : end;

            // And set.
            setStyle( el, property, value );
        }
    }
});

NS.StyleAnimation = StyleAnimation;

}( this.O ) );
<commit_msg>Support animation of non-matrix transforms in old browsers.<commit_after>// -------------------------------------------------------------------------- \\
// File: StyleAnimation.js                                                    \\
// Module: Animation                                                          \\
// Requires: Animation.js                                                     \\
// Author: Neil Jenkins                                                       \\
// License: © 2010–2013 Opera Software ASA. All rights reserved.              \\
// -------------------------------------------------------------------------- \\

"use strict";

( function ( NS ) {

var transformSplitter = /(\-?\d*\.\d+|\-?\d+)/;
var numbersToNumber = function ( item, index ) {
    return index & 1 ? parseFloat( item ) : item;
};
var styleAnimators = {
    display: {
        calcDelta: function ( startValue, endValue ) {
            return endValue === 'none' ? startValue : endValue;
        },
        calcValue: function ( position, deltaValue, startValue ) {
            return position ? deltaValue : startValue;
        }
    },
    transform: {
        calcDelta: function ( startValue, endValue ) {
            var start = startValue
                    .split( transformSplitter )
                    .map( numbersToNumber ),
                end = endValue
                    .split( transformSplitter )
                    .map( numbersToNumber );
            if ( start.length !== end.length ) {
                start = [ startValue ];
                end = [ endValue ];
            }
            return {
                start: start,
                delta: end.map( function ( value, index ) {
                    return index & 1 ? value - start[ index ] : 0;
                })
            };
        },
        calcValue: function ( position, deltaValue ) {
            var start = deltaValue.start,
                delta = deltaValue.delta,
                transform = start[0],
                i, l;
            for ( i = 1, l = start.length; i < l; i += 2 ) {
                transform += start[ i ] + ( position * delta[ i ] );
                transform += start[ i + 1 ];
            }
            return transform;
        }
    }
};

var supported = {
    display: 1,

    top: 1,
    right: 1,
    bottom: 1,
    left: 1,

    width: 1,
    height: 1,

    transform: 1,

    opacity: 1
};

/**
    Class: O.StyleAnimation

    Extends: O.Animation

    Animates the CSS styles of an element without using CSS transitions. This is
    used in browsers that don't support CSS transitions, but could also be
    useful if you want to animate an element using an easing method not
    supported by CSS transitions.

    Note, only the following CSS properties are currently supported by this
    class (all others will be set immediately without transition):

    * top
    * right
    * bottom
    * left
    * width
    * height
    * transform (values must be in matrix form)
    * opacity
*/
var StyleAnimation = NS.Class({

    Extends: NS.Animation,

    /**
        Method (protected): O.StyleAnimation#prepare

        Goes through the new styles for the element, works out which of these
        can be animated, and caches the delta value (difference between end and
        start value) for each one to save duplicated calculation when drawing a
        frame.

        Parameters:
            styles - {Object} A map of style name to desired value.

        Returns:
            {Boolean} True if any of the styles are going to be animated.
    */
    prepare: function ( styles ) {
        var animated = this.animated = [],
            from = this.startValue = this.current,
            current = this.current = NS.clone( from ),
            delta = this.deltaValue = {},
            units = this.units = {},

            property, start, end, animator;

        this.endValue = styles;

        for ( property in styles ) {
            start = from[ property ] || 0;
            end = styles[ property ] || 0;
            if ( start !== end ) {
                // We only support animating key layout properties.
                if ( supported[ property ] ) {
                    animated.push( property );
                    animator = styleAnimators[ property ];
                    if ( animator ) {
                        delta[ property ] = animator.calcDelta( start, end );
                    } else {
                        units[ property ] =
                            ( typeof start === 'string' &&
                                start.replace( /[\.\-\d]/g, '' ) ) ||
                            ( typeof end === 'string' &&
                                end.replace( /[\.\-\d]/g, '' ) ) ||
                            'px';
                        start = from[ property ] = parseInt( start, 10 );
                        delta[ property ] = parseInt( end, 10 ) - start;
                    }
                } else {
                    current[ property ] = end;
                    NS.Element.setStyle( this.element, property, end );
                }
            }
        }
        return !!animated.length;
    },

    /**
        Method (protected): O.StyleAnimation#drawFrame

        Updates the animating styles on the element to the interpolated values
        at the position given.

        Parameters:
            position - {Number} The position in the animation.
    */
    drawFrame: function ( position ) {
        var animated = this.animated,
            l = animated.length,

            from = this.startValue,
            to = this.endValue,
            difference = this.deltaValue,
            units = this.units,
            current = this.current,

            el = this.element,
            setStyle = NS.Element.setStyle,
            property, value, start, end, delta, unit, animator;

        while ( l-- ) {
            property = animated[l];

            // Calculate new value.
            start = from[ property ] || 0;
            end = to[ property ] || 0;
            delta = difference[ property ];
            unit = units[ property ];

            animator = styleAnimators[ property ];

            value = current[ property ] = position < 1 ?
                animator ?
                    animator.calcValue( position, delta, start, end ) :
                    ( start + ( position * delta ) ) + unit :
                end;

            // And set.
            setStyle( el, property, value );
        }
    }
});

NS.StyleAnimation = StyleAnimation;

}( this.O ) );
<|endoftext|>


<commit_before>//socket io
const URL = 'http://localhost:6277'
const socket = io.connect(url)
//user input all infomation
let inclipinfo = []
let intileinfo = []
//user input item
let incliptags
let intiletags = []
// svg con stack(not loaded)
let svg_con_stack_clip = {}
let svg_con_stack_tile = {}
//recieve tags stack
let stacktile = []
//clip form flag
let cflag = true

//send to get all tags
socket.emit('get_allcliptags')

//recieve all clip tags
socket.on('res_allcliptags', (rec) => {
  //set complete clip
  let cliptags = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('clip'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    local: $.map(rec, (tag) => {
      return {
        clip: tag
      }
    })
  })
  //reflection of clip setting
  cliptags.initialize()
  //input auto complete clip
  $('.clip-tags-form').tagsinput({
    typeaheadjs: [{
      minLength: 1,
      highlight: true,
    },{
      minlength: 1,
      name: 'cliptags',
      displayKey: 'clip',
      valueKey: 'clip',
      source: cliptags.ttAdapter()
    }],
    freeInput: true
  })
})

//search button event
$('#search-button').click(() => {
  $('.clip-form').css('overflow-y', 'scroll')
  cflag = true
  $('.aleart-text').empty()
  incliptags = $('.clip-tags-form').tagsinput('items')
  //select dropdown item to array
  for(i = 0; i < $('.dropdown-menu li').length; i++){
    if($('#'+i).prop('checked')){
      intiletags.push($('#'+i).val())
    }
  }
  if(incliptags.length == 0){
    $('.clip-form').css('overflow', 'hidden')
    incliptags = undefined
    stacktile = []
    $('.dropdown-menu').empty()
    $('.aleart-text').append('
CLIP NOT FOUND
')
  }
  if(intiletags.length == 0){
    intiletags = undefined
  }else{
    cflag = false
  }
  inclipinfo.push({
    cliptags: incliptags,
    startdate: Date.parse($('.start').val()),
    enddate: Date.parse($('.end').val())
  })
  intileinfo.push({
    tiletags: intiletags,
    cliptags: incliptags,
    startdate: Date.parse($('.start').val()),
    enddate: Date.parse($('.end').val())
  })
  $('.clip-form').empty()
  $('.tile-form').empty()
  //send input data
  socket.emit('send_clipsearchdata', inclipinfo[0])
  if(intiletags !== undefined){
    socket.emit('send_tilesearchdata', intileinfo[0])
  }
  inclipinfo = []
  intileinfo = []
  intiletags = []
})

//tile tags dropdown
socket.on('res_alltiletags', (rec) => {
  if(rec.join(',') != stacktile.join(',')){
    $('.dropdown-menu').empty()
    for(i = 0; i < rec.length; i++){
      let $li = $('

')
      let $input = $('<input></input>', {
        id: i,
        type: "checkbox",
        value: rec[i]
      })
      let $label = $('', {
        for: i,
        text: rec[i]
      })
      $('.dropdown-menu').append($li.append($input))
      $('.dropdown-menu').append($li.append($label))
    }
  }
  stacktile = rec
})

//clip form
socket.on('res_clips', (rec) => {
  if(cflag){
    for(i = 0; i < rec.length; i++){
      let $div = $('

', {
        class: 'clip-field ' + i + ' clearfix'
      })
      $('.clip-form').append($div)
      for(j = 0; j < rec[i].tile.length; j++){
        switch(rec[i].tile[j].sty){
          case "txt":
            let $div = $('

', {
              class: 'col-sm-' + rec[i].tile[j].col + ' tile',
              text: rec[i].tile[j].con
            })
            $('.'+i).append($div)

            MathJax.Hub.Queue(["Typeset", MathJax.Hub, "clip-field"])
            break
          case "svg":
            let $span = $('', {
              class: 'col-sm-' + rec[i].tile[j].col + ' iframe-wrap'
            })
            let $iframe = $('', {
              id: rec[i].tile[j]._id,
              src: URL + "/html/read_nu/",
              scrolling: "auto"
            })
            $('.'+i).append(
              $span.append($iframe)
            )

            // stack to var
            svg_con_stack_clip[rec[i].tile[j]._id] = rec[i].tile[j].con

            // onload
            // caution: can not use arrow func
            $iframe.on('load', function(){
              // write path
              $(this)[0].contentWindow.loadPath(svg_con_stack_clip[$(this).attr("id")])
              // delete from stack
              delete svg_con_stack_clip[$(this).attr("id")]
            })
            break
          default:
            console.error('sty error')
            break
        }
      }
    }
  }
})

//tile form
socket.on('res_tiles', (rec) => {
  let $div = $('

', {
    class: 'clip-field clearfix with_tile'
  })
  $('.clip-form').append($div)
  for(i = 0; i < rec.length; i++){
    switch(rec[i].sty){
      case "txt":
        let $div = $('

', {
          class: 'col-sm-' + rec[i].col + ' tile',
          text: rec[i].con
        })
        $('.with_tile').append($div)

        MathJax.Hub.Queue(["Typeset", MathJax.Hub, "clip-field"])
        break
      case "svg":
        let $span = $('', {
          class: 'col-sm-' + rec[i].col + ' iframe-wrap'
        })
        let $iframe = $('', {
          id: rec[i]._id,
          src: URL + "/html/read_nu/",
          scrolling: "auto"
        })
        $('.with_tile').append(
          $span.append($iframe)
        )

        // stack to var
        svg_con_stack_tile[rec[i]._id] = rec[i].con

        // onload
        // caution: can not use arrow func
        $iframe.on('load', function(){
          // write path
          $(this)[0].contentWindow.loadPath(svg_con_stack_tile[$(this).attr("id")])
          // delete from stack
          delete svg_con_stack_tile[$(this).attr("id")]
        })
        break
      default:
        console.error('sty error')
        break
    }
  }
})

<commit_msg>fix url => URL<commit_after>//socket io
const URL = 'http://localhost:6277'
const socket = io.connect(URL)
//user input all infomation
let inclipinfo = []
let intileinfo = []
//user input item
let incliptags
let intiletags = []
// svg con stack(not loaded)
let svg_con_stack_clip = {}
let svg_con_stack_tile = {}
//recieve tags stack
let stacktile = []
//clip form flag
let cflag = true

//send to get all tags
socket.emit('get_allcliptags')

//recieve all clip tags
socket.on('res_allcliptags', (rec) => {
  //set complete clip
  let cliptags = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace('clip'),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    local: $.map(rec, (tag) => {
      return {
        clip: tag
      }
    })
  })
  //reflection of clip setting
  cliptags.initialize()
  //input auto complete clip
  $('.clip-tags-form').tagsinput({
    typeaheadjs: [{
      minLength: 1,
      highlight: true,
    },{
      minlength: 1,
      name: 'cliptags',
      displayKey: 'clip',
      valueKey: 'clip',
      source: cliptags.ttAdapter()
    }],
    freeInput: true
  })
})

//search button event
$('#search-button').click(() => {
  $('.clip-form').css('overflow-y', 'scroll')
  cflag = true
  $('.aleart-text').empty()
  incliptags = $('.clip-tags-form').tagsinput('items')
  //select dropdown item to array
  for(i = 0; i < $('.dropdown-menu li').length; i++){
    if($('#'+i).prop('checked')){
      intiletags.push($('#'+i).val())
    }
  }
  if(incliptags.length == 0){
    $('.clip-form').css('overflow', 'hidden')
    incliptags = undefined
    stacktile = []
    $('.dropdown-menu').empty()
    $('.aleart-text').append('
CLIP NOT FOUND
')
  }
  if(intiletags.length == 0){
    intiletags = undefined
  }else{
    cflag = false
  }
  inclipinfo.push({
    cliptags: incliptags,
    startdate: Date.parse($('.start').val()),
    enddate: Date.parse($('.end').val())
  })
  intileinfo.push({
    tiletags: intiletags,
    cliptags: incliptags,
    startdate: Date.parse($('.start').val()),
    enddate: Date.parse($('.end').val())
  })
  $('.clip-form').empty()
  $('.tile-form').empty()
  //send input data
  socket.emit('send_clipsearchdata', inclipinfo[0])
  if(intiletags !== undefined){
    socket.emit('send_tilesearchdata', intileinfo[0])
  }
  inclipinfo = []
  intileinfo = []
  intiletags = []
})

//tile tags dropdown
socket.on('res_alltiletags', (rec) => {
  if(rec.join(',') != stacktile.join(',')){
    $('.dropdown-menu').empty()
    for(i = 0; i < rec.length; i++){
      let $li = $('

')
      let $input = $('<input></input>', {
        id: i,
        type: "checkbox",
        value: rec[i]
      })
      let $label = $('', {
        for: i,
        text: rec[i]
      })
      $('.dropdown-menu').append($li.append($input))
      $('.dropdown-menu').append($li.append($label))
    }
  }
  stacktile = rec
})

//clip form
socket.on('res_clips', (rec) => {
  if(cflag){
    for(i = 0; i < rec.length; i++){
      let $div = $('

', {
        class: 'clip-field ' + i + ' clearfix'
      })
      $('.clip-form').append($div)
      for(j = 0; j < rec[i].tile.length; j++){
        switch(rec[i].tile[j].sty){
          case "txt":
            let $div = $('

', {
              class: 'col-sm-' + rec[i].tile[j].col + ' tile',
              text: rec[i].tile[j].con
            })
            $('.'+i).append($div)

            MathJax.Hub.Queue(["Typeset", MathJax.Hub, "clip-field"])
            break
          case "svg":
            let $span = $('', {
              class: 'col-sm-' + rec[i].tile[j].col + ' iframe-wrap'
            })
            let $iframe = $('', {
              id: rec[i].tile[j]._id,
              src: URL + "/html/read_nu/",
              scrolling: "auto"
            })
            $('.'+i).append(
              $span.append($iframe)
            )

            // stack to var
            svg_con_stack_clip[rec[i].tile[j]._id] = rec[i].tile[j].con

            // onload
            // caution: can not use arrow func
            $iframe.on('load', function(){
              // write path
              $(this)[0].contentWindow.loadPath(svg_con_stack_clip[$(this).attr("id")])
              // delete from stack
              delete svg_con_stack_clip[$(this).attr("id")]
            })
            break
          default:
            console.error('sty error')
            break
        }
      }
    }
  }
})

//tile form
socket.on('res_tiles', (rec) => {
  let $div = $('

', {
    class: 'clip-field clearfix with_tile'
  })
  $('.clip-form').append($div)
  for(i = 0; i < rec.length; i++){
    switch(rec[i].sty){
      case "txt":
        let $div = $('

', {
          class: 'col-sm-' + rec[i].col + ' tile',
          text: rec[i].con
        })
        $('.with_tile').append($div)

        MathJax.Hub.Queue(["Typeset", MathJax.Hub, "clip-field"])
        break
      case "svg":
        let $span = $('', {
          class: 'col-sm-' + rec[i].col + ' iframe-wrap'
        })
        let $iframe = $('', {
          id: rec[i]._id,
          src: URL + "/html/read_nu/",
          scrolling: "auto"
        })
        $('.with_tile').append(
          $span.append($iframe)
        )

        // stack to var
        svg_con_stack_tile[rec[i]._id] = rec[i].con

        // onload
        // caution: can not use arrow func
        $iframe.on('load', function(){
          // write path
          $(this)[0].contentWindow.loadPath(svg_con_stack_tile[$(this).attr("id")])
          // delete from stack
          delete svg_con_stack_tile[$(this).attr("id")]
        })
        break
      default:
        console.error('sty error')
        break
    }
  }
})

<|endoftext|>


<commit_before>(function () {
  "use strict";

  safari.application.addEventListener('message', function (event) {
    if (event.name === 'requestSettings') {
      return requestSettings(event);
    }
  }, false);

  function requestSettings (event) {
    var settings = safari.extension.settings,
      jitter, delay, blacklist, whitelist;

    jitter = parseInt(settings.jitter, 10);
    if (isNaN(jitter) || jitter < 0) {
      jitter = 0;
    }

    delay = parseInt(settings.delay, 10);
    if (isNaN(delay) || delay < 0) {
      delay = 0;
    }
    delay = 1000 * ((delay - jitter) + (2 * jitter * Math.random()));

    if (settings.mode === 'blacklist') {
      blacklist = sanitize(settings.blacklist);
    }
    else {
      whitelist = sanitize(settings.whitelist);
    }

    event.target.page.dispatchMessage('receiveSettings', {
      'blacklist': blacklist,
      'delay': delay,
      'timer': settings.timer,
      'whitelist': whitelist
    });
  }

  function sanitize (list) {
    list = list
      .toLowerCase()
      .replace(/[.]/g, '[.]')
      .replace(/([?*])/g, '.$1')
      .split(/\s+/)
      .sort()
      ;
    return new RegExp('^(' + list.join('|') + ')$', 'i');
  }
}());
<commit_msg>Remove superfluous methods.<commit_after>(function () {
  "use strict";

  safari.application.addEventListener('message', function (event) {
    if (event.name === 'requestSettings') {
      return requestSettings(event);
    }
  }, false);

  function requestSettings (event) {
    var settings = safari.extension.settings,
      jitter, delay, blacklist, whitelist;

    jitter = parseInt(settings.jitter, 10);
    if (isNaN(jitter) || jitter < 0) {
      jitter = 0;
    }

    delay = parseInt(settings.delay, 10);
    if (isNaN(delay) || delay < 0) {
      delay = 0;
    }
    delay = 1000 * ((delay - jitter) + (2 * jitter * Math.random()));

    if (settings.mode === 'blacklist') {
      blacklist = sanitize(settings.blacklist);
    }
    else {
      whitelist = sanitize(settings.whitelist);
    }

    event.target.page.dispatchMessage('receiveSettings', {
      'blacklist': blacklist,
      'delay': delay,
      'timer': settings.timer,
      'whitelist': whitelist
    });
  }

  function sanitize (list) {
    list = list.replace(/[.]/g, '[.]').replace(/([?*])/g, '.$1').split(/\s+/);
    return new RegExp('^(' + list.join('|') + ')$', 'i');
  }
}());
<|endoftext|>


<commit_before>module( "jump_navigation", {
  setup: function() {
    $('body')
      .append('')
      .append('')
      .append('');
    var $fancy = $('.fancy');
    $fancy.fancybox();
    $fancy.on('click', function() {
      setTimeout( function() {
        $('.fancybox-wrap').hide();
        $('.fancybox-overlay').hide();
      }, 100);
    }).first().trigger('click');

  },
  teardown: function() {
    $('.fancy').remove();
    F.close();
  }
});

/*
*
*= JUMPTO
*
*/

test( "jumpto accepts index, direction, and router as parameters", function() {

  var directionParam = 'right',
    indexParam = 1,
    routerParam = 'jumpto',
    jumptoSpy = this.spy(F, 'jumpto'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(indexParam, directionParam, routerParam);
  ok(jumptoSpy.called, "F.jumpto was called");
  ok(jumptoSpy.calledWithExactly(indexParam, directionParam, routerParam), "F.jumpto called with proper parameters");

  expect(2);
  delete F.current;

});

test( "jumpto returns if current is null", function() {
});

// **For the following two tests**
//
// If no direction param is present,
// jumpto default to current.direction.next or current.direction.prev, based on
// how the current.index value compares to the index value passed in.
test( "jumpto defaults direction to next when index param >= current.index", function() {
  // @TODO
});

test( "jumpto defaults direction to prev when index param < current.index", function() {
  // @TODO
});

test("if current.group at position index is not undefined, F.cancel() and F._start() are called", function() {
  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(directionParam);
  ok(cancelSpy.called, "F.cancel was called");
  ok(startSpy.called, "F._start() was called");

  expect(2);
  delete F.current;
});

// ensure order of functons is maintained
test("if current.group at position index is not undefined, F.cancel() is called, and then F._start() is called", function() {
  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(directionParam);
  ok(cancelSpy.called, "F.cancel was called");
  ok(startSpy.called, "F._start was called");
  ok(cancelSpy.calledBefore(startSpy), "F.cancel was called before F.start");

  expect(3);
  delete F.current;
});

test("if current.group at position index is undefined, F.cancel() and F._start() are not called", function() {

  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    bigIndex = 10000,
    current = {
      index: bigIndex, // pass in an impossible index
      direction: { next: 'left', prev: 'right' },
      loop: false, // ensure index stays as the param we passed in
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    },
    stubIndexCalc = this.stub(window, 'getScalar', function(arg) {
      // ensure we maintain unaltered bigIndex
      return arg;
    });
  $.extend(F, { current: current });

  F.jumpto(directionParam);

  ok(typeof(current.group[ bigIndex ]) === 'undefined', "current.group at position index is undefined");
  equal(cancelSpy.callCount, 0, "F.cancel was not called");
  equal(startSpy.callCount, 0, "F._start() was not called");

  expect(3);
  delete F.current;
});<commit_msg>final tests for jumpto<commit_after>module( "jump_navigation", {
  setup: function() {
    $('body')
      .append('')
      .append('')
      .append('');
    var $fancy = $('.fancy');
    $fancy.fancybox();
    $fancy.on('click', function() {
      setTimeout( function() {
        $('.fancybox-wrap').hide();
        $('.fancybox-overlay').hide();
      }, 100);
    }).first().trigger('click');

  },
  teardown: function() {
    $('.fancy').remove();
    F.close();
  }
});

/*
*
*= JUMPTO
*
*/

test( "jumpto accepts index, direction, and router as parameters", function() {

  var directionParam = 'right',
    indexParam = 1,
    routerParam = 'jumpto',
    jumptoSpy = this.spy(F, 'jumpto'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(indexParam, directionParam, routerParam);
  ok(jumptoSpy.called, "F.jumpto was called");
  ok(jumptoSpy.calledWithExactly(indexParam, directionParam, routerParam), "F.jumpto called with proper parameters");

  expect(2);
  delete F.current;

});

test( "jumpto returns if current is null", function() {
  var directionParam = 'right',
    jumptoSpy = this.spy(F, 'jumpto'),
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    bigIndex = 10000;

  $.extend(F, { current: null });

  F.jumpto(directionParam);

  ok(jumptoSpy.firstCall, "F.jumpto was called first");
  ok(jumptoSpy.calledOnce, "F.jumpto was called once");
  equal(jumptoSpy.returnValues[0], undefined, "F.jumpto returned no values");
  ok(jumptoSpy.lastCall, "F.jumpto was called last");
  equal(cancelSpy.callCount, 0, "F.cancel was not called");
  equal(startSpy.callCount, 0, "F._start() was not called");

  expect(6);
  delete F.current;
});

test( "jumpto does not call F.cancel if current is null", function() {
  var directionParam = 'right',
    jumptoSpy = this.spy(F, 'jumpto'),
    cancelSpy = this.spy(F, 'cancel'),
    bigIndex = 10000;

  $.extend(F, { current: null });

  F.jumpto(directionParam);

  ok(jumptoSpy.firstCall, "F.jumpto was called first");
  equal(cancelSpy.callCount, 0, "F.cancel was not called");
  ok(jumptoSpy.lastCall, "F.jumpto was called last");

  expect(3);
  delete F.current;
});

test( "jumpto does not call F._start if current is null", function() {
  var directionParam = 'right',
    jumptoSpy = this.spy(F, 'jumpto'),
    startSpy = this.spy(F, '_start'),
    bigIndex = 10000;

  $.extend(F, { current: null });

  F.jumpto(directionParam);

  ok(jumptoSpy.firstCall, "F.jumpto was called first");
  equal(startSpy.callCount, 0, "F._start was not called");
  ok(jumptoSpy.lastCall, "F.jumpto was called last");

  expect(3);
  delete F.current;
});

test("if current.group at position index is not undefined, F.cancel() and F._start() are called", function() {
  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(directionParam);
  ok(cancelSpy.called, "F.cancel was called");
  ok(startSpy.called, "F._start() was called");

  expect(2);
  delete F.current;
});

// ensure order of functons is maintained
test("if current.group at position index is not undefined, F.cancel() is called, and then F._start() is called", function() {
  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    current = {
      index: 1,
      direction: { next: 'left', prev: 'right' },
      loop: true,
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    };
  $.extend(F, { current: current });

  F.jumpto(directionParam);
  ok(cancelSpy.called, "F.cancel was called");
  ok(startSpy.called, "F._start was called");
  ok(cancelSpy.calledBefore(startSpy), "F.cancel was called before F.start");

  expect(3);
  delete F.current;
});

test("if current.group at position index is undefined, F.cancel() and F._start() are not called", function() {

  var directionParam = 'right',
    cancelSpy = this.spy(F, 'cancel'),
    startSpy = this.spy(F, '_start'),
    bigIndex = 10000,
    current = {
      index: bigIndex, // pass in an impossible index
      direction: { next: 'left', prev: 'right' },
      loop: false, // ensure index stays as the param we passed in
      group: [
        { href: "img/cat.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-2.jpeg", isDom: true, title: "", type: "image" },
        { href: "img/cat-3.jpeg", isDom: true, title: "", type: "image" }
      ]
    },
    stubIndexCalc = this.stub(window, 'getScalar', function(arg) {
      // ensure we maintain unaltered bigIndex
      return arg;
    });
  $.extend(F, { current: current });

  F.jumpto(directionParam);

  ok(typeof(current.group[ bigIndex ]) === 'undefined', "current.group at position index is undefined");
  equal(cancelSpy.callCount, 0, "F.cancel was not called");
  equal(startSpy.callCount, 0, "F._start() was not called");

  expect(3);
  delete F.current;
});<|endoftext|>


<commit_before>import styles from './App.less';

import React, { Component } from 'react';
import createPagination from 'createPagination';

export default class App extends Component {
  constructor() {
    super();

    this.state = {
      itemsPerPage: 10,
      maxPages: 5,
      currentPage: 4,
      totalItems: 127
    };

    this.updateItemsPerPage = ::this.updateItemsPerPage;
    this.updateMaxPages = ::this.updateMaxPages;
    this.updateCurrentPage = ::this.updateCurrentPage;
    this.updateTotalItems = ::this.updateTotalItems;
    this.setCurrentPage = ::this.setCurrentPage;
  }

  updateItemsPerPage(event) {
    this.setState({
      itemsPerPage: parseInt(event.target.value, 10)
    });
  }

  updateMaxPages(event) {
    this.setState({
      maxPages: parseInt(event.target.value, 10)
    });
  }

  updateCurrentPage(event) {
    this.setState({
      currentPage: parseInt(event.target.value, 10)
    });
  }

  updateTotalItems(event) {
    this.setState({
      totalItems: parseInt(event.target.value, 10)
    });
  }

  setCurrentPage(page) {
    this.setState({
      currentPage: page
    });
  }

  render() {
    const { itemsPerPage, maxPages, currentPage, totalItems } = this.state;
    const { showFirst, showPrev, pages, showNext, showLast, lastPage } =
      createPagination({ itemsPerPage, maxPages })({ currentPage, totalItems });

    return (

              Items per page:

            <input id="items-per-page" className={styles.input}
                   type="number" min="1"
                   value={itemsPerPage} onChange={this.updateItemsPerPage} />

              Max pages:

            <input id="max-pages" className={styles.input}
                   type="number" min="1"
                   value={maxPages}  onChange={this.updateMaxPages} />

              Current page:

            <input id="current-page" className={styles.input}
                   type="number" min="1"
                   value={currentPage} onChange={this.updateCurrentPage} />

              Total items:

            <input id="total-items" className={styles.input}
                   type="number" min="1"
                   value={totalItems} onChange={this.updateTotalItems} />

          {
            <button className={`${styles.button} ${showFirst ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, 1)}>
              First
            </button>
          }
          {
            <button className={`${styles.button} ${showPrev ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, currentPage - 1)}>
              Prev
            </button>
          }
          {
            pages.map(page =>
              <button className={`${styles.button} ${styles.page} ${page === currentPage ? styles.currentPage : ''}`}
                 onClick={this.setCurrentPage.bind(null, page)}
                 key={page}>
                {page}
              </button>
            )
          }
          {
            <button className={`${styles.button} ${showNext ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, currentPage + 1)}>
              Next
            </button>
          }
          {
            <button className={`${styles.button} ${showLast ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, lastPage)}>
              Last
            </button>
          }

            Crafted with love by
            {' '}@moroshko

    );
  }
};
<commit_msg>Add min and max values for inputs in demo<commit_after>import styles from './App.less';

import React, { Component } from 'react';
import createPagination from 'createPagination';

export default class App extends Component {
  constructor() {
    super();

    const itemsPerPage = 10;
    const maxPages = 5;
    const currentPage = 4;
    const totalItems = 127;
    const paginationData =
      createPagination({ itemsPerPage, maxPages })({ currentPage, totalItems });

    this.state = {
      itemsPerPage,
      maxPages,
      currentPage,
      totalItems,
      ...paginationData
    };

    this.updateItemsPerPage = ::this.updateItemsPerPage;
    this.updateMaxPages = ::this.updateMaxPages;
    this.updateCurrentPage = ::this.updateCurrentPage;
    this.updateTotalItems = ::this.updateTotalItems;
    this.setCurrentPage = ::this.setCurrentPage;
  }

  updateState(newState) {
    const nextState = {
      ...this.state,
      ...newState
    };
    const { itemsPerPage, maxPages, currentPage, totalItems } = nextState;

    try {
      const newPaginationData =
        createPagination({ itemsPerPage, maxPages })({ currentPage, totalItems });

      this.setState({
        ...nextState,
        ...newPaginationData
      });
    } catch (error) {
      console.info(error); // eslint-disable-line no-console
    }
  }

  updateItemsPerPage(event) {
    this.updateState({
      itemsPerPage: parseInt(event.target.value, 10)
    });
  }

  updateMaxPages(event) {
    this.updateState({
      maxPages: parseInt(event.target.value, 10)
    });
  }

  updateCurrentPage(event) {
    this.updateState({
      currentPage: parseInt(event.target.value, 10)
    });
  }

  updateTotalItems(event) {
    this.updateState({
      totalItems: parseInt(event.target.value, 10)
    });
  }

  setCurrentPage(page) {
    this.updateState({
      currentPage: page
    });
  }

  render() {
    const { itemsPerPage, maxPages, currentPage, totalItems,
            showFirst, showPrev, pages, showNext, showLast, lastPage } = this.state;
    const maxItemsPerPage = Math.floor((totalItems - 1) / (currentPage - 1));
    const minTotalItems = itemsPerPage * (currentPage - 1) + 1;

    return (

              Items per page:

            <input id="items-per-page" className={styles.input}
                   type="number" min="1" max={maxItemsPerPage}
                   value={itemsPerPage} onChange={this.updateItemsPerPage} />

              Max pages:

            <input id="max-pages" className={styles.input}
                   type="number" min="1"
                   value={maxPages}  onChange={this.updateMaxPages} />

              Current page:

            <input id="current-page" className={styles.input}
                   type="number" min="1" max={lastPage}
                   value={currentPage} onChange={this.updateCurrentPage} />

              Total items:

            <input id="total-items" className={styles.input}
                   type="number" min={minTotalItems}
                   value={totalItems} onChange={this.updateTotalItems} />

          {
            <button className={`${styles.button} ${showFirst ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, 1)}>
              First
            </button>
          }
          {
            <button className={`${styles.button} ${showPrev ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, currentPage - 1)}>
              Prev
            </button>
          }
          {
            pages.map(page =>
              <button className={`${styles.button} ${styles.page} ${page === currentPage ? styles.currentPage : ''}`}
                 onClick={this.setCurrentPage.bind(null, page)}
                 key={page}>
                {page}
              </button>
            )
          }
          {
            <button className={`${styles.button} ${showNext ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, currentPage + 1)}>
              Next
            </button>
          }
          {
            <button className={`${styles.button} ${showLast ? '' : styles.hidden}`}
               onClick={this.setCurrentPage.bind(null, lastPage)}>
              Last
            </button>
          }

            Crafted with love by
            {' '}@moroshko

    );
  }
};
<|endoftext|>


<commit_before>// TODO:
//
// - See if there is a setup feature so the version number only has
//   to be changed in one place.

QUnit.test("Make sure the version number is accurate", function(assert) {

  // Given
  var required_version_number = "0.5.0";

  // When
  var imageLoader = new ImageLoader_0_5_x();

  // Then
  assert.equal(imageLoader.version_number(), required_version_number);

});

QUnit.test("Integration Test: Basic call verification", function(assert) {

    var target_string = '';

    // Given
    var imageLoader = new ImageLoader_0_5_x();

    // TODO: Add environmental param override to standardize output.

    // Then
    assert.equal(imageLoader.image_string_from_params( { } ), target_string);

});

QUnit.test("Integration Test: Verify environmental parameters load", function(assert) {

    // It's a little weird to tests these params, but I
    // want to make sure they work for integration purposes.

    // Given
    var target_dpr = window.devicePixelRatio ? window.devicePixelRatio : 1;

    // When
    var imageLoader = new ImageLoader_0_5_x();
    imageLoader.load_environmental_params();

    // Then
    assert.equal(imageLoader._dpr, target_dpr);

});

QUnit.test("2x DPR Basic call stright to load_params", function(assert) {

  // Given
	var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 2,
    	image_name: "horses.jpg",
      percent_of_viewport_width: 50,
      quality: 80,
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then - Verify instance variables.
  assert.equal(imageLoader._dpr, 2, "Device pixel resolution");
  assert.equal(imageLoader._image_name, "horses.jpg", "Image name");
  assert.equal(imageLoader._raw_height, 1067, "Raw image height");
  assert.equal(imageLoader._raw_width, 1600, "Raw image width");
  assert.equal(imageLoader._percent_of_viewport_width, 50, "Percent of viewport width");
  assert.equal(imageLoader._quality, 80, "Quality level");
  assert.equal(imageLoader._viewport_height, 680, "Viewport height");
  assert.equal(imageLoader._viewport_width, 1024, "Viewport width");
  assert.equal(imageLoader._url_template, "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]", "URL Template");

  // And - Verify functions
  assert.equal(imageLoader.url_to_call(), "http://res.cloudinary.com/demo/image/upload/w_1024,h_682,q_80/horses.jpg", "Final URL");
  assert.equal(imageLoader.render_height(), 341, "Render height");
  assert.equal(imageLoader.render_width(), 512, "Render width");
  assert.equal(imageLoader.url_request_height(), 682, "Request height");
  assert.equal(imageLoader.url_request_width(), 1024, "Request width");
  assert.equal(imageLoader.img_tag(), '', "Image tag");

});

QUnit.test("2x DPR stright to load_params with max width enforced", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
      dpr: 2,
      image_name: "horses.jpg",
      percent_of_viewport_width: 50,
      quality: 80,
      raw_height: 1067,
      raw_width: 1600,
      max_render_width: 200,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader._max_render_width, 200, "Max render width");
  assert.equal(imageLoader.render_width(), 200, "Verify render_width == max_render_width");
  assert.equal(imageLoader.render_height(), 133, "Verify render_width == max_render_width");
  assert.equal(imageLoader.url_request_width(), 400, "Request width");
  assert.equal(imageLoader.url_request_height(), 266, "Request height");

});

QUnit.test("Verify defaults", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
      image_name: "horses.jpg",
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader._max_render_width, 1600, "Default Max Render Width == Raw Width");
  assert.equal(imageLoader._dpr, 1, "Device pixel ratio default");
  assert.equal(imageLoader._percent_of_viewport_width, 100, "Percent of viewport width default");
  assert.equal(imageLoader._quality, 80, "Quality default");

});

QUnit.test("Ensure values are integers", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 2,
      image_name: "horses.jpg",
      percent_of_viewport_width: 25,
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1021,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader.render_height(), 170, "Render height");
  assert.equal(imageLoader.render_width(), 255, "Render width is 255 and not 255.25");
  assert.equal(imageLoader.url_request_height(), 340, "Request height");
  assert.equal(imageLoader.url_request_width(), 510, "Request width is 510 and not 510.5");

});

QUnit.test("Restrict height", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 1,
      image_name: "horses.jpg",
      percent_of_viewport_height: 90,
      raw_height: 2000,
      raw_width: 2200,
      viewport_height: 1000,
      viewport_width: 1800,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  assert.equal(imageLoader.render_height(), 900, "Render height");

});

<commit_msg>Prepped for better call to the primary integration test.<commit_after>// TODO:
//
// - See if there is a setup feature so the version number only has
//   to be changed in one place.

QUnit.test("Make sure the version number is accurate", function(assert) {

  // Given
  var required_version_number = "0.5.0";

  // When
  var imageLoader = new ImageLoader_0_5_x();

  // Then
  assert.equal(imageLoader.version_number(), required_version_number);

});

QUnit.test("Integration Test: Basic call verification", function(assert) {

    // Preflight
    var target_string = '';

    // Given
    var imageLoader = new ImageLoader_0_5_x();

    // When
    var image_params = {
        filename: "horses.jpg",
        alt_text: "Photo of Horses",
        source_width: 1600,
        source_height: 1067
    };

    var result_string = imageLoader.image_string_from_params(image_params);

    // TODO: Add environmental param override to standardize output independent of browser.

    // Then
    assert.equal(result_string, target_string);

});

QUnit.test("Integration Test: Verify environmental parameters load", function(assert) {

    // It's a little weird to tests these params, but I
    // want to make sure they work for integration purposes.

    // Given
    var target_dpr = window.devicePixelRatio ? window.devicePixelRatio : 1;

    // When
    var imageLoader = new ImageLoader_0_5_x();
    imageLoader.load_environmental_params();

    // Then
    assert.equal(imageLoader._dpr, target_dpr);

});

QUnit.test("2x DPR Basic call stright to load_params", function(assert) {

  // Given
	var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 2,
    	image_name: "horses.jpg",
      percent_of_viewport_width: 50,
      quality: 80,
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then - Verify instance variables.
  assert.equal(imageLoader._dpr, 2, "Device pixel resolution");
  assert.equal(imageLoader._image_name, "horses.jpg", "Image name");
  assert.equal(imageLoader._raw_height, 1067, "Raw image height");
  assert.equal(imageLoader._raw_width, 1600, "Raw image width");
  assert.equal(imageLoader._percent_of_viewport_width, 50, "Percent of viewport width");
  assert.equal(imageLoader._quality, 80, "Quality level");
  assert.equal(imageLoader._viewport_height, 680, "Viewport height");
  assert.equal(imageLoader._viewport_width, 1024, "Viewport width");
  assert.equal(imageLoader._url_template, "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]", "URL Template");

  // And - Verify functions
  assert.equal(imageLoader.url_to_call(), "http://res.cloudinary.com/demo/image/upload/w_1024,h_682,q_80/horses.jpg", "Final URL");
  assert.equal(imageLoader.render_height(), 341, "Render height");
  assert.equal(imageLoader.render_width(), 512, "Render width");
  assert.equal(imageLoader.url_request_height(), 682, "Request height");
  assert.equal(imageLoader.url_request_width(), 1024, "Request width");
  assert.equal(imageLoader.img_tag(), '', "Image tag");

});

QUnit.test("2x DPR stright to load_params with max width enforced", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
      dpr: 2,
      image_name: "horses.jpg",
      percent_of_viewport_width: 50,
      quality: 80,
      raw_height: 1067,
      raw_width: 1600,
      max_render_width: 200,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader._max_render_width, 200, "Max render width");
  assert.equal(imageLoader.render_width(), 200, "Verify render_width == max_render_width");
  assert.equal(imageLoader.render_height(), 133, "Verify render_width == max_render_width");
  assert.equal(imageLoader.url_request_width(), 400, "Request width");
  assert.equal(imageLoader.url_request_height(), 266, "Request height");

});

QUnit.test("Verify defaults", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
      image_name: "horses.jpg",
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1024,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader._max_render_width, 1600, "Default Max Render Width == Raw Width");
  assert.equal(imageLoader._dpr, 1, "Device pixel ratio default");
  assert.equal(imageLoader._percent_of_viewport_width, 100, "Percent of viewport width default");
  assert.equal(imageLoader._quality, 80, "Quality default");

});

QUnit.test("Ensure values are integers", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 2,
      image_name: "horses.jpg",
      percent_of_viewport_width: 25,
      raw_height: 1067,
      raw_width: 1600,
      viewport_height: 680,
      viewport_width: 1021,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  // Then:
  assert.equal(imageLoader.render_height(), 170, "Render height");
  assert.equal(imageLoader.render_width(), 255, "Render width is 255 and not 255.25");
  assert.equal(imageLoader.url_request_height(), 340, "Request height");
  assert.equal(imageLoader.url_request_width(), 510, "Request width is 510 and not 510.5");

});

QUnit.test("Restrict height", function(assert) {

  // Given
  var imageLoader = new ImageLoader_0_5_x();

  // When
  imageLoader.load_params(
    {
    	dpr: 1,
      image_name: "horses.jpg",
      percent_of_viewport_height: 90,
      raw_height: 2000,
      raw_width: 2200,
      viewport_height: 1000,
      viewport_width: 1800,
      url_template: "http://res.cloudinary.com/demo/image/upload/w_[WIDTH],h_[HEIGHT],q_[QUALITY]/[IMAGE_NAME]"
    }
  );

  assert.equal(imageLoader.render_height(), 900, "Render height");

});

<|endoftext|>


<commit_before>/**
 * Utilities for the greenweb add-on
 *
 * @author Arend-Jan Tetteroo <aj@cleanbits.net>
 * @copyright Cleanbits/The Green Web Foundation 2010-2012
 */

/**
 * Get the url from the given location
 */
function getUrl(loc)
{
    loc = this.stripProtocolFromUrl(loc);
    if(loc == false){
        return false;
    }
    loc = this.stripPageFromUrl(loc);
    loc = this.stripPortFromUrl(loc);
    return loc;
}

/**
 * Strip the protocol from the location
 * If no http or https given, then return false
 */
function stripProtocolFromUrl(loc)
{
    if(loc == undefined){
        return false;
    }
    var prot = loc.substring(0,5);
    if(prot == 'http:'){
        loc = loc.substring(7);
    }else if(prot == 'https'){
        loc = loc.substring(8);
    }else{
        return false;
    }
    return loc;
}

/**
 * Only use the domain.tld, not the page
 */
function stripPageFromUrl(loc)
{
    var temp = new Array();
    temp = loc.split('/');
    loc = temp[0];
    return loc;
}

/**
 * Only use the domain.tld, not the port
 */
function stripPortFromUrl(loc)
{
    var temp = new Array();
    temp = loc.split(':');
    loc = temp[0];
    return loc;
}

/**
 * Get the image with a cleanbits link around it
 */
function getLinkImage(image,tooltip)
{
    var output = "";
    output += image + "";
    return output;
}

/**
 * Get the image element based on the color
 */
function getImage(color)
{
    var img = getImagePath(color);
    return  "";
}

/**
 * Get the image path based on file
 */
function getImagePath(file)
{
    var icons = new Array();
    icons['green']      = new Array(safari.extension.baseURI + "images/smily_16_kleur_happy.png", 16);
    icons['grey']       = new Array(safari.extension.baseURI + "images/smily_16_kleur_sad.png", 16);
    icons['greentoolbar'] = new Array(safari.extension.baseURI + "images/smily_16_happy.png", 16);
    icons['greytoolbar'] = new Array(safari.extension.baseURI + "images/smily_16_sad.png", 16);
    icons['greenquestion'] = new Array(safari.extension.baseURI + "images/greenquestion16x16.png",16);
    icons['greenfan'] = new Array(safari.extension.baseURI + "images/greenfan16x16.png", 16);
    icons['greenhouse'] = new Array(safari.extension.baseURI + "images/greenhouse20x20.gif", 20);
    icons['greenpopover'] = new Array(safari.extension.baseURI + "images/smily_26_happy.png", 26);
    icons['greypopover'] = new Array(safari.extension.baseURI + "images/smily_26_sad.png", 26);
    icons['greenfanpopover'] = new Array(safari.extension.baseURI + "images/greenfan26x26.png",26);

     if(icons[file]){
        return icons[file];
    }

    img = new Array();
    img[1] = 16;
    img[0] = iconPath = 'http://images.cleanbits.net/icons/' + file + "20x20.gif";
    return img;
}

/**
 * Get the resulting image from the data
 */
function getResult(data)
{
    icon = getIcon(data);
    title = getTitle(data);
    return getLinkImage(getImage(icon),title) + getPoweredResult(data) + ' ';
}

/**
 * Get the resulting image from the data as jquery dom node
 */
function getResultNode(data)
{
    icon = getIcon(data);
    return getLinkNode(icon).append(getPoweredResult(data));
}

function getLinkNode(color)
{
    return addLinkNodeToImage(getImageNode(color));
}

function addLinkNodeToImage(image, provider)
{
    var href = 'http://www.thegreenwebfoundation.org';
    if(provider){
        href = href + '/thegreenweb/#/providers/' + provider;
    }
    var a    = $("", { href: href, class: 'TGWF-addon' })
                 .append(image);
    return a;
}

function getImageNode(color)
{
    var img = getImagePath(color);
    return $('', { style: 'width:' + img[1] + 'px !important; height:' + img[1] + 'px !important;border:none;', src: img[0]});
}

/**
 * Get the resulting image from the data
 */
function getPoweredResult(data)
{
    if(data.poweredby) {
        icon = 'greenhouse';
        return getLinkNode(icon);
    }else{
        return '';
    }
}

/**
 * Get the icon based on the data
 */
function getIcon(data)
{
    var icon = 'grey';
    if(data.green) {
        icon = 'green';
        if(data.icon) {
            icon = data.icon;
        }
    }else if(data.data == false){
        icon = 'greenquestion';
    }
    return icon;
}

/**
 * Get the icon based on the data
 */
function getIconPopover(data)
{
    icon = this.getIcon(data);
    return icon + 'popover';
}

/**
 * Get the title based on the data
 */
function getTitle(data)
{
    if(data.green) {
        if(data.hostedby){
            title = 'Sustainably hosted by ' + data.hostedby;
        }else{
            title = 'is made sustainable through Cleanbits';
        }
    }else{
        if(data.data == false){
            // No data available, show help message
            title = " No data available yet for this country domain. Wanna help? Contact us through www.cleanbits.net";
        }else{
            // Data available, so show grey site
            title = data.url + ' is hosted grey';
        }
    }
    return title;
}

/**
 * Show the start message
 */
function startMessage()
{
    msg = " The Green Web";
    document.getElementById('thegreenweb').innerHTML = msg;
}

/**
 * Show the resulting icon based on the response
 */
function showIcon(resp, type)
{
    title = getTitle(resp);
    provider = false;
    if (resp.hostedbyid) {
        provider = resp.hostedbyid;
    }
    var link = addLinkNodeToImage(getImageNode(getIconPopover(resp)),provider);

    link.append($('', { id: 'thegreenwebtext', text: title}));

    $('#thegreenweb').html(link);

    showToolbarIcon(resp.green);
}

/**
 * Change the toolbaricon to happy smiley for green or sad smily for grey
 */
function showToolbarIcon(green)
{
    var itemArray = safari.extension.toolbarItems;
    for (var i = 0; i < itemArray.length; ++i) {
        var item = itemArray[i];
        if (item.identifier == "thegreenweb")
        {
            if(green){
                item.image = safari.extension.baseURI + "images/smily_16_happy.png";
            } else {
                item.image = safari.extension.baseURI + "images/smily_16_sad.png";
            }

            localStorage.lasturl = url;
        }
    }
}

/**
 * Do a request to the api, if not cached
 */
function doRequest(type)
{
    activewindow = safari.application.activeBrowserWindow;
    url = getUrl(activewindow.activeTab.url);

    if(url !== false){
        date = new Date();
        currenttime = date.getTime();

        var cached = localStorage.getItem("cache"+url);
        if(cached !== null){
            // Item in cache, check cachetime
            var resp = JSON.parse(cached);
            if(resp.time > currenttime - 3600000){
                showIcon(resp);
                return;
            }
        }

        // Url is valid and not cached, so retrieve from api
        doApiCall(url);

    }else{
        startMessage();
    }
}

/**
 * Do an api call
 */
function doApiCall(url)
{
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "http://api.thegreenwebfoundation.org/greencheck/"+url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
                var resp = JSON.parse(xhr.responseText);
                resp.time = currenttime;
                localStorage.setItem("cache"+url, JSON.stringify(resp));
                showIcon(resp);
        }
    }
    xhr.send();
}<commit_msg>Fix qtip with missing titlelink method Refactor old methods<commit_after>/**
 * Utilities for the greenweb add-on
 *
 * @author Arend-Jan Tetteroo <aj@cleanbits.net>
 * @copyright Cleanbits/The Green Web Foundation 2010-2012
 */

/**
 * Get the url from the given location
 */
function getUrl(loc)
{
    loc = this.stripProtocolFromUrl(loc);
    if(loc == false){
        return false;
    }
    loc = this.stripPageFromUrl(loc);
    loc = this.stripPortFromUrl(loc);
    return loc;
}

/**
 * Strip the protocol from the location
 * If no http or https given, then return false
 */
function stripProtocolFromUrl(loc)
{
    if(loc == undefined){
        return false;
    }
    var prot = loc.substring(0,5);
    if(prot == 'http:'){
        loc = loc.substring(7);
    }else if(prot == 'https'){
        loc = loc.substring(8);
    }else{
        return false;
    }
    return loc;
}

/**
 * Only use the domain.tld, not the page
 */
function stripPageFromUrl(loc)
{
    var temp = new Array();
    temp = loc.split('/');
    loc = temp[0];
    return loc;
}

/**
 * Only use the domain.tld, not the port
 */
function stripPortFromUrl(loc)
{
    var temp = new Array();
    temp = loc.split(':');
    loc = temp[0];
    return loc;
}

/**
 * Get the image with a cleanbits link around it
 */
function getLinkImage(image,tooltip)
{
    var output = "";
    output += image + "";
    return output;
}

/**
 * Get the image element based on the color
 */
function getImage(color)
{
    var img = getImagePath(color);
    return  "";
}

/**
 * Get the image path based on file
 */
function getImagePath(file)
{
    var icons = new Array();
    icons['green']      = new Array(safari.extension.baseURI + "images/smily_16_kleur_happy.png", 16);
    icons['grey']       = new Array(safari.extension.baseURI + "images/smily_16_kleur_sad.png", 16);
    icons['greentoolbar'] = new Array(safari.extension.baseURI + "images/smily_16_happy.png", 16);
    icons['greytoolbar'] = new Array(safari.extension.baseURI + "images/smily_16_sad.png", 16);
    icons['greenquestion'] = new Array(safari.extension.baseURI + "images/greenquestion16x16.png",16);
    icons['greenfan'] = new Array(safari.extension.baseURI + "images/greenfan16x16.png", 16);
    icons['greenhouse'] = new Array(safari.extension.baseURI + "images/greenhouse20x20.gif", 20);
    icons['greenpopover'] = new Array(safari.extension.baseURI + "images/smily_26_happy.png", 26);
    icons['greypopover'] = new Array(safari.extension.baseURI + "images/smily_26_sad.png", 26);
    icons['greenfanpopover'] = new Array(safari.extension.baseURI + "images/greenfan26x26.png",26);

     if(icons[file]){
        return icons[file];
    }

    img = new Array();
    img[1] = 16;
    img[0] = iconPath = 'http://images.cleanbits.net/icons/' + file + "20x20.gif";
    return img;
}

/**
 * Get the resulting image from the data
 */
function getResult(data)
{
    icon = getIcon(data);
    title = getTitle(data);
    return getLinkImage(getImage(icon),title) + getPoweredResult(data) + ' ';
}

/**
 * Get the resulting image from the data as jquery dom node
 */
function getResultNode(data)
{
    icon = getIcon(data);
    provider = false;
    if(data.hostedbyid){
        provider = data.hostedbyid;
    }
    return addLinkNodeToImage(getImageNode(icon),provider).append(getPoweredResult(data));
}

function getProviderLink(provider)
{
    var href = 'http://www.thegreenwebfoundation.org';
    if(provider){
        href = href + '/thegreenweb/#/providers/' + provider;
    }
    return href;
}

function addLinkNodeToImage(image, provider)
{
    var href = getProviderLink(provider);
    var a    = $("", { href: href, class: 'TGWF-addon' })
                 .append(image);
    return a;
}

function getImageNode(color)
{
    var img = getImagePath(color);
    return $('', { style: 'width:' + img[1] + 'px !important; height:' + img[1] + 'px !important;border:none;', src: img[0]});
}

/**
 * Get the resulting image from the data
 */
function getPoweredResult(data)
{
    if(data.poweredby) {
        icon = 'greenhouse';
        return addLinkNodeToImage(getImageNode(icon),false);
    }else{
        return '';
    }
}

/**
 * Get the icon based on the data
 */
function getIcon(data)
{
    var icon = 'grey';
    if(data.green) {
        icon = 'green';
        if(data.icon) {
            icon = data.icon;
        }
    }else if(data.data == false){
        icon = 'greenquestion';
    }
    return icon;
}

/**
 * Get the icon based on the data
 */
function getIconPopover(data)
{
    icon = this.getIcon(data);
    return icon + 'popover';
}

/**
 * Get the title based on the data
 */
function getTitle(data)
{
    if(data.green) {
        if(data.hostedby){
            title = 'Sustainably hosted by ' + data.hostedby;
        }else{
            title = 'is made sustainable through Cleanbits';
        }
    }else{
        if(data.data == false){
            // No data available, show help message
            title = " No data available yet for this country domain. Wanna help? Contact us through www.cleanbits.net";
        }else{
            // Data available, so show grey site
            title = data.url + ' is hosted grey';
        }
    }
    return title;
}

/**
 * Get the title based on the data
 */
function getTitleWithLink(data)
{
    title = '';
    if (data) {
        if (data.green) {
            if (data.hostedby) {
                var href = getProviderLink(data.hostedbyid);
                title = data.url + ' ' + '' + ' is sustainably hosted by ' + ' ' + data.hostedby + '';
            } else {
                title = data.url + ' ' + 'is made sustainable through Cleanbits';
            }
        } else {
            if (data.data == false) {
                var href = getProviderLink(false);
                // No data available, show help message
                title = "No data available yet for this country domain. Wanna help? Contact us through "
                        + " www.thegreenwebfoundation.org";
            } else {
                // Data available, so show grey site
                title = data.url + ' ' + ' is hosted grey';
            }
        }
    }
    return title;
}

/**
 * Show the start message
 */
function startMessage()
{
    msg = " The Green Web";
    document.getElementById('thegreenweb').innerHTML = msg;
}

/**
 * Show the resulting icon based on the response
 */
function showIcon(resp, type)
{
    title = getTitle(resp);
    provider = false;
    if (resp.hostedbyid) {
        provider = resp.hostedbyid;
    }
    var link = addLinkNodeToImage(getImageNode(getIconPopover(resp)),provider);

    link.append($('', { id: 'thegreenwebtext', text: title}));

    $('#thegreenweb').html(link);

    showToolbarIcon(resp.green);
}

/**
 * Change the toolbaricon to happy smiley for green or sad smily for grey
 */
function showToolbarIcon(green)
{
    var itemArray = safari.extension.toolbarItems;
    for (var i = 0; i < itemArray.length; ++i) {
        var item = itemArray[i];
        if (item.identifier == "thegreenweb")
        {
            if(green){
                item.image = safari.extension.baseURI + "images/smily_16_happy.png";
            } else {
                item.image = safari.extension.baseURI + "images/smily_16_sad.png";
            }

            localStorage.lasturl = url;
        }
    }
}

/**
 * Do a request to the api, if not cached
 */
function doRequest(type)
{
    activewindow = safari.application.activeBrowserWindow;
    url = getUrl(activewindow.activeTab.url);

    if(url !== false){
        date = new Date();
        currenttime = date.getTime();

        var cached = localStorage.getItem("cache"+url);
        if(cached !== null){
            // Item in cache, check cachetime
            var resp = JSON.parse(cached);
            if(resp.time > currenttime - 3600000){
                showIcon(resp);
                return;
            }
        }

        // Url is valid and not cached, so retrieve from api
        doApiCall(url);

    }else{
        startMessage();
    }
}

/**
 * Do an api call
 */
function doApiCall(url)
{
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "http://api.thegreenwebfoundation.org/greencheck/"+url, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
                var resp = JSON.parse(xhr.responseText);
                resp.time = currenttime;
                localStorage.setItem("cache"+url, JSON.stringify(resp));
                showIcon(resp);
        }
    }
    xhr.send();
}<|endoftext|>


<commit_before>import React from 'react';

import './styles.css';

export default class About extends React.Component {
  render() {
    return (

Um verkefnið

Á þessum vef finnur þú upplýsingar og tölfræði um þingmenn og þingflokka fyrir kjörtímabilið 2013-2016.

Markmið þessa verkefnis er að sækja hluta af því mikla magni gagna sem finna má á vef Alþingis, vinna úr þeim gögnum og setja fram á skemmtilegan, fræðandi og helst af öllu hlutlausan máta.

Þetta verkefni er eftir Bæring Gunnar Steinþórsson, sérstakar þakkir fá samstarfsfélagar mínir hjá Aranja;
 Davíð Bachmann Jóhannesson, Sebastian Helms og Ægir Giraldo Þorsteinsson fyrir hjálp við útfærslu og hönnun á vefnum.

Ábendingar og hugmyndir eru vel þegnar á thingmenn@aranja.com.

    );
  }
}
<commit_msg>Frontend: update about page<commit_after>import React from 'react';

import './styles.css';

export default class About extends React.Component {
  render() {
    return (

Um verkefnið

Um vefinn

Á þessum vef finnur þú upplýsingar og tölfræði um þingmenn og þingflokka fyrir kjörtímabilið 2013-2016.

Markmið vefsins er að sækja gögn sem finna má á vef Alþingis, vinna úr þeim og setja fram á skemmtilegan, fræðandi og helst af öllu hlutlausan máta.

Hvaðan koma gögnin?

Öll gögn eru sótt af vef Alþingis og skiptast þau niður í tvo meginflokka: atkvæðaskrá og ræður eftir þingum.

Gögnin eru sótt sjálfkrafa, hver einasta undirsíða fyrir alla þingmenn er skoðuð og gögnum breytt yfir á form sem þægilegt er fyrir tölvu að vinna úr þeim.

Því næst er byrjað að vinna úr gögnunum, þau flokkuð og skoðuð fyrir hvern þingmann. Eftir að hafa unnið úr þeim fyrir hvern þingmann eru upplýsingum safnað saman til þess að fá gögn fyrir þingflokkana.

Verkefnið

Þetta er verkefni eftir Bæring Gunnar Steinþórsson.

Sérstakar þakkir fá samstarfsfélagar mínir hjá Aranja;
 Davíð Bachmann Jóhannesson, Sebastian Helms og Ægir Giraldo Þorsteinsson fyrir hjálp við útfærslu og hönnun á vefnum.

Ábendingar og hugmyndir eru vel þegnar á thingmenn@aranja.com.

    );
  }
}
<|endoftext|>