<commit_before>var list      = require('./list');
var Issue     = require('./issue');
var config    = require('../config');

var issues = {

	/**
	 * List
	 *
	 * List of all validator issues.
	 */
	list: list,

	/**
	 * Issue
	 *
	 * Issue constructor
	 */
	Issue: Issue,

    /**
     * Filter Fieldmaps
     *
     * Remove fieldmap related warnings if no fieldmaps
     * are present.
     */
    filterFieldMaps: function (issueList, summary) {
        if (summary.modalities.indexOf("fieldmap") < 0) {
            var filteredIssueList = [];
            var fieldmapRelatedCodes = [6, 7, 8, 9];
            for (var i = 0; i < issueList.length; i++) {
                var issue = issueList[i];
                if (fieldmapRelatedCodes.indexOf(issue.code) < 0) {
                    filteredIssueList.push(issue);
                }
            }
            issueList = filteredIssueList;
        }
        return issueList
    },

	/**
     * Format Issues
     */
    format: function (issueList, summary, options) {
        var errors = [], warnings = [], ignored = [];

        issueList = this.filterFieldMaps(issueList, summary);

        // sort alphabetically by relative path of files
        issueList.sort(function (a,b) {
            var aPath = a.file ? a.file.relativePath : '';
            var bPath = b.file ? b.file.relativePath : '';
            return (aPath > bPath) ? 1 : ((bPath > aPath) ? -1 : 0);
        });

        // organize by issue code
        var categorized = {};
        var codes = []
        for (var i = 0; i < issueList.length; i++) {
            var issue = issueList[i];

            if (issue.file && config.ignoredFile(options.config, issue.file.relativePath)) {
                continue;
            }

            if (!categorized[issue.code]) {
                codes.push(issue.key);
                codes.push(issue.code);
                categorized[issue.code] = list[issue.code];
                categorized[issue.code].files = [];
                categorized[issue.code].additionalFileCount = 0;
            }
            if (options.verbose || (categorized[issue.code].files.length < 10)) {
                categorized[issue.code].files.push(issue);
            } else {
                categorized[issue.code].additionalFileCount++;
            }
        }

        var severityMap = config.interpret(codes, options.config);

        // organize by severity
        for (var key in categorized) {
            issue = categorized[key];
            issue.code = key;

            if (severityMap.hasOwnProperty(issue.code)) {
                issue.severity = severityMap[issue.code];
            }

            if (severityMap.hasOwnProperty(issue.key)) {
                issue.severity = severityMap[issue.key];
            }

            if (issue.severity === 'error') {
                errors.push(issue);
            } else if (issue.severity === 'warning' && !options.ignoreWarnings) {
                warnings.push(issue);
            } else if (issue.severity === 'ignore') {
                ignored.push(issue);
            }

        }

        return {errors: errors, warnings: warnings, ignored: ignored};
    },

    /**
     * Reformat
     *
     * Takes an already formatted set of issues, a
     * summary and a config object and returns the
     * same issues reformatted against the config.
     */
    reformat: function (issueList, summary, config) {
        var errors   = issueList.errors   ? issueList.errors   : [],
            warnings = issueList.warnings ? issueList.warnings : [],
            ignored  = issueList.ignored  ? issueList.ignored  : [];

        issueList = errors.concat(warnings).concat(ignored);
        var unformatted = [];
        for (var i = 0; i < issueList.length; i++) {
            var issue = issueList[i];
            for (var j = 0; j < issue.files.length; j++) {
                var file = issue.files[j];
                unformatted.push(file);
            }
        }
        return issues.format(unformatted, summary, {config: config});
    }
};

module.exports = issues;<commit_msg>reduce cognitive complexity<commit_after>var list      = require('./list');
var Issue     = require('./issue');
var config    = require('../config');

var issues = {

	/**
	 * List
	 *
	 * List of all validator issues.
	 */
	list: list,

	/**
	 * Issue
	 *
	 * Issue constructor
	 */
	Issue: Issue,

    /**
     * Filter Fieldmaps
     *
     * Remove fieldmap related warnings if no fieldmaps
     * are present.
     */
    filterFieldMaps: function (issueList) {
        var filteredIssueList = [];
        var fieldmapRelatedCodes = [6, 7, 8, 9];
        for (var i = 0; i < issueList.length; i++) {
            var issue = issueList[i];
            if (fieldmapRelatedCodes.indexOf(issue.code) < 0) {
                filteredIssueList.push(issue);
            }
        }
        return filteredIssueList
    },

	/**
     * Format Issues
     */
    format: function (issueList, summary, options) {
        var errors = [], warnings = [], ignored = [];

        if (summary.modalities.indexOf("fieldmap") < 0) {
            issueList = this.filterFieldMaps(issueList);
        }

        // sort alphabetically by relative path of files
        issueList.sort(function (a,b) {
            var aPath = a.file ? a.file.relativePath : '';
            var bPath = b.file ? b.file.relativePath : '';
            return (aPath > bPath) ? 1 : ((bPath > aPath) ? -1 : 0);
        });

        // organize by issue code
        var categorized = {};
        var codes = []
        for (var i = 0; i < issueList.length; i++) {
            var issue = issueList[i];

            if (issue.file && config.ignoredFile(options.config, issue.file.relativePath)) {
                continue;
            }

            if (!categorized[issue.code]) {
                codes.push(issue.key);
                codes.push(issue.code);
                categorized[issue.code] = list[issue.code];
                categorized[issue.code].files = [];
                categorized[issue.code].additionalFileCount = 0;
            }
            if (options.verbose || (categorized[issue.code].files.length < 10)) {
                categorized[issue.code].files.push(issue);
            } else {
                categorized[issue.code].additionalFileCount++;
            }
        }

        var severityMap = config.interpret(codes, options.config);

        // organize by severity
        for (var key in categorized) {
            issue = categorized[key];
            issue.code = key;

            if (severityMap.hasOwnProperty(issue.code)) {
                issue.severity = severityMap[issue.code];
            }

            if (severityMap.hasOwnProperty(issue.key)) {
                issue.severity = severityMap[issue.key];
            }

            if (issue.severity === 'error') {
                errors.push(issue);
            } else if (issue.severity === 'warning' && !options.ignoreWarnings) {
                warnings.push(issue);
            } else if (issue.severity === 'ignore') {
                ignored.push(issue);
            }

        }

        return {errors: errors, warnings: warnings, ignored: ignored};
    },

    /**
     * Reformat
     *
     * Takes an already formatted set of issues, a
     * summary and a config object and returns the
     * same issues reformatted against the config.
     */
    reformat: function (issueList, summary, config) {
        var errors   = issueList.errors   ? issueList.errors   : [],
            warnings = issueList.warnings ? issueList.warnings : [],
            ignored  = issueList.ignored  ? issueList.ignored  : [];

        issueList = errors.concat(warnings).concat(ignored);
        var unformatted = [];
        for (var i = 0; i < issueList.length; i++) {
            var issue = issueList[i];
            for (var j = 0; j < issue.files.length; j++) {
                var file = issue.files[j];
                unformatted.push(file);
            }
        }
        return issues.format(unformatted, summary, {config: config});
    }
};

module.exports = issues;<|endoftext|>


<commit_before>/*eslint-env node*/
var CoreObject = require('core-object');
var fs         = require('fs');
var path       = require('path');
var mime       = require('mime');
var RSVP       = require('rsvp');
var _          = require('lodash');

module.exports = CoreObject.extend({
  init: function(options) {
    this._super(options);
    var AWS = require('aws-sdk');
    var s3Options = {
      region: this.plugin.readConfig('region')
    };

    const proxy = this.plugin.readConfig('proxy');
    if (proxy) {
      this._proxyAgent = this.plugin.readConfig('proxyAgent') || require('proxy-agent');
      s3Options.httpOptions = {
        agent: this._proxyAgent(proxy)
      };
    }

    const accessKeyId = this.plugin.readConfig('accessKeyId');
    const secretAccessKey = this.plugin.readConfig('secretAccessKey');
    const sessionToken = this.plugin.readConfig('sessionToken');
    const profile = this.plugin.readConfig('profile');
    const signatureVersion = this.plugin.readConfig('signatureVersion');

    if (accessKeyId && secretAccessKey) {
      this.plugin.log('Using AWS access key id and secret access key from config', { verbose: true });
      s3Options.accessKeyId = accessKeyId;
      s3Options.secretAccessKey = secretAccessKey;
    }

    if (signatureVersion) {
      this.plugin.log('Using signature version from config', { verbose: true });
      s3Options.signatureVersion = signatureVersion;
    }

    if (sessionToken) {
      this.plugin.log('Using AWS session token from config', { verbose: true });
      s3Options.sessionToken = sessionToken;
    }

    if (profile && !this.plugin.readConfig('s3Client')) {
      this.plugin.log('Using AWS profile from config', { verbose: true });
      AWS.config.credentials = new AWS.SharedIniFileCredentials({ profile: profile });
    }

    this._client = this.plugin.readConfig('s3Client') || new AWS.S3(s3Options);
  },

  upload: function(options) {
    options = options || {};
    return this._determineFilePaths(options).then(function(filePaths) {
      const allFilesUploaded = this._putObjects(filePaths, options);

      const manifestPath = options.manifestPath;
      if (manifestPath) {
        return allFilesUploaded.then(function(filesUploaded) {
          return this._putObject(manifestPath, options).then(function(manifestUploaded) {
            return filesUploaded.concat(manifestUploaded);
          });
        }.bind(this));
      } else {
        return allFilesUploaded;
      }
    }.bind(this));
  },

  _determineFilePaths: function(options) {
    var plugin = this.plugin;
    var filePaths = options.filePaths || [];
    if (typeof filePaths === 'string') {
      filePaths = [filePaths];
    }
    var prefix       = options.prefix;
    var manifestPath = options.manifestPath;
    if (manifestPath) {
      var key = prefix === '' ? manifestPath : [prefix, manifestPath].join('/');
      plugin.log('Downloading manifest for differential deploy from `' + key + '`...', { verbose: true });
      return new RSVP.Promise(function(resolve, reject){
        var params = { Bucket: options.bucket, Key: key};
        this._client.getObject(params, function(error, data) {
          if (error) {
            reject(error);
          } else {
            resolve(data.Body.toString().split('\n'));
          }
        }.bind(this));
      }.bind(this)).then(function(manifestEntries){
        plugin.log("Manifest found. Differential deploy will be applied.", { verbose: true });
        return _.difference(filePaths, manifestEntries);
      }).catch(function(/* reason */){
        plugin.log("Manifest not found. Disabling differential deploy.", { color: 'yellow', verbose: true });
        return RSVP.resolve(filePaths);
      });
    } else {
      return RSVP.resolve(filePaths);
    }
  },

  _putObject: function(filePath, options, filePaths) {
    var plugin                    = this.plugin;
    var cwd                       = options.cwd;
    var bucket                    = options.bucket;
    var prefix                    = options.prefix;
    var acl                       = options.acl;
    var gzippedFilePaths          = options.gzippedFilePaths || [];
    var brotliCompressedFilePaths = options.brotliCompressedFilePaths || [];
    var cacheControl              = options.cacheControl;
    var expires                   = options.expires;
    var serverSideEncryption      = options.serverSideEncryption;

    mime.default_type = options.defaultMimeType || mime.lookup('bin');

    var basePath    = path.join(cwd, filePath);
    var data        = fs.readFileSync(basePath);
    var contentType = mime.lookup(basePath);
    var encoding    = mime.charsets.lookup(contentType);
    var key         = prefix === '' ? filePath : [prefix, filePath].join('/');
    var isGzipped   = gzippedFilePaths.indexOf(filePath) !== -1;
    var isBrotliCompressed = brotliCompressedFilePaths.indexOf(filePath) !== -1;

    if (isGzipped && path.extname(basePath) === '.gz') {
      var basePathUngzipped = path.basename(basePath, '.gz');
      if (filePaths && filePaths.indexOf(basePathUngzipped) !== -1) {
        contentType = mime.lookup(basePathUngzipped);
        encoding = mime.charsets.lookup(contentType);
      }
    }
    if (isBrotliCompressed) {
      var basePathUncompressed = path.basename(basePath, '.br');
      if (filePaths && filePaths.indexOf(basePathUncompressed) !== -1) {
        contentType = mime.lookup(basePathUncompressed);
        encoding = mime.charsets.lookup(contentType);
      }
    }

    if (encoding) {
      contentType += '; charset=';
      contentType += encoding.toLowerCase();
    }

    var params = {
      Bucket: bucket,
      ACL: acl,
      Body: data,
      ContentType: contentType,
      Key: key,
      CacheControl: cacheControl,
      Expires: expires
    };

    if (serverSideEncryption) {
      params.ServerSideEncryption = serverSideEncryption;
    }

    if (isGzipped) {
      params.ContentEncoding = 'gzip';
    }
    if (isBrotliCompressed) {
      params.ContentEncoding = 'br';
    }

    return new RSVP.Promise(function(resolve, reject) {
      this._client.putObject(params, function(error) {
        if (error) {
          reject(error);
        } else {
          plugin.log('✔  ' + key, { verbose: true });
          resolve(filePath);
        }
      });
    }.bind(this));
  },

  _currentEnd: 0,
  _putObjectsBatch: function(filePaths, options) {
    var currentBatch = filePaths.slice(this._currentEnd, Math.min(this._currentEnd + options.batchSize, filePaths.length));

    this._currentEnd += currentBatch.length;

    //Execute our current batch of promises
    return RSVP.all(currentBatch.map(function (filePath) {
      return this._putObject(filePath, options, filePaths);
    }.bind(this)))
    //Then check if we need to execute another batch
    .then(function () {
      if (this._currentEnd < filePaths.length) {
        return this._putObjectsBatch(filePaths, options);
      }

      return filePaths;
    }.bind(this));
  },

  _putObjects: function (filePaths, options) {
    if (options.batchSize > 0) {
      this._currentEnd = 0;
      return this._putObjectsBatch(filePaths, options);
    }

    return RSVP.all(filePaths.map(function (filePath) {
      return this._putObject(filePath, options, filePaths);
    }.bind(this)));
  }
});
<commit_msg>replace lookup with getType to address mime deprecation #105<commit_after>/*eslint-env node*/
var CoreObject = require('core-object');
var fs         = require('fs');
var path       = require('path');
var mime       = require('mime');
var RSVP       = require('rsvp');
var _          = require('lodash');

module.exports = CoreObject.extend({
  init: function(options) {
    this._super(options);
    var AWS = require('aws-sdk');
    var s3Options = {
      region: this.plugin.readConfig('region')
    };

    const proxy = this.plugin.readConfig('proxy');
    if (proxy) {
      this._proxyAgent = this.plugin.readConfig('proxyAgent') || require('proxy-agent');
      s3Options.httpOptions = {
        agent: this._proxyAgent(proxy)
      };
    }

    const accessKeyId = this.plugin.readConfig('accessKeyId');
    const secretAccessKey = this.plugin.readConfig('secretAccessKey');
    const sessionToken = this.plugin.readConfig('sessionToken');
    const profile = this.plugin.readConfig('profile');
    const signatureVersion = this.plugin.readConfig('signatureVersion');

    if (accessKeyId && secretAccessKey) {
      this.plugin.log('Using AWS access key id and secret access key from config', { verbose: true });
      s3Options.accessKeyId = accessKeyId;
      s3Options.secretAccessKey = secretAccessKey;
    }

    if (signatureVersion) {
      this.plugin.log('Using signature version from config', { verbose: true });
      s3Options.signatureVersion = signatureVersion;
    }

    if (sessionToken) {
      this.plugin.log('Using AWS session token from config', { verbose: true });
      s3Options.sessionToken = sessionToken;
    }

    if (profile && !this.plugin.readConfig('s3Client')) {
      this.plugin.log('Using AWS profile from config', { verbose: true });
      AWS.config.credentials = new AWS.SharedIniFileCredentials({ profile: profile });
    }

    this._client = this.plugin.readConfig('s3Client') || new AWS.S3(s3Options);
  },

  upload: function(options) {
    options = options || {};
    return this._determineFilePaths(options).then(function(filePaths) {
      const allFilesUploaded = this._putObjects(filePaths, options);

      const manifestPath = options.manifestPath;
      if (manifestPath) {
        return allFilesUploaded.then(function(filesUploaded) {
          return this._putObject(manifestPath, options).then(function(manifestUploaded) {
            return filesUploaded.concat(manifestUploaded);
          });
        }.bind(this));
      } else {
        return allFilesUploaded;
      }
    }.bind(this));
  },

  _determineFilePaths: function(options) {
    var plugin = this.plugin;
    var filePaths = options.filePaths || [];
    if (typeof filePaths === 'string') {
      filePaths = [filePaths];
    }
    var prefix       = options.prefix;
    var manifestPath = options.manifestPath;
    if (manifestPath) {
      var key = prefix === '' ? manifestPath : [prefix, manifestPath].join('/');
      plugin.log('Downloading manifest for differential deploy from `' + key + '`...', { verbose: true });
      return new RSVP.Promise(function(resolve, reject){
        var params = { Bucket: options.bucket, Key: key};
        this._client.getObject(params, function(error, data) {
          if (error) {
            reject(error);
          } else {
            resolve(data.Body.toString().split('\n'));
          }
        }.bind(this));
      }.bind(this)).then(function(manifestEntries){
        plugin.log("Manifest found. Differential deploy will be applied.", { verbose: true });
        return _.difference(filePaths, manifestEntries);
      }).catch(function(/* reason */){
        plugin.log("Manifest not found. Disabling differential deploy.", { color: 'yellow', verbose: true });
        return RSVP.resolve(filePaths);
      });
    } else {
      return RSVP.resolve(filePaths);
    }
  },

  _putObject: function(filePath, options, filePaths) {
    var plugin                    = this.plugin;
    var cwd                       = options.cwd;
    var bucket                    = options.bucket;
    var prefix                    = options.prefix;
    var acl                       = options.acl;
    var gzippedFilePaths          = options.gzippedFilePaths || [];
    var brotliCompressedFilePaths = options.brotliCompressedFilePaths || [];
    var cacheControl              = options.cacheControl;
    var expires                   = options.expires;
    var serverSideEncryption      = options.serverSideEncryption;

    mime.default_type = options.defaultMimeType || mime.getType('bin');

    var basePath    = path.join(cwd, filePath);
    var data        = fs.readFileSync(basePath);
    var contentType = mime.getType(basePath);
    var encoding    = mime.charsets.lookup(contentType);
    var key         = prefix === '' ? filePath : [prefix, filePath].join('/');
    var isGzipped   = gzippedFilePaths.indexOf(filePath) !== -1;
    var isBrotliCompressed = brotliCompressedFilePaths.indexOf(filePath) !== -1;

    if (isGzipped && path.extname(basePath) === '.gz') {
      var basePathUngzipped = path.basename(basePath, '.gz');
      if (filePaths && filePaths.indexOf(basePathUngzipped) !== -1) {
        contentType = mime.getType(basePathUngzipped);
        encoding = mime.charsets.lookup(contentType);
      }
    }
    if (isBrotliCompressed) {
      var basePathUncompressed = path.basename(basePath, '.br');
      if (filePaths && filePaths.indexOf(basePathUncompressed) !== -1) {
        contentType = mime.getType(basePathUncompressed);
        encoding = mime.charsets.lookup(contentType);
      }
    }

    if (encoding) {
      contentType += '; charset=';
      contentType += encoding.toLowerCase();
    }

    var params = {
      Bucket: bucket,
      ACL: acl,
      Body: data,
      ContentType: contentType,
      Key: key,
      CacheControl: cacheControl,
      Expires: expires
    };

    if (serverSideEncryption) {
      params.ServerSideEncryption = serverSideEncryption;
    }

    if (isGzipped) {
      params.ContentEncoding = 'gzip';
    }
    if (isBrotliCompressed) {
      params.ContentEncoding = 'br';
    }

    return new RSVP.Promise(function(resolve, reject) {
      this._client.putObject(params, function(error) {
        if (error) {
          reject(error);
        } else {
          plugin.log('✔  ' + key, { verbose: true });
          resolve(filePath);
        }
      });
    }.bind(this));
  },

  _currentEnd: 0,
  _putObjectsBatch: function(filePaths, options) {
    var currentBatch = filePaths.slice(this._currentEnd, Math.min(this._currentEnd + options.batchSize, filePaths.length));

    this._currentEnd += currentBatch.length;

    //Execute our current batch of promises
    return RSVP.all(currentBatch.map(function (filePath) {
      return this._putObject(filePath, options, filePaths);
    }.bind(this)))
    //Then check if we need to execute another batch
    .then(function () {
      if (this._currentEnd < filePaths.length) {
        return this._putObjectsBatch(filePaths, options);
      }

      return filePaths;
    }.bind(this));
  },

  _putObjects: function (filePaths, options) {
    if (options.batchSize > 0) {
      this._currentEnd = 0;
      return this._putObjectsBatch(filePaths, options);
    }

    return RSVP.all(filePaths.map(function (filePath) {
      return this._putObject(filePath, options, filePaths);
    }.bind(this)));
  }
});
<|endoftext|>


<commit_before>exports.productionVersionId = "c248579bbdf2";<commit_msg>Incrementing Production Version.<commit_after>exports.productionVersionId = "f21fec135681";<|endoftext|>


<commit_before>/* ==========================================================
 * bootstrap-maxlength.js v1.3.7
 *
 * Copyright (c) 2013 Maurizio Napoleoni;
 *
 * Licensed under the terms of the MIT license.
 * See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE
 * ========================================================== */
(function(h){h.fn.extend({maxlength:function(d,l){function m(b){var c=b.val().match(/\n/g),c=c?c.length:0;return b.val().length+c}function k(b,c){return c-m(b)}function n(b,c){var a="";d.preText&&(a+=d.preText);a=a+c+d.separator+b;d.postText&&(a+=d.postText);return a}function p(b,c,a,e){e.html(n(a,b));if(0b&&(f=!1);f?e.removeClass(d.limitReachedClass).addClass(d.warningClass).css({display:"block"}):e.css({display:"none"})}else e.removeClass(d.warningClass).addClass(d.limitReachedClass).css({display:"block"})}
function q(b,c){var a;a=b[0];a=h.extend({},"function"===typeof a.getBoundingClientRect?a.getBoundingClientRect():{width:a.offsetWidth,height:a.offsetHeight},b.offset());var e=b.outerWidth(),f=c.outerWidth(),g=c.width(),j=c.height();switch(d.placement){case "bottom":c.css({top:a.top+a.height,left:a.left+a.width/2-g/2});break;case "top":c.css({top:a.top-j,left:a.left+a.width/2-g/2});break;case "left":c.css({top:a.top+a.height/2-j/2,left:a.left-g});break;case "right":c.css({top:a.top+a.height/2-j/2,
left:a.left+a.width});break;case "bottom-right":c.css({top:a.top+a.height,left:a.left+a.width});break;case "top-right":c.css({top:a.top-j,left:a.left+e});break;case "top-left":c.css({top:a.top-j,left:a.left-f});break;case "bottom-left":c.css({top:a.top+b.outerHeight(),left:a.left-f});break;case "centered-right":c.css({top:a.top+j/2,left:a.left+e-f-3})}}function g(b){return b.attr("maxlength")||b.attr("size")}var r=h("body");h.isFunction(d)&&!l&&(l=d,d={});d=h.extend({alwaysShow:!1,threshold:10,warningClass:"badge badge-success",
limitReachedClass:"badge badge-important",separator:" / ",preText:"",postText:"",placement:"bottom",validate:!1},d);return this.each(function(){var b=h(this),c=g(b),a=h("").css({display:"none",position:"absolute",whiteSpace:"nowrap",zIndex:999}).html(n(c,"0"));b.is("textarea")&&(b.data("maxlenghtsizex",b.outerWidth()),b.data("maxlenghtsizey",b.outerHeight()),b.mouseup(function(){(b.outerWidth()!==b.data("maxlenghtsizex")||b.outerHeight()!==b.data("maxlenghtsizey"))&&q(b,a);b.data("maxlenghtsizex",
b.outerWidth());b.data("maxlenghtsizey",b.outerHeight())}));r.append(a);b.focus(function(){var d=k(b,g(b));p(d,b,c,a);q(b,a)});b.blur(function(){a.css("display","none")});b.keyup(function(){var e=k(b,g(b)),f=!0;d.validate&&0>e?f=!1:p(e,b,c,a);return f});b.keydown(function(a){if(0>=k(b,g(b))&&46!==a.keyCode&&8!==a.keyCode)return!1})})}})})(jQuery);<commit_msg>minified with new showMaxLength<commit_after>/* ==========================================================
 * bootstrap-maxlength.js v1.3.7
 *
 * Copyright (c) 2013 Maurizio Napoleoni;
 *
 * Licensed under the terms of the MIT license.
 * See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE
 * ========================================================== */
(function(){(function(e){"use strict";e.fn.extend({maxlength:function(t,n){function s(e){var t=e.val(),n=t.match(/\n/g),r=n?n.length:0;return e.val().length+r}function o(e,n,r){var i=!0;return!t.alwaysShow&&r-s(e)>n&&(i=!1),i}function u(e,t){var n=t-s(e);return n}function a(e){e.css({display:"block"})}function f(e){e.css({display:"none"})}function l(e,n){var r="";return t.preText&&(r+=t.preText),t.showMaxLength?r=r+n+t.separator+e:r+=n,t.postText&&(r+=t.postText),r}function c(e,n,r,i){i.html(l(r,e)),e>0?o(n,t.threshold,r)?a(i.removeClass(t.limitReachedClass).addClass(t.warningClass)):f(i):a(i.removeClass(t.warningClass).addClass(t.limitReachedClass))}function h(t){var n=t[0];return e.extend({},typeof n.getBoundingClientRect=="function"?n.getBoundingClientRect():{width:n.offsetWidth,height:n.offsetHeight},t.offset())}function p(e,n){var r=h(e),i=e.outerWidth(),s=n.outerWidth(),o=n.width(),u=n.height();switch(t.placement){case"bottom":n.css({top:r.top+r.height,left:r.left+r.width/2-o/2});break;case"top":n.css({top:r.top-u,left:r.left+r.width/2-o/2});break;case"left":n.css({top:r.top+r.height/2-u/2,left:r.left-o});break;case"right":n.css({top:r.top+r.height/2-u/2,left:r.left+r.width});break;case"bottom-right":n.css({top:r.top+r.height,left:r.left+r.width});break;case"top-right":n.css({top:r.top-u,left:r.left+i});break;case"top-left":n.css({top:r.top-u,left:r.left-s});break;case"bottom-left":n.css({top:r.top+e.outerHeight(),left:r.left-s});break;case"centered-right":n.css({top:r.top+u/2,left:r.left+i-s-3})}}function d(e){return e.attr("maxlength")||e.attr("size")}var r=e("body"),i={alwaysShow:!1,threshold:10,warningClass:"badge badge-success",limitReachedClass:"badge badge-important",separator:" / ",preText:"",postText:"",showMaxLength:!0,placement:"bottom",validate:!1};return e.isFunction(t)&&!n&&(n=t,t={}),t=e.extend(i,t),this.each(function(){var n=e(this),i=d(n),s=e("").css({display:"none",position:"absolute",whiteSpace:"nowrap",zIndex:999}).html(l(i,"0"));n.is("textarea")&&(n.data("maxlenghtsizex",n.outerWidth()),n.data("maxlenghtsizey",n.outerHeight()),n.mouseup(function(){(n.outerWidth()!==n.data("maxlenghtsizex")||n.outerHeight()!==n.data("maxlenghtsizey"))&&p(n,s),n.data("maxlenghtsizex",n.outerWidth()),n.data("maxlenghtsizey",n.outerHeight())})),r.append(s),n.focus(function(){var e=u(n,d(n));c(e,n,i,s),p(n,s)}),n.blur(function(){s.css("display","none")}),n.keyup(function(){var e=u(n,d(n)),r=!0;return t.validate&&e<0?r=!1:c(e,n,i,s),r}),n.keydown(function(e){var t=u(n,d(n));if(t<=0&&e.keyCode!==46&&e.keyCode!==8)return!1})})}})})(jQuery)}).call(this);
<|endoftext|>


<commit_before>/**
 * MinLength
 */
Validation.prototype.addAttributeConstructor('minLength', function minLengthConstructor() {

  return function minLength(property, propertyValue, attributeValue, propertyAttributes, callback) {
    if (isString(propertyValue) && propertyValue.length >= attributeValue) {
      return callback();
    } else {
      return callback(true);
    }
  };

});<commit_msg>Change the way the ‘minLength’ attribute reports errors<commit_after>/**
 * MinLength
 */
Validation.prototype.addAttributeConstructor('minLength', function minLengthConstructor() {
  return function minLength(property, propertyValue, attributeValue, propertyAttributes, callback) {

    if (isString(propertyValue) && propertyValue.length < attributeValue) {
      this.addError();
    }

    return callback();

  };
});<|endoftext|>


<commit_before>var _ = require('lodash');
var React = require('react/addons');
var invariant = require('react/lib/invariant');
var cloneWithProps = require('react/lib/cloneWithProps');
var update = require('react/lib/update');
var RSVP = require('rsvp');

var applyContext = require('./utils/applyContext');

function isPromise(value) {
  return value && value.then;
}

var Form = React.createClass({
  displayName: 'Form',
  mixins: [React.addons.PureRenderMixin],
  propTypes: {
    validators: React.PropTypes.object,
    onSubmit: React.PropTypes.func,
    onSuccess: React.PropTypes.func,
    onErrors: React.PropTypes.func
  },

  componentWillMount: function() {
    this._debouncedFuncs = {};
    this._loadingValidators = {};
    this._fieldCount = 0;
    this._formState = {
      submitting: false,
      submitted: false,
      submitError: null
    };

    this._formElements = [];
    this._formFieldElements = {};
    this._fieldStates = {};

    this.setAvailableValidators(this.props);
  },

  componentWillReceiveProps: function(nextProps) {
    this.setAvailableValidators(nextProps);
  },

  getDefaultProps: function() {
    return {
      defaultValues: {}
    };
  },

  getContext: function() {
    return {
      // Element methods
      registerElement: this.registerElement,
      unregisterElement: this.unregisterElement,

      // Field methods
      registerField: this.registerField,
      unregisterField: this.unregisterField,
      changeFieldValue: this.changeFieldValue,

      // Form methods
      submit: this.submit,

      // getState methods
      getFormState: this.getFormState,
      getFieldState: this.getFieldState
    };
  },

  getFormState: function() {
    return {
      submitting: this._formState.submitting,
      submitted: this._formState.submitted,
      submitError: this._formState.submitError,
      validatorNames: this.validatorNames
    };
  },

  setAvailableValidators: function(props) {
    this.validatorNames = _.keys(props.validators);
  },

  // Element methods

  registerElement: function(element) {
    this._formElements.push(element);
  },

  unregisterElement: function(element) {
    this._formElements = _.without(this._formElements, element);
  },

  // Field methods

  registerField: function(element, fieldName, validators) {
    this._formFieldElements[fieldName] = element;
    var value = this.props.defaultValues[fieldName] || null;
    var initialFieldState = {
      value: value,
      error: null,
      state: 'loading', // Set to loading for initial validation.
      isPristine: true,
      isFirst: this._fieldCount++ === 0
    };

    this._fieldStates[fieldName] = initialFieldState;

    // Initially validate the newly-registered field.
    this._updateFieldValidationState(fieldName, value, validators);
  },

  unregisterField: function(fieldName) {
    delete this._formFieldElements[fieldName];
    delete this._fieldStates[fieldName];
  },

  changeFieldValue: function(fieldName, value, params) {
    var _this = this;
    params = params || {};

    // Set field loading state.
    _this._updateFieldState(fieldName, {
      value: value,
      state: 'loading',
      error: null,
      isPristine: false
    });

    function change() {
      // TODO: Add a promise to this._loadingValidators to prevent a race
      // condition.
      _this._updateFieldValidationState(fieldName, value, params.validators);
    }

    if (params.debounce) {
      if (!this._debouncedFuncs[fieldName]) {
        this._debouncedFuncs[fieldName] = _.debounce(function(func) {
          func();
        }, params.debounce);
      }
      this._debouncedFuncs[fieldName](change);
    } else {
      change();
    }
  },

  _updateFieldValidationState: function(fieldName, value, validators) {
    var self = this;

    // Keep track of loading validators.
    var promise = this.validateField(fieldName, value, validators)
      .then(function(message) {
        var state;
        if (message) {
          state = {state: 'invalid', error: message};
        } else {
          state = {state: 'valid', error: null};
        }

        self._updateFieldState(fieldName, state);
      })
      .catch(function(err) {
        self._updateFieldState({state: 'invalid', error: err});
      })
      .finally(function() {
        delete self._loadingValidators[fieldName];
      });

    this._loadingValidators[fieldName] = promise;
  },

  _updateFieldState: function(fieldName, state) {
    _.extend(this._fieldStates[fieldName], state);
    // Re-render the changed element.
    this._formFieldElements[fieldName].forceUpdate();
  },

  _updateFormState: function(state) {
    _.extend(this._formState, state);
    _.each(this._formElements, function(element) {
      element.forceUpdate();
    });
  },

  getFieldState: function(fieldName) {
    return this._fieldStates[fieldName];
  },

  setFieldValue: function(fieldName, value) {
    this._updateFieldState(fieldName, {
      value: value,
      state: 'valid',
      error: null
    });
  },

  // Form methods

  submit: function() {
    var self = this;

    if (this.props.onSubmit) {
      this.props.onSubmit();
    }

    // Change form state for all form elements.
    this._updateFormState({
      submitting: true,
      submitted: true,
      submitError: null
    });

    // Asynchronously wait for all loading validations before using errors.
    RSVP.hash(this._loadingValidators || {})
      .then(function() {
        var errors = _.mapValues(self._fieldStates, 'error');
        var filteredErrors = _.pick(errors, _.identity);
        if (!_.isEmpty(filteredErrors)) {
          return self.handleErrors(filteredErrors);
        }

        return self.handleSuccess(_.mapValues(self._fieldStates, 'value'));
      })
      .catch(function(err) {
        console.error((err && err.stack) || err);
      });
  },

  validateField: function(name, value, validators) {
    var self = this;

    // Only check for errors if validators are defined for the field.
    if (!validators) {
      return RSVP.resolve(null);
    }

    // Combine into one resolved error string or null.
    var result = RSVP.resolve(null);

    // TODO: Even though it is supported my most major browsers, make the
    // validator order explicit instead of depending on the object's key order.
    _.each(validators, function(options, validatorName) {
      result = result.then(function() {
        // Validator will return a validation result value (error or null) or
        // a promise that will be fulfilled with a validation result value.
        var result = self.props.validators[validatorName](value, options);
        if (isPromise(result)) {
          // Reject on error
          result.then(function(value) {
            if (value) {
              return RSVP.reject(value);
            }
            return null;
          });
        } else if (result) {
          // Handle static value
          result = RSVP.reject(result);
        } else {
          // Handle static value
          result = RSVP.resolve(null);
        }

        return result;
      });
    });

    return result.catch(function(err) {
      return err;
    });
  },

  handleErrors: function(errors) {
    if (this.props.onErrors) {
      this.props.onErrors(errors);
    }
    this._updateFormState({submitting: false});
  },

  handleSuccess: function(data) {
    var self = this;
    if (this.props.onSuccess) {
      var promise = this.props.onSuccess(data);

      if (isPromise(promise)) {
        return promise.then(function() {
          self._updateFormState({submitting: false});
        }).catch(function(err) {
          self._updateFormState({submitError: err, submitting: false});
        });
      }
    }

    self._updateFormState({submitting: false});
  },

  handleKeyDown: function(e) {
    if (e.key === 'Enter') {
      this.submit();
    }
  },

  render: function() {
    // Add form data to all field props.
    applyContext(this.props.children, {
      _formContext: this.getContext()
    }, []);

    return React.DOM.div({
      className: 'Form',
      key: this.props.key,
      onKeyDown: this.handleKeyDown
    }, this.props.children);
  }
});

module.exports = Form;
<commit_msg>Ignore validators if the field is empty and required is not set<commit_after>var _ = require('lodash');
var React = require('react/addons');
var invariant = require('react/lib/invariant');
var cloneWithProps = require('react/lib/cloneWithProps');
var update = require('react/lib/update');
var RSVP = require('rsvp');

var applyContext = require('./utils/applyContext');

function isPromise(value) {
  return value && value.then;
}

var Form = React.createClass({
  displayName: 'Form',
  mixins: [React.addons.PureRenderMixin],
  propTypes: {
    validators: React.PropTypes.object,
    onSubmit: React.PropTypes.func,
    onSuccess: React.PropTypes.func,
    onErrors: React.PropTypes.func
  },

  componentWillMount: function() {
    this._debouncedFuncs = {};
    this._loadingValidators = {};
    this._fieldCount = 0;
    this._formState = {
      submitting: false,
      submitted: false,
      submitError: null
    };

    this._formElements = [];
    this._formFieldElements = {};
    this._fieldStates = {};

    this.setAvailableValidators(this.props);
  },

  componentWillReceiveProps: function(nextProps) {
    this.setAvailableValidators(nextProps);
  },

  getDefaultProps: function() {
    return {
      defaultValues: {}
    };
  },

  getContext: function() {
    return {
      // Element methods
      registerElement: this.registerElement,
      unregisterElement: this.unregisterElement,

      // Field methods
      registerField: this.registerField,
      unregisterField: this.unregisterField,
      changeFieldValue: this.changeFieldValue,

      // Form methods
      submit: this.submit,

      // getState methods
      getFormState: this.getFormState,
      getFieldState: this.getFieldState
    };
  },

  getFormState: function() {
    return {
      submitting: this._formState.submitting,
      submitted: this._formState.submitted,
      submitError: this._formState.submitError,
      validatorNames: this.validatorNames
    };
  },

  setAvailableValidators: function(props) {
    this.validatorNames = _.keys(props.validators);
  },

  // Element methods

  registerElement: function(element) {
    this._formElements.push(element);
  },

  unregisterElement: function(element) {
    this._formElements = _.without(this._formElements, element);
  },

  // Field methods

  registerField: function(element, fieldName, validators) {
    this._formFieldElements[fieldName] = element;
    var value = this.props.defaultValues[fieldName] || null;
    var initialFieldState = {
      value: value,
      error: null,
      state: 'loading', // Set to loading for initial validation.
      isPristine: true,
      isFirst: this._fieldCount++ === 0
    };

    this._fieldStates[fieldName] = initialFieldState;

    // Initially validate the newly-registered field.
    this._updateFieldValidationState(fieldName, value, validators);
  },

  unregisterField: function(fieldName) {
    delete this._formFieldElements[fieldName];
    delete this._fieldStates[fieldName];
  },

  changeFieldValue: function(fieldName, value, params) {
    var _this = this;
    params = params || {};

    // Set field loading state.
    _this._updateFieldState(fieldName, {
      value: value,
      state: 'loading',
      error: null,
      isPristine: false
    });

    function change() {
      // TODO: Add a promise to this._loadingValidators to prevent a race
      // condition.
      _this._updateFieldValidationState(fieldName, value, params.validators);
    }

    if (params.debounce) {
      if (!this._debouncedFuncs[fieldName]) {
        this._debouncedFuncs[fieldName] = _.debounce(function(func) {
          func();
        }, params.debounce);
      }
      this._debouncedFuncs[fieldName](change);
    } else {
      change();
    }
  },

  _updateFieldValidationState: function(fieldName, value, validators) {
    var self = this;

    // Keep track of loading validators.
    var promise = this.validateField(fieldName, value, validators)
      .then(function(message) {
        var state;
        if (message) {
          state = {state: 'invalid', error: message};
        } else {
          state = {state: 'valid', error: null};
        }

        self._updateFieldState(fieldName, state);
      })
      .catch(function(err) {
        self._updateFieldState({state: 'invalid', error: err});
      })
      .finally(function() {
        delete self._loadingValidators[fieldName];
      });

    this._loadingValidators[fieldName] = promise;
  },

  _updateFieldState: function(fieldName, state) {
    _.extend(this._fieldStates[fieldName], state);
    // Re-render the changed element.
    this._formFieldElements[fieldName].forceUpdate();
  },

  _updateFormState: function(state) {
    _.extend(this._formState, state);
    _.each(this._formElements, function(element) {
      element.forceUpdate();
    });
  },

  getFieldState: function(fieldName) {
    return this._fieldStates[fieldName];
  },

  setFieldValue: function(fieldName, value) {
    this._updateFieldState(fieldName, {
      value: value,
      state: 'valid',
      error: null
    });
  },

  // Form methods

  submit: function() {
    var self = this;

    if (this.props.onSubmit) {
      this.props.onSubmit();
    }

    // Change form state for all form elements.
    this._updateFormState({
      submitting: true,
      submitted: true,
      submitError: null
    });

    // Asynchronously wait for all loading validations before using errors.
    RSVP.hash(this._loadingValidators || {})
      .then(function() {
        var errors = _.mapValues(self._fieldStates, 'error');
        var filteredErrors = _.pick(errors, _.identity);
        if (!_.isEmpty(filteredErrors)) {
          return self.handleErrors(filteredErrors);
        }

        return self.handleSuccess(_.mapValues(self._fieldStates, 'value'));
      })
      .catch(function(err) {
        console.error((err && err.stack) || err);
      });
  },

  validateField: function(name, value, validators) {
    var self = this;

    // Only check for errors if validators are defined for the field.
    if (!validators) {
      return RSVP.resolve(null);
    }

    // Ignore validators if the field is empty and required is not set.
    if (!validators.required && (!value || value === '')) {
      return RSVP.resolve(null);
    }

    // Combine into one resolved error string or null.
    var result = RSVP.resolve(null);

    // TODO: Even though it is supported my most major browsers, make the
    // validator order explicit instead of depending on the object's key order.
    _.each(validators, function(options, validatorName) {
      result = result.then(function() {
        // Validator will return a validation result value (error or null) or
        // a promise that will be fulfilled with a validation result value.
        var result = self.props.validators[validatorName](value, options);
        if (isPromise(result)) {
          // Reject on error
          result.then(function(value) {
            if (value) {
              return RSVP.reject(value);
            }
            return null;
          });
        } else if (result) {
          // Handle static value
          result = RSVP.reject(result);
        } else {
          // Handle static value
          result = RSVP.resolve(null);
        }

        return result;
      });
    });

    return result.catch(function(err) {
      return err;
    });
  },

  handleErrors: function(errors) {
    if (this.props.onErrors) {
      this.props.onErrors(errors);
    }
    this._updateFormState({submitting: false});
  },

  handleSuccess: function(data) {
    var self = this;
    if (this.props.onSuccess) {
      var promise = this.props.onSuccess(data);

      if (isPromise(promise)) {
        return promise.then(function() {
          self._updateFormState({submitting: false});
        }).catch(function(err) {
          self._updateFormState({submitError: err, submitting: false});
        });
      }
    }

    self._updateFormState({submitting: false});
  },

  handleKeyDown: function(e) {
    if (e.key === 'Enter') {
      this.submit();
    }
  },

  render: function() {
    // Add form data to all field props.
    applyContext(this.props.children, {
      _formContext: this.getContext()
    }, []);

    return React.DOM.div({
      className: 'Form',
      key: this.props.key,
      onKeyDown: this.handleKeyDown
    }, this.props.children);
  }
});

module.exports = Form;
<|endoftext|>


<commit_before>exports.localization = {
    en: {
        CHAT_WITH_US: 'Chat with us',
        NAME: 'Name',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Start conversation',
        NEW_MESSAGE: 'New message',
        NO_USERS_ONLINE: 'Currently there are no users online to help you. Leave a message and we will get back to you ASAP! Sorry!',
        CONVERSATION_HISTORY: 'Conversation history',
        JUST_WRITE: 'Just write...',
        PROBLEM_CONNECTING_TO_SERVER: 'We got a problem connecting to the server!',
        ERROR_FILL_ALL_FIELDS: 'Please fill the fields with valid name and email.',
        CONNECTING_TO_SERVER: 'Connecting to the server...',
        SERVER_OFFLINE: 'Server is now offline!',
        USER_IS_TYPING: 'User is typing...',
        UPLOADING_FILE: 'Uploading file',
        ERROR_UPLOADING_FILE: 'Error uploading the file!',
        ATTACH_FILE: 'Attach a file',
        USER_OFFLINE: 'Looks like internet is gone!',
        USER_ONLINE: 'We are back!',
        MUTE: 'Mute conversation',
        CLOSE: 'Close conversation',
        MINIMIZE: 'Minimize conversation',
        TITLE: 'Support',
        POWERED_BY: 'Powered by  Prud.io'
    },
    pt: {
        CHAT_WITH_US: 'Fale connosco',
        NAME: 'Nome',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Iniciar conversa',
        NEW_MESSAGE: 'Nova mensagem',
        NO_USERS_ONLINE: 'Desculpe! Neste momento não existem utilizadores online para ajuda-lo. Deixe uma mensagem e tentaremos entrar em contacto o mais rápido possivel!',
        CONVERSATION_HISTORY: 'Histórico da conversa',
        JUST_WRITE: 'Escreva aqui...',
        PROBLEM_CONNECTING_TO_SERVER: 'Ocorreu um problema ao efectuar a ligação ao servidor!',
        ERROR_FILL_ALL_FIELDS: 'Por favor preencha os campos com um nome e e-mail válido.',
        CONNECTING_TO_SERVER: 'A ligar ao servidor...',
        SERVER_OFFLINE: 'Servidor está desligado!',
        USER_IS_TYPING: 'A escrever...',
        UPLOADING_FILE: 'Anexando ficheiro',
        ERROR_UPLOADING_FILE: 'Erro ao anexar ficheiro!',
        ATTACH_FILE: 'Anexar um ficheiro',
        USER_OFFLINE: 'Parece que a internet foi abaixo!',
        USER_ONLINE: 'Estamos de volta!',
        MUTE: 'Silenciar conversa',
        CLOSE: 'Fechar conversa',
        MINIMIZE: 'Minimizar conversa',
        TITLE: 'Suporte',
        POWERED_BY: 'Desenvolvido por  Prud.io'
    },
    de: {
        CHAT_WITH_US: 'Chatten Sie mit uns',
        NAME: 'Name',
        EMAIL: 'E-Mail',
        START_CONVERSATION: 'Starten Sie einen Chat',
        NEW_MESSAGE: 'Neue Nachricht',
        NO_USERS_ONLINE: 'Zur Zeit ist niemand online um Ihnen zu helfen. Hinterlassen Sie eine Nachricht und wir melden uns sobald wie möglich bei Ihnen.',
        CONVERSATION_HISTORY: 'Nachrichtenverlauf',
        JUST_WRITE: 'Hier schreiben...',
        PROBLEM_CONNECTING_TO_SERVER: 'Die Verbindung zum Server ist fehlgeschlagen.',
        ERROR_FILL_ALL_FIELDS: 'Bitte füllen Sie die Felder mit einem Namen und einer gültigen E-Mail Adresse aus.',
        CONNECTING_TO_SERVER: 'Verbinde zum Server...',
        SERVER_OFFLINE: 'Server ist offline.',
        USER_IS_TYPING: 'Benutzer schreibt...',
        UPLOADING_FILE: 'Datei wird hochgeladen',
        ERROR_UPLOADING_FILE: 'Das Hochladen ist fehlgeschlagen',
        ATTACH_FILE: 'Datei hochladen',
        USER_OFFLINE: 'Keine Internetverbindung',
        USER_ONLINE: 'Internetverbindung wieder vorhanden',
        MUTE: 'Chat stummschalten',
        CLOSE: 'Chat schliessen',
        MINIMIZE: 'Chat minimieren',
        TITLE: 'Support',
        POWERED_BY: ' Prud.io'
    },
    es: {
        CHAT_WITH_US: 'Habla con nosotros',
        NAME: 'Nombre',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Iniciar una conversación',
        NEW_MESSAGE: 'Nuevo mensaje',
        NO_USERS_ONLINE: 'Perdon! Actualmente no hay miembros en línea para ayudarle. Deja un mensaje y trataremos de ponernos en contacto lo antes posible!',
        CONVERSATION_HISTORY: 'Historial de la conversación',
        JUST_WRITE: 'Escriba aqui...',
        PROBLEM_CONNECTING_TO_SERVER: 'Hubo un problema al conectar con el servidor!',
        ERROR_FILL_ALL_FIELDS: 'Por favor, rellene los campos con un nombre y una dirección de correo electrónico válida.',
        CONNECTING_TO_SERVER: 'Conectando con el servidor...',
        SERVER_OFFLINE: 'Servidor está apagado!',
        USER_IS_TYPING: 'El usuario está escribiendo...',
        UPLOADING_FILE: 'Enviando fichero',
        ERROR_UPLOADING_FILE: 'Error al adjuntar fichero!',
        ATTACH_FILE: 'Adjuntar un fichero',
        USER_OFFLINE: 'La internet se ha ido',
        USER_ONLINE: 'Estamos de vuelta!',
        MUTE: 'Silenciar conversación',
        CLOSE: 'Cerrar conversación',
        MINIMIZE: 'Minimizar conversación',
        TITLE: 'Soporte',
        POWERED_BY: 'Desarrollado por  Prud.io'
    },
    fr: {
        CHAT_WITH_US: 'Chattez avec nous',
        NAME: 'Nom',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Démarrez une conversation',
        NEW_MESSAGE: 'Nouveau message',
        NO_USERS_ONLINE: 'Désolé! Actuellement aucun membre en ligne pour vous aider. laissez un message et nous reviendrons vers vous dès que possible!',
        CONVERSATION_HISTORY: 'L\'historique des conversations',
        JUST_WRITE: 'Écrivez ici ...',
        PROBLEM_CONNECTING_TO_SERVER: 'Problème de connexion au serveur!',
        ERROR_FILL_ALL_FIELDS: 'S\'il vous plaît remplir les champs avec un nom et une adresse email valide.',
        CONNECTING_TO_SERVER: 'Connexion au serveur...',
        SERVER_OFFLINE: 'Server est hors ligne!',
        USER_IS_TYPING: 'l\'utilisateur est en train de taper...',
        UPLOADING_FILE: 'Envoi de fichier',
        ERROR_UPLOADING_FILE: 'Une erreur est survenue durant l\'envoi du fichier!',
        ATTACH_FILE: 'Joindre un fichier',
        USER_OFFLINE: 'Pas de connexion internet',
        USER_ONLINE: 'Nous sommes de retour!',
        MUTE: 'Mettre en sourdine',
        CLOSE: 'Fermer la conversation',
        MINIMIZE: 'Réduire la conversation',
        TITLE: 'Soutien',
        POWERED_BY: 'Propulsé par  Prud.io'
    },
    tr: {
        CHAT_WITH_US: 'Bizimle Konuşun',
        NAME: 'Ad',
        EMAIL: 'E-posta',
        START_CONVERSATION: 'Görüşme başlat',
        NEW_MESSAGE: 'Yeni mesaj',
        NO_USERS_ONLINE: 'Şu anda size yardımcı olabilecek çevrimiçi kullanıcı mevcut değil. Lütfen bir mesaj bırakın, size en kısa zamanda dönüş yapacağız. Üzgünüz.',
        CONVERSATION_HISTORY: 'Görüşme Tarihçesi',
        JUST_WRITE: 'Buraya yazın...',
        PROBLEM_CONNECTING_TO_SERVER: 'Sunucuya bağlanırken bir sorun oluştu!',
        ERROR_FILL_ALL_FIELDS: 'Lütfen tüm alanları geçerli bir isim ve e-posta ile doldurun.',
        CONNECTING_TO_SERVER: 'Sunucuya bağlanılıyor...',
        SERVER_OFFLINE: 'Sunucu şu anda çevrimdışı!',
        USER_IS_TYPING: 'Kullanıcı yazıyor...',
        UPLOADING_FILE: 'Dosya sunucuya yükleniyor',
        ERROR_UPLOADING_FILE: 'Sunucuya dosya yüklenirken hata oluştu!',
        ATTACH_FILE: 'Dosya Ekle',
        USER_OFFLINE: 'Görünüşe göre internet gitti!',
        USER_ONLINE: 'Geri geldik!',
        MUTE: 'Görüşmeyi sessize al',
        CLOSE: 'Görüşmeyi kapat',
        MINIMIZE: 'Görüşmeyi aşağıya indir',
        TITLE: 'Destek',
        POWERED_BY: ' Prud.io ile çalışmaktadır.'
    }
};
<commit_msg>Romanian Translation<commit_after>exports.localization = {
    en: {
        CHAT_WITH_US: 'Chat with us',
        NAME: 'Name',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Start conversation',
        NEW_MESSAGE: 'New message',
        NO_USERS_ONLINE: 'Currently there are no users online to help you. Leave a message and we will get back to you ASAP! Sorry!',
        CONVERSATION_HISTORY: 'Conversation history',
        JUST_WRITE: 'Just write...',
        PROBLEM_CONNECTING_TO_SERVER: 'We got a problem connecting to the server!',
        ERROR_FILL_ALL_FIELDS: 'Please fill the fields with valid name and email.',
        CONNECTING_TO_SERVER: 'Connecting to the server...',
        SERVER_OFFLINE: 'Server is now offline!',
        USER_IS_TYPING: 'User is typing...',
        UPLOADING_FILE: 'Uploading file',
        ERROR_UPLOADING_FILE: 'Error uploading the file!',
        ATTACH_FILE: 'Attach a file',
        USER_OFFLINE: 'Looks like internet is gone!',
        USER_ONLINE: 'We are back!',
        MUTE: 'Mute conversation',
        CLOSE: 'Close conversation',
        MINIMIZE: 'Minimize conversation',
        TITLE: 'Support',
        POWERED_BY: 'Powered by  Prud.io'
    },
    pt: {
        CHAT_WITH_US: 'Fale connosco',
        NAME: 'Nome',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Iniciar conversa',
        NEW_MESSAGE: 'Nova mensagem',
        NO_USERS_ONLINE: 'Desculpe! Neste momento não existem utilizadores online para ajuda-lo. Deixe uma mensagem e tentaremos entrar em contacto o mais rápido possivel!',
        CONVERSATION_HISTORY: 'Histórico da conversa',
        JUST_WRITE: 'Escreva aqui...',
        PROBLEM_CONNECTING_TO_SERVER: 'Ocorreu um problema ao efectuar a ligação ao servidor!',
        ERROR_FILL_ALL_FIELDS: 'Por favor preencha os campos com um nome e e-mail válido.',
        CONNECTING_TO_SERVER: 'A ligar ao servidor...',
        SERVER_OFFLINE: 'Servidor está desligado!',
        USER_IS_TYPING: 'A escrever...',
        UPLOADING_FILE: 'Anexando ficheiro',
        ERROR_UPLOADING_FILE: 'Erro ao anexar ficheiro!',
        ATTACH_FILE: 'Anexar um ficheiro',
        USER_OFFLINE: 'Parece que a internet foi abaixo!',
        USER_ONLINE: 'Estamos de volta!',
        MUTE: 'Silenciar conversa',
        CLOSE: 'Fechar conversa',
        MINIMIZE: 'Minimizar conversa',
        TITLE: 'Suporte',
        POWERED_BY: 'Desenvolvido por  Prud.io'
    },
    de: {
        CHAT_WITH_US: 'Chatten Sie mit uns',
        NAME: 'Name',
        EMAIL: 'E-Mail',
        START_CONVERSATION: 'Starten Sie einen Chat',
        NEW_MESSAGE: 'Neue Nachricht',
        NO_USERS_ONLINE: 'Zur Zeit ist niemand online um Ihnen zu helfen. Hinterlassen Sie eine Nachricht und wir melden uns sobald wie möglich bei Ihnen.',
        CONVERSATION_HISTORY: 'Nachrichtenverlauf',
        JUST_WRITE: 'Hier schreiben...',
        PROBLEM_CONNECTING_TO_SERVER: 'Die Verbindung zum Server ist fehlgeschlagen.',
        ERROR_FILL_ALL_FIELDS: 'Bitte füllen Sie die Felder mit einem Namen und einer gültigen E-Mail Adresse aus.',
        CONNECTING_TO_SERVER: 'Verbinde zum Server...',
        SERVER_OFFLINE: 'Server ist offline.',
        USER_IS_TYPING: 'Benutzer schreibt...',
        UPLOADING_FILE: 'Datei wird hochgeladen',
        ERROR_UPLOADING_FILE: 'Das Hochladen ist fehlgeschlagen',
        ATTACH_FILE: 'Datei hochladen',
        USER_OFFLINE: 'Keine Internetverbindung',
        USER_ONLINE: 'Internetverbindung wieder vorhanden',
        MUTE: 'Chat stummschalten',
        CLOSE: 'Chat schliessen',
        MINIMIZE: 'Chat minimieren',
        TITLE: 'Support',
        POWERED_BY: ' Prud.io'
    },
    es: {
        CHAT_WITH_US: 'Habla con nosotros',
        NAME: 'Nombre',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Iniciar una conversación',
        NEW_MESSAGE: 'Nuevo mensaje',
        NO_USERS_ONLINE: 'Perdon! Actualmente no hay miembros en línea para ayudarle. Deja un mensaje y trataremos de ponernos en contacto lo antes posible!',
        CONVERSATION_HISTORY: 'Historial de la conversación',
        JUST_WRITE: 'Escriba aqui...',
        PROBLEM_CONNECTING_TO_SERVER: 'Hubo un problema al conectar con el servidor!',
        ERROR_FILL_ALL_FIELDS: 'Por favor, rellene los campos con un nombre y una dirección de correo electrónico válida.',
        CONNECTING_TO_SERVER: 'Conectando con el servidor...',
        SERVER_OFFLINE: 'Servidor está apagado!',
        USER_IS_TYPING: 'El usuario está escribiendo...',
        UPLOADING_FILE: 'Enviando fichero',
        ERROR_UPLOADING_FILE: 'Error al adjuntar fichero!',
        ATTACH_FILE: 'Adjuntar un fichero',
        USER_OFFLINE: 'La internet se ha ido',
        USER_ONLINE: 'Estamos de vuelta!',
        MUTE: 'Silenciar conversación',
        CLOSE: 'Cerrar conversación',
        MINIMIZE: 'Minimizar conversación',
        TITLE: 'Soporte',
        POWERED_BY: 'Desarrollado por  Prud.io'
    },
    fr: {
        CHAT_WITH_US: 'Chattez avec nous',
        NAME: 'Nom',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Démarrez une conversation',
        NEW_MESSAGE: 'Nouveau message',
        NO_USERS_ONLINE: 'Désolé! Actuellement aucun membre en ligne pour vous aider. laissez un message et nous reviendrons vers vous dès que possible!',
        CONVERSATION_HISTORY: 'L\'historique des conversations',
        JUST_WRITE: 'Écrivez ici ...',
        PROBLEM_CONNECTING_TO_SERVER: 'Problème de connexion au serveur!',
        ERROR_FILL_ALL_FIELDS: 'S\'il vous plaît remplir les champs avec un nom et une adresse email valide.',
        CONNECTING_TO_SERVER: 'Connexion au serveur...',
        SERVER_OFFLINE: 'Server est hors ligne!',
        USER_IS_TYPING: 'l\'utilisateur est en train de taper...',
        UPLOADING_FILE: 'Envoi de fichier',
        ERROR_UPLOADING_FILE: 'Une erreur est survenue durant l\'envoi du fichier!',
        ATTACH_FILE: 'Joindre un fichier',
        USER_OFFLINE: 'Pas de connexion internet',
        USER_ONLINE: 'Nous sommes de retour!',
        MUTE: 'Mettre en sourdine',
        CLOSE: 'Fermer la conversation',
        MINIMIZE: 'Réduire la conversation',
        TITLE: 'Soutien',
        POWERED_BY: 'Propulsé par  Prud.io'
    },
    tr: {
        CHAT_WITH_US: 'Bizimle Konuşun',
        NAME: 'Ad',
        EMAIL: 'E-posta',
        START_CONVERSATION: 'Görüşme başlat',
        NEW_MESSAGE: 'Yeni mesaj',
        NO_USERS_ONLINE: 'Şu anda size yardımcı olabilecek çevrimiçi kullanıcı mevcut değil. Lütfen bir mesaj bırakın, size en kısa zamanda dönüş yapacağız. Üzgünüz.',
        CONVERSATION_HISTORY: 'Görüşme Tarihçesi',
        JUST_WRITE: 'Buraya yazın...',
        PROBLEM_CONNECTING_TO_SERVER: 'Sunucuya bağlanırken bir sorun oluştu!',
        ERROR_FILL_ALL_FIELDS: 'Lütfen tüm alanları geçerli bir isim ve e-posta ile doldurun.',
        CONNECTING_TO_SERVER: 'Sunucuya bağlanılıyor...',
        SERVER_OFFLINE: 'Sunucu şu anda çevrimdışı!',
        USER_IS_TYPING: 'Kullanıcı yazıyor...',
        UPLOADING_FILE: 'Dosya sunucuya yükleniyor',
        ERROR_UPLOADING_FILE: 'Sunucuya dosya yüklenirken hata oluştu!',
        ATTACH_FILE: 'Dosya Ekle',
        USER_OFFLINE: 'Görünüşe göre internet gitti!',
        USER_ONLINE: 'Geri geldik!',
        MUTE: 'Görüşmeyi sessize al',
        CLOSE: 'Görüşmeyi kapat',
        MINIMIZE: 'Görüşmeyi aşağıya indir',
        TITLE: 'Destek',
        POWERED_BY: ' Prud.io ile çalışmaktadır.'
    },
    ro: {
        CHAT_WITH_US: 'Discută cu noi',
        NAME: 'Nume',
        EMAIL: 'E-mail',
        START_CONVERSATION: 'Pornește conversație',
        NEW_MESSAGE: 'Mesaj Nou',
        NO_USERS_ONLINE: 'Momentan nu este online nici un utilizator care să te ajute. Lasă un mesaj și te vom contacta în curând!',
        CONVERSATION_HISTORY: 'Istoric Conversații',
        JUST_WRITE: 'Scrie aici...',
        PROBLEM_CONNECTING_TO_SERVER: 'A apărut o eroare de conectare la server!',
        ERROR_FILL_ALL_FIELDS: 'Completati câmpurile cu name și email valide.',
        CONNECTING_TO_SERVER: 'Se conectează la server...',
        SERVER_OFFLINE: 'Serverul nu este disponibil!',
        USER_IS_TYPING: 'Utilizatorul scrie...',
        UPLOADING_FILE: 'Încărcare fișier',
        ERROR_UPLOADING_FILE: 'Eroare de încărcare a fișierului!',
        ATTACH_FILE: ' Atașază un fișier',
        USER_OFFLINE: 'Conexiune la Internet indisponibilă!',
        USER_ONLINE: 'Ne-am întors!',
        MUTE: 'Fără Sonor',
        CLOSE: 'Închide conversația',
        MINIMIZE: 'Minimizează conversația',
        TITLE: 'Asistență',
        POWERED_BY: 'Dezvoltat de  Prud.io'
    }
};
<|endoftext|>


<commit_before>var _ = require('underscore');
var fs = require('fs');
var url = require('url');
var path = require('path');
var yaml = require('js-yaml');
var mapnik = require('mapnik');
var mkdirp = require('mkdirp');
var chrono = require('chrono');
var crypto = require('crypto');
var tilelive = require('tilelive');
var existsSync = require('fs').existsSync || require('path').existsSync;

tilelive.protocols['mbtiles:'] = require('mbtiles');

var tm = {};

// Set or get config.
tm.db;
tm._config = {};
tm.config = function(opts, callback) {
    if (!opts) return tm._config;

    tm._config = _(opts).defaults(tm._config);
    // @TODO create other paths (cache, etc.)
    try {
        mkdirp.sync(path.dirname(tm.config().db));
        mkdirp.sync(tm.config().cache);
        mkdirp.sync(tm.config().fonts);
    } catch(err) { throw err; }

    // Register default fonts.
    mapnik.register_fonts(path.dirname(require.resolve('tm2-default-fonts')), { recurse: true });
    mapnik.register_fonts(path.dirname(require.resolve('tm2-private-fonts')), { recurse: true });
    mapnik.register_fonts(tm.config().fonts, { recurse: true });

    // Load + compact the app database.
    var old = require('dirty')(tm.config().db);
    old.on('load', function() {
        var db_name = tm.config().db;
        if (existsSync(db_name)) {
            fs.unlinkSync(db_name);
        }
        tm.db = require('dirty')(db_name);

        // Check app db version and run any migrations if necessary.
        switch (old.get('version')) {
        case 2:
            old.forEach(function(k,v) { tm.db.set(k,v); });
            break;
        case 1:
        case undefined:
            old.set('version', 2);
            old.set('history', _(old.get('history')||{}).reduce(function(memo, list, type) {
                if (type !== 'style' && type !== 'source') return memo;
                memo[type] = list.map(function(id) {
                    var uri = url.parse(id);
                    if (uri.protocol === 'mapbox:') {
                        return !uri.pathname ? id.replace('://', ':///') : id;
                    } else if (!uri.protocol) {
                        return 'tm' + type + '://' + uri.pathname;
                    } else {
                        return id;
                    }
                });
                return memo;
            }, { style:[], source:[] }));
            old.forEach(function(k,v) { tm.db.set(k,v); });
            break;
        };

        if (callback) return callback();
    });

    return tm._config;
};
tm.config({
    db: path.join(process.env.HOME, '.tilemill', 'v2', 'app.db'),
    cache: path.join(process.env.HOME, '.tilemill', 'v2', 'cache'),
    fonts: path.join(process.env.HOME, '.tilemill', 'v2', 'fonts'),
    mapboxauth: 'https://tiles.mapbox.com',
    mapboxtile: 'http://a.tiles.mapbox.com/v3'
});

// Set or remove a project id from recent history.
var defaultSources = [
    'mapbox:///mapbox.mapbox-streets-v4'
];
tm.history = function(type, id, invalidate) {
    var history = tm.db.get('history') || {};
    history = _(history).isArray() ? { style: history } : history;
    history.style = history.style || [];
    history.source = history.source || [];

    if (id && type) {
        if (!history[type]) throw new Error('History set requires valid type');
        var update = invalidate
            ? _(history[type]).reject(function(k) { return k === id; })
            : _(history[type].concat([id])).uniq();
        if (!_(update).isEqual(history[type])) {
            history[type] = update;
            tm.db.set('history', history);
        }
    }
    history.source = _(history.source.concat(defaultSources)).uniq();
    history.style.sort();
    history.source.sort();
    return history;
};

// Remotes cache.
tm.remote = function(id, data) {
    var remote = tm.db.get('remote') || {};
    if (!data) {
        return remote[id] || false;
    } else {
        remote[id] = data;
        tm.db.set('remote', remote);
        return remote[id];
    }
};

// Load templates.
tm.templates = _(fs.readdirSync(__dirname + '/../templates')).reduce(function(memo, file) {
    if (file.charAt(0) === '.') return memo;
    memo[file.split('.')[0]] = _(fs.readFileSync(__dirname + '/../templates/' + file, 'utf8')).template();
    return memo;
}, {});

// Writes an array of files defined by { basename:[basename], data:[data] }
tm.writefiles = function(basepath, files, callback) {
    var write = function() {
        if (!files.length) return callback();
        var file = files.shift();
        fs.writeFile(path.join(basepath, file.basename), file.data, function(err) {
            if (err) return callback(err);
            write();
        });
    };
    mkdirp(basepath, function(err) {
        if (err) return callback(err);
        write();
    });
};

// Reads a directory of files.
tm.dirfiles = function(basepath, callback) {
    fs.readdir(path.resolve(basepath), function(err, files) {
        if (err) return callback(err);
        files = files.filter(function(f) { return f[0] !== '.' });

        var stats = [];
        var stat = function() {
            if (!files.length) {
                stats.sort(function(a,b) {
                    if (a.type < b.type) return -1;
                    if (a.type > b.type) return 1;
                    if (a.basename.toLowerCase() < b.basename.toLowerCase()) return -1;
                    if (a.basename.toLowerCase() > b.basename.toLowerCase()) return 1;
                    return 0;
                });
                return callback(null, stats);
            }
            var p = path.join(basepath, files.shift());
            fs.stat(p, function(err, s) {
                if (err) return callback(err);
                if (s.isFile()) s.type = 'file';
                if (s.isDirectory()) s.type = 'dir';
                if (s.type) {
                    s.path = p;
                    s.extname = path.extname(p);
                    s.basename = path.basename(p);
                    stats.push(s);
                }
                stat();
            });
        };
        stat();
    });
};

// Filter keys out of an object that are not present in defaults.
tm.filterkeys = function(data, defaults) {
    return _(data).reduce(function(memo,v,k) {
        if (!(k in defaults)) return memo;
        memo[k] = v;
        return memo;
    }, {})
};

// Return an object with sorted keys, ignoring case.
tm.sortkeys = function(obj) {
    try {
        return obj.map(tm.sortkeys);
    } catch(e) {};
    try {
        return Object.keys(obj).sort(function(a, b) {
            a = a.toLowerCase();
            b = b.toLowerCase();
            if (a === 'id') return -1;
            if (b === 'id') return 1;
            if (a > b) return 1;
            if (a < b) return -1;
            return 0;
        }).reduce(function(memo, key) {
            memo[key] = tm.sortkeys(obj[key]);
            return memo;
        }, {});
    } catch(e) { return obj };
};

// Generate or verify that an id is a temporary one.
tm.tmpid = function(protocol, id, md5) {
    if (id && !md5) return (new RegExp(protocol + '\/\/\/tmp-[0-9a-f]{8}')).test(id);

    if (id && md5) {
        return protocol + '///tmp-' + crypto.createHash('md5').update(id).digest('hex').substr(0,8);
    } else {
        id = protocol + '///tmp-';
        var base16 = '0123456789abcdef';
        for (var i = 0; i < 8; i++) id += base16[Math.random() * 16 | 0];
        return id;
    }
};

// Named projections.
tm.srs = {
    'WGS84': '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs',
    '900913': '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over'
};
tm.extent = {
    'WGS84': '-180,-85.0511,180,85.0511',
    '900913': '-20037508.34,-20037508.34,20037508.34,20037508.34'
};
// Reverse the above hash to allow for srs name lookups.
tm.srsname = {};
for (name in tm.srs) tm.srsname[tm.srs[name]] = name;

// Render a preview image for a font.
tm.font = function(name, callback) {
    tm.fonts = tm.fonts || mapnik.fonts();
    if (tm.fonts.indexOf(name) === -1) return callback(new Error('Invalid font ' + name));

    // Attempt to retrieve from cache.
    var xml = tm.templates.xrayfont(name);
    var ckey = 'font-' + crypto.createHash('md5').update(xml).digest('hex').substr(0,8) + '.png';
    var file = tm.config().cache + '/' + ckey;
    fs.readFile(file, function(err, buffer) {
        if (err && err.code !== 'ENOENT') return callback(err);
        if (buffer) return callback(null, buffer);

        var map = new mapnik.Map(400,40);
        map.extent = [-180,-10,180,10];
        map.fromString(xml, function(err) {
            if (err) return callback(err);
            map.render(new mapnik.Image(400,40), {}, function(err, image) {
                if (err) return callback(err);
                image.encode('png8:m=h:c=64', function(err, buffer) {
                    if (err) return callback(err);

                    // Don't wait for disk write to return image buffer.
                    // If caching fails, error is logged to console.
                    callback(null, buffer);
                    fs.writeFile(file, buffer, function(err) {
                        if (err) console.error(err);
                    });
                });
            });
        });
    });
};

// For a given from => to copy task,
// 1. Returns a completed progress object if export exists.
// 2. Returns the current in-progress object if export is active.
// 3. Creates a new copy task for the export otherwise.
tm._copy = null;
tm.copytask = function(from, to, callback) {
    function state() {
        if (!tm._copy) return callback();
        // Snapshot current task stats.
        tm._copy.stats = tm._copy.task ? tm._copy.task.stats.snapshot() : null;
        // Return job object to caller.
        callback(null, tm._copy);
        // Clear out current job reference if complete.
        tm._copy = tm._copy.task ? tm._copy : null;
        return;
    };

    if (!from && !to) return state();

    if (tm._copy) return tm._copy.id !== from
        ? callback(new Error('Active task already in progress'))
        : state();

    var furi = url.parse(from);
    var turi = url.parse(to);

    if (furi.protocol !== 'tmsource:')
        return callback(new Error('Unsupported source protocol'));

    if (turi.protocol !== 'mbtiles:')
        return callback(new Error('Unsupported sink protocol'));

    fs.stat(turi.pathname, function(err, stat) {
        if (err && err.code !== 'ENOENT') return callback(err);

        if (stat) return callback(null, {
            id: from,
            file: turi.pathname,
            stat: stat,
            task: null,
            stats: null
        });

        var source = require('./source');
        source(from, function(err, s) {
            if (err) return callback(err);
            s.getInfo(function(err, info) {
                if (err) return callback(err);

                var tmp = 'mbtiles:///tmp/' + path.basename(turi.pathname);
                var opts = {
                    minzoom: info.minzoom,
                    maxzoom: info.maxzoom,
                    bbox: s.data.bounds || source.extent(s.data)
                };
                var scheme = tilelive.Scheme.create('scanline', opts);
                var task = new tilelive.CopyTask(from, tmp, scheme);
                tm._copy = {
                    id: from,
                    file: null,
                    stat: null,
                    task: task,
                    stats: task.stats.snapshot()
                };

                // This method responds early to be as responsive as possible.
                // Errors in the export will be apparent one way or another.
                callback(null, tm._copy);

                // Move tmp export into place when complete.
                tm._copy.task.on('finished', function(err) {
                    // Enable caching on source again.
                    delete s._nocache;

                    fs.unlink(tm._copy.task.job, function(err) {
                        if (err) console.error(err);
                    });
                    fs.rename(url.parse(tmp).pathname, turi.pathname, function(err) {
                        if (err) return (tm._copy = err) && console.error(err);
                        fs.stat(turi.pathname, function(err, stat) {
                            if (err) return (tm._copy = err) && console.error(err);
                            tm._copy = {
                                id: from,
                                file: turi.pathname,
                                stat: stat,
                                task: null,
                                stats: null
                            };
                            console.log('[%s] %s complete',
                                (new Date).toUTCString(),
                                path.basename(from));
                        });
                    });
                });
                // Start the task.
                console.log('[%s] %s (z%d-%d @ %s)',
                    (new Date).toUTCString(),
                    path.basename(from),
                    opts.minzoom,
                    opts.maxzoom,
                    opts.bbox.join(','));

                // Disable source caching for export.
                s._nocache = true;

                tm._copy.task.start(function(err) {

                    // Emit an event for tests.
                    tm._copy.task.emit('started');

                    if (err) return (tm._copy = err) && console.error(err);
                    tm._copy.task.sink.putInfo(info, function(err) {
                        if (err) return (tm._copy = err) && console.error(err);
                    });
                });
            });
        });
    });
};

// Cancel/clear the current active task.
// No-op if no task is active.
tm.cleartask = function(callback) {
    if (!tm._copy) return callback();
    if (tm._copy.task) return tm._copy.task.pause(function(err) {
        if (err) return callback(err);
        // Remove sink file.
        fs.unlink(tm._copy.task.sink.filename, function(err) {
            if (err && err.code !== 'ENOENT') return callback(Err);
            // Remove job file.
            fs.unlink(tm._copy.task.job, function(err) {
                if (err && err.code !== 'ENOENT') return callback(Err);
                console.log('[%s] %s canceled',
                    (new Date).toUTCString(),
                    path.basename(tm._copy.id));
                tm._copy = null;
                return callback();
            });
        });
    });
    tm._copy = null;
    return callback();
};

module.exports = tm;
<commit_msg>fix unintended commit<commit_after>var _ = require('underscore');
var fs = require('fs');
var url = require('url');
var path = require('path');
var yaml = require('js-yaml');
var mapnik = require('mapnik');
var mkdirp = require('mkdirp');
var chrono = require('chrono');
var crypto = require('crypto');
var tilelive = require('tilelive');
var existsSync = require('fs').existsSync || require('path').existsSync;

tilelive.protocols['mbtiles:'] = require('mbtiles');

var tm = {};

// Set or get config.
tm.db;
tm._config = {};
tm.config = function(opts, callback) {
    if (!opts) return tm._config;

    tm._config = _(opts).defaults(tm._config);
    // @TODO create other paths (cache, etc.)
    try {
        mkdirp.sync(path.dirname(tm.config().db));
        mkdirp.sync(tm.config().cache);
        mkdirp.sync(tm.config().fonts);
    } catch(err) { throw err; }

    // Register default fonts.
    mapnik.register_fonts(path.dirname(require.resolve('tm2-default-fonts')), { recurse: true });
    mapnik.register_fonts(tm.config().fonts, { recurse: true });

    // Load + compact the app database.
    var old = require('dirty')(tm.config().db);
    old.on('load', function() {
        var db_name = tm.config().db;
        if (existsSync(db_name)) {
            fs.unlinkSync(db_name);
        }
        tm.db = require('dirty')(db_name);

        // Check app db version and run any migrations if necessary.
        switch (old.get('version')) {
        case 2:
            old.forEach(function(k,v) { tm.db.set(k,v); });
            break;
        case 1:
        case undefined:
            old.set('version', 2);
            old.set('history', _(old.get('history')||{}).reduce(function(memo, list, type) {
                if (type !== 'style' && type !== 'source') return memo;
                memo[type] = list.map(function(id) {
                    var uri = url.parse(id);
                    if (uri.protocol === 'mapbox:') {
                        return !uri.pathname ? id.replace('://', ':///') : id;
                    } else if (!uri.protocol) {
                        return 'tm' + type + '://' + uri.pathname;
                    } else {
                        return id;
                    }
                });
                return memo;
            }, { style:[], source:[] }));
            old.forEach(function(k,v) { tm.db.set(k,v); });
            break;
        };

        if (callback) return callback();
    });

    return tm._config;
};
tm.config({
    db: path.join(process.env.HOME, '.tilemill', 'v2', 'app.db'),
    cache: path.join(process.env.HOME, '.tilemill', 'v2', 'cache'),
    fonts: path.join(process.env.HOME, '.tilemill', 'v2', 'fonts'),
    mapboxauth: 'https://tiles.mapbox.com',
    mapboxtile: 'http://a.tiles.mapbox.com/v3'
});

// Set or remove a project id from recent history.
var defaultSources = [
    'mapbox:///mapbox.mapbox-streets-v4'
];
tm.history = function(type, id, invalidate) {
    var history = tm.db.get('history') || {};
    history = _(history).isArray() ? { style: history } : history;
    history.style = history.style || [];
    history.source = history.source || [];

    if (id && type) {
        if (!history[type]) throw new Error('History set requires valid type');
        var update = invalidate
            ? _(history[type]).reject(function(k) { return k === id; })
            : _(history[type].concat([id])).uniq();
        if (!_(update).isEqual(history[type])) {
            history[type] = update;
            tm.db.set('history', history);
        }
    }
    history.source = _(history.source.concat(defaultSources)).uniq();
    history.style.sort();
    history.source.sort();
    return history;
};

// Remotes cache.
tm.remote = function(id, data) {
    var remote = tm.db.get('remote') || {};
    if (!data) {
        return remote[id] || false;
    } else {
        remote[id] = data;
        tm.db.set('remote', remote);
        return remote[id];
    }
};

// Load templates.
tm.templates = _(fs.readdirSync(__dirname + '/../templates')).reduce(function(memo, file) {
    if (file.charAt(0) === '.') return memo;
    memo[file.split('.')[0]] = _(fs.readFileSync(__dirname + '/../templates/' + file, 'utf8')).template();
    return memo;
}, {});

// Writes an array of files defined by { basename:[basename], data:[data] }
tm.writefiles = function(basepath, files, callback) {
    var write = function() {
        if (!files.length) return callback();
        var file = files.shift();
        fs.writeFile(path.join(basepath, file.basename), file.data, function(err) {
            if (err) return callback(err);
            write();
        });
    };
    mkdirp(basepath, function(err) {
        if (err) return callback(err);
        write();
    });
};

// Reads a directory of files.
tm.dirfiles = function(basepath, callback) {
    fs.readdir(path.resolve(basepath), function(err, files) {
        if (err) return callback(err);
        files = files.filter(function(f) { return f[0] !== '.' });

        var stats = [];
        var stat = function() {
            if (!files.length) {
                stats.sort(function(a,b) {
                    if (a.type < b.type) return -1;
                    if (a.type > b.type) return 1;
                    if (a.basename.toLowerCase() < b.basename.toLowerCase()) return -1;
                    if (a.basename.toLowerCase() > b.basename.toLowerCase()) return 1;
                    return 0;
                });
                return callback(null, stats);
            }
            var p = path.join(basepath, files.shift());
            fs.stat(p, function(err, s) {
                if (err) return callback(err);
                if (s.isFile()) s.type = 'file';
                if (s.isDirectory()) s.type = 'dir';
                if (s.type) {
                    s.path = p;
                    s.extname = path.extname(p);
                    s.basename = path.basename(p);
                    stats.push(s);
                }
                stat();
            });
        };
        stat();
    });
};

// Filter keys out of an object that are not present in defaults.
tm.filterkeys = function(data, defaults) {
    return _(data).reduce(function(memo,v,k) {
        if (!(k in defaults)) return memo;
        memo[k] = v;
        return memo;
    }, {})
};

// Return an object with sorted keys, ignoring case.
tm.sortkeys = function(obj) {
    try {
        return obj.map(tm.sortkeys);
    } catch(e) {};
    try {
        return Object.keys(obj).sort(function(a, b) {
            a = a.toLowerCase();
            b = b.toLowerCase();
            if (a === 'id') return -1;
            if (b === 'id') return 1;
            if (a > b) return 1;
            if (a < b) return -1;
            return 0;
        }).reduce(function(memo, key) {
            memo[key] = tm.sortkeys(obj[key]);
            return memo;
        }, {});
    } catch(e) { return obj };
};

// Generate or verify that an id is a temporary one.
tm.tmpid = function(protocol, id, md5) {
    if (id && !md5) return (new RegExp(protocol + '\/\/\/tmp-[0-9a-f]{8}')).test(id);

    if (id && md5) {
        return protocol + '///tmp-' + crypto.createHash('md5').update(id).digest('hex').substr(0,8);
    } else {
        id = protocol + '///tmp-';
        var base16 = '0123456789abcdef';
        for (var i = 0; i < 8; i++) id += base16[Math.random() * 16 | 0];
        return id;
    }
};

// Named projections.
tm.srs = {
    'WGS84': '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs',
    '900913': '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over'
};
tm.extent = {
    'WGS84': '-180,-85.0511,180,85.0511',
    '900913': '-20037508.34,-20037508.34,20037508.34,20037508.34'
};
// Reverse the above hash to allow for srs name lookups.
tm.srsname = {};
for (name in tm.srs) tm.srsname[tm.srs[name]] = name;

// Render a preview image for a font.
tm.font = function(name, callback) {
    tm.fonts = tm.fonts || mapnik.fonts();
    if (tm.fonts.indexOf(name) === -1) return callback(new Error('Invalid font ' + name));

    // Attempt to retrieve from cache.
    var xml = tm.templates.xrayfont(name);
    var ckey = 'font-' + crypto.createHash('md5').update(xml).digest('hex').substr(0,8) + '.png';
    var file = tm.config().cache + '/' + ckey;
    fs.readFile(file, function(err, buffer) {
        if (err && err.code !== 'ENOENT') return callback(err);
        if (buffer) return callback(null, buffer);

        var map = new mapnik.Map(400,40);
        map.extent = [-180,-10,180,10];
        map.fromString(xml, function(err) {
            if (err) return callback(err);
            map.render(new mapnik.Image(400,40), {}, function(err, image) {
                if (err) return callback(err);
                image.encode('png8:m=h:c=64', function(err, buffer) {
                    if (err) return callback(err);

                    // Don't wait for disk write to return image buffer.
                    // If caching fails, error is logged to console.
                    callback(null, buffer);
                    fs.writeFile(file, buffer, function(err) {
                        if (err) console.error(err);
                    });
                });
            });
        });
    });
};

// For a given from => to copy task,
// 1. Returns a completed progress object if export exists.
// 2. Returns the current in-progress object if export is active.
// 3. Creates a new copy task for the export otherwise.
tm._copy = null;
tm.copytask = function(from, to, callback) {
    function state() {
        if (!tm._copy) return callback();
        // Snapshot current task stats.
        tm._copy.stats = tm._copy.task ? tm._copy.task.stats.snapshot() : null;
        // Return job object to caller.
        callback(null, tm._copy);
        // Clear out current job reference if complete.
        tm._copy = tm._copy.task ? tm._copy : null;
        return;
    };

    if (!from && !to) return state();

    if (tm._copy) return tm._copy.id !== from
        ? callback(new Error('Active task already in progress'))
        : state();

    var furi = url.parse(from);
    var turi = url.parse(to);

    if (furi.protocol !== 'tmsource:')
        return callback(new Error('Unsupported source protocol'));

    if (turi.protocol !== 'mbtiles:')
        return callback(new Error('Unsupported sink protocol'));

    fs.stat(turi.pathname, function(err, stat) {
        if (err && err.code !== 'ENOENT') return callback(err);

        if (stat) return callback(null, {
            id: from,
            file: turi.pathname,
            stat: stat,
            task: null,
            stats: null
        });

        var source = require('./source');
        source(from, function(err, s) {
            if (err) return callback(err);
            s.getInfo(function(err, info) {
                if (err) return callback(err);

                var tmp = 'mbtiles:///tmp/' + path.basename(turi.pathname);
                var opts = {
                    minzoom: info.minzoom,
                    maxzoom: info.maxzoom,
                    bbox: s.data.bounds || source.extent(s.data)
                };
                var scheme = tilelive.Scheme.create('scanline', opts);
                var task = new tilelive.CopyTask(from, tmp, scheme);
                tm._copy = {
                    id: from,
                    file: null,
                    stat: null,
                    task: task,
                    stats: task.stats.snapshot()
                };

                // This method responds early to be as responsive as possible.
                // Errors in the export will be apparent one way or another.
                callback(null, tm._copy);

                // Move tmp export into place when complete.
                tm._copy.task.on('finished', function(err) {
                    // Enable caching on source again.
                    delete s._nocache;

                    fs.unlink(tm._copy.task.job, function(err) {
                        if (err) console.error(err);
                    });
                    fs.rename(url.parse(tmp).pathname, turi.pathname, function(err) {
                        if (err) return (tm._copy = err) && console.error(err);
                        fs.stat(turi.pathname, function(err, stat) {
                            if (err) return (tm._copy = err) && console.error(err);
                            tm._copy = {
                                id: from,
                                file: turi.pathname,
                                stat: stat,
                                task: null,
                                stats: null
                            };
                            console.log('[%s] %s complete',
                                (new Date).toUTCString(),
                                path.basename(from));
                        });
                    });
                });
                // Start the task.
                console.log('[%s] %s (z%d-%d @ %s)',
                    (new Date).toUTCString(),
                    path.basename(from),
                    opts.minzoom,
                    opts.maxzoom,
                    opts.bbox.join(','));

                // Disable source caching for export.
                s._nocache = true;

                tm._copy.task.start(function(err) {

                    // Emit an event for tests.
                    tm._copy.task.emit('started');

                    if (err) return (tm._copy = err) && console.error(err);
                    tm._copy.task.sink.putInfo(info, function(err) {
                        if (err) return (tm._copy = err) && console.error(err);
                    });
                });
            });
        });
    });
};

// Cancel/clear the current active task.
// No-op if no task is active.
tm.cleartask = function(callback) {
    if (!tm._copy) return callback();
    if (tm._copy.task) return tm._copy.task.pause(function(err) {
        if (err) return callback(err);
        // Remove sink file.
        fs.unlink(tm._copy.task.sink.filename, function(err) {
            if (err && err.code !== 'ENOENT') return callback(Err);
            // Remove job file.
            fs.unlink(tm._copy.task.job, function(err) {
                if (err && err.code !== 'ENOENT') return callback(Err);
                console.log('[%s] %s canceled',
                    (new Date).toUTCString(),
                    path.basename(tm._copy.id));
                tm._copy = null;
                return callback();
            });
        });
    });
    tm._copy = null;
    return callback();
};

module.exports = tm;
<|endoftext|>


<commit_before>exports.productionVersionId = "530572b8be61";<commit_msg>Incrementing Production Version.<commit_after>exports.productionVersionId = "5e635d61a856";<|endoftext|>