<commit_before>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

/**
 * Create a new FormBuilder to generate an HTML form
 *
 * @param {String} id - The form HTML element ID.
 */
function FormBuilder(id) {
    this.form_id = id;
    this.html = "\n" + '<form role="form" id="' + id + '">';
}

/**
 * Return complete rendered HTML for the form.
 *
 * @return {String} form HTML
 */
FormBuilder.prototype.render = function() {
    return this.html + "</form>\n\n";
};

/**
 * Add a paragraph (``p`` tag) to the form.
 *
 * @param {String} content - The content of the ``p`` tag.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addP = function(content) {
    this.html += '
' + content + "
\n";
    return this;
};

/**
 * Add a hidden ``input`` to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} value - The value of the form element
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addHidden = function(id, name, value) {
    this.html += '<input type="hidden" id="' + id + '" name="' + name + '" value="' + value + '">\n';
    return this;
};

/**
 * Add a text ``input`` to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 * @param {String} options.inputHtml - extra HTML string to include in the
 *  actual ``input`` element *(optional; defaults to null)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addText = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ groupHtml: null, inputHtml: null }, options);
    this.html += '
' + label + '' +
        '<input class="form-control" id="' + id + '" name="' + name + '" type="text"';
    if(options.inputHtml !== null) { this.html += ' ' + options.inputHtml; }
    this.html += '>
\n';
    return this;
};

/**
 * Add a text ``input`` for currency to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.htmlClass - The HTML class to apply to the element; defaults to ``form-control``.
 * @param {String} options.helpBlock - Content for block of help text after input; defaults to null.
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 *  form-group div. This is where we'd usually add a default style/display. Defaults to null.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addCurrency = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ htmlClass: 'form-control', helpBlock: null, groupHtml: null }, options);
    this.html += '
' + label + '' +
        '
' +
        '$' +
        '<input class="' + options.htmlClass + '" id="' + id + '" name="' + name + '" type="text">' +
        '
';
    if (options.helpBlock !== null) {
        this.html += '
' + options.helpBlock + "
";
    }
    this.html += '
\n';
    return this;
};

/**
 * Add a date picker input to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addDatePicker = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ groupHtml: null }, options);
    this.html += '
' +
        '' + label + '' +
        '
' +
        '' +
        '<input class="form-control" id="' + id + '" name="' + name + '" type="text" size="12" maxlength="10">' +
        '
' +
        '
\n';
    return this;
};

/**
 * Add a select element to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Array} options - the options for the select, array of objects
 *  (order is preserved) each having the following attributes:
 * @param {String} options.label - the label for the option
 * @param {String} options.value - the value for the option
 * @param {Boolean} options.selected - whether the option should be the
 *  default selected value *(optional; defaults to False)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addSelect = function(id, name, label, options) {
    this.html += '
' + label + '' +
        '<select id="' + id + '" name="' + name + '" class="form-control">';
    for (var idx in options) {
        this.html += '<option value="' + options[idx].value + '"';
        if ('selected' in options[idx] && options[idx].selected === true) { this.html += ' selected="selected"'; }
        this.html += '>' + options[idx].label + '</option>';
    }
    this.html += '</select>
\n';
    return this;
};

/**
 * Add a select element to the form, taking an Object of options where keys
 * are the labels and values are the values. This is a convenience wrapper
 * around :js:func:`budgetTransferDivForm`.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options - the options for the select, label to value
 * @param {String} defaultValue - A value to select as the default
 * @param {Boolean} addNone - If true, prepend an option with a value of
 *  "None" and an empty label.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addLabelToValueSelect = function(id, name, label, options, defaultValue, addNone) {
    var built = [];
    if (addNone === true) {
        built.push({ value: 'None', label: '' });
        if(defaultValue === undefined) { defaultValue = 'None'; }
    }
    Object.keys(options).forEach(function (key) {
        var tmp = { value: options[key], label: key };
        if (defaultValue == options[key]) { tmp.selected = true; }
        built.push(tmp);
    });
    return this.addSelect(id, name, label, built);
};

/**
 * Add an inline radio button set to the form.
 *
 * Options is an Array of Objects, each object having keys ``id``, ``value``
 * and ``label``. Optional keys are ``checked`` (Boolean) and ``onchange``,
 * which will have its value placed literally in the HTML.
 *
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Array} options - the options for the select; array of objects
 *  each having the following attributes:
 * @param {String} options.id - the ID for the option
 * @param {String} options.value - the value for the option
 * @param {String} options.label - the label for the option
 * @param {Boolean} options.checked - whether the option should be checked
 *  by default *(optional; defaults to false)*
 * @param {String} options.inputHtml - extra HTML string to include in the
 *  actual ``input`` element *(optional; defaults to null)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addRadioInline = function(name, label, options) {
    this.html += '
' + label + '  ';
    for (var idx in options) {
        var o = options[idx];
        this.html += '' +
            '<input type="radio" name="' + name + '" id="' + o.id + '" value="' + o.value + '"';
        if ('inputHtml' in o) { this.html += ' ' + o.inputHtml; }
        if ('checked' in o && o.checked === true ) { this.html += ' checked'; }
        this.html += '>' + o.label + '';
    }
    this.html += '
\n';
    return this;
};

/**
 * Add a checkbox to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Boolean} checked - Whether to default to checked or not
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addCheckbox = function(id, name, label, checked) {
    this.html += '
' +
        '' +
        '<input type="checkbox" id="' + id + '" name="' + name + '"';
    if (checked === true) { this.html += ' checked'; }
    this.html += '> ' + label + '
\n';
    return this;
};
<commit_msg>fix typo<commit_after>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

/**
 * Create a new FormBuilder to generate an HTML form
 *
 * @param {String} id - The form HTML element ID.
 */
function FormBuilder(id) {
    this.form_id = id;
    this.html = "\n" + '<form role="form" id="' + id + '">';
}

/**
 * Return complete rendered HTML for the form.
 *
 * @return {String} form HTML
 */
FormBuilder.prototype.render = function() {
    return this.html + "</form>\n\n";
};

/**
 * Add a paragraph (``p`` tag) to the form.
 *
 * @param {String} content - The content of the ``p`` tag.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addP = function(content) {
    this.html += '
' + content + "
\n";
    return this;
};

/**
 * Add a hidden ``input`` to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} value - The value of the form element
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addHidden = function(id, name, value) {
    this.html += '<input type="hidden" id="' + id + '" name="' + name + '" value="' + value + '">\n';
    return this;
};

/**
 * Add a text ``input`` to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 * @param {String} options.inputHtml - extra HTML string to include in the
 *  actual ``input`` element *(optional; defaults to null)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addText = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ groupHtml: null, inputHtml: null }, options);
    this.html += '
' + label + '' +
        '<input class="form-control" id="' + id + '" name="' + name + '" type="text"';
    if(options.inputHtml !== null) { this.html += ' ' + options.inputHtml; }
    this.html += '>
\n';
    return this;
};

/**
 * Add a text ``input`` for currency to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.htmlClass - The HTML class to apply to the element; defaults to ``form-control``.
 * @param {String} options.helpBlock - Content for block of help text after input; defaults to null.
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 *  form-group div. This is where we'd usually add a default style/display. Defaults to null.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addCurrency = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ htmlClass: 'form-control', helpBlock: null, groupHtml: null }, options);
    this.html += '
' + label + '' +
        '
' +
        '$' +
        '<input class="' + options.htmlClass + '" id="' + id + '" name="' + name + '" type="text">' +
        '
';
    if (options.helpBlock !== null) {
        this.html += '
' + options.helpBlock + "
";
    }
    this.html += '
\n';
    return this;
};

/**
 * Add a date picker input to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options
 * @param {String} options.groupHtml - Additional HTML to add to the outermost
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addDatePicker = function(id, name, label, options) {
    if(options === undefined) { options = {}; }
    options = $.extend({ groupHtml: null }, options);
    this.html += '
' +
        '' + label + '' +
        '
' +
        '' +
        '<input class="form-control" id="' + id + '" name="' + name + '" type="text" size="12" maxlength="10">' +
        '
' +
        '
\n';
    return this;
};

/**
 * Add a select element to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Array} options - the options for the select, array of objects
 *  (order is preserved) each having the following attributes:
 * @param {String} options.label - the label for the option
 * @param {String} options.value - the value for the option
 * @param {Boolean} options.selected - whether the option should be the
 *  default selected value *(optional; defaults to False)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addSelect = function(id, name, label, options) {
    this.html += '
' + label + '' +
        '<select id="' + id + '" name="' + name + '" class="form-control">';
    for (var idx in options) {
        this.html += '<option value="' + options[idx].value + '"';
        if ('selected' in options[idx] && options[idx].selected === true) { this.html += ' selected="selected"'; }
        this.html += '>' + options[idx].label + '</option>';
    }
    this.html += '</select>
\n';
    return this;
};

/**
 * Add a select element to the form, taking an Object of options where keys
 * are the labels and values are the values. This is a convenience wrapper
 * around :js:func:`budgetTransferDivForm`.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Object} options - the options for the select, label to value
 * @param {String} defaultValue - A value to select as the default
 * @param {Boolean} addNone - If true, prepend an option with a value of
 *  "None" and an empty label.
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addLabelToValueSelect = function(id, name, label, options, defaultValue, addNone) {
    var built = [];
    if (addNone === true) {
        built.push({ value: 'None', label: '' });
        if(defaultValue === undefined) { defaultValue = 'None'; }
    }
    Object.keys(options).forEach(function (key) {
        var tmp = { value: options[key], label: key };
        if (defaultValue == options[key]) { tmp.selected = true; }
        built.push(tmp);
    });
    return this.addSelect(id, name, label, built);
};

/**
 * Add an inline radio button set to the form.
 *
 * Options is an Array of Objects, each object having keys ``id``, ``value``
 * and ``label``. Optional keys are ``checked`` (Boolean) and ``onchange``,
 * which will have its value placed literally in the HTML.
 *
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Array} options - the options for the select; array of objects
 *  each having the following attributes:
 * @param {String} options.id - the ID for the option
 * @param {String} options.value - the value for the option
 * @param {String} options.label - the label for the option
 * @param {Boolean} options.checked - whether the option should be checked
 *  by default *(optional; defaults to false)*
 * @param {String} options.inputHtml - extra HTML string to include in the
 *  actual ``input`` element *(optional; defaults to null)*
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addRadioInline = function(name, label, options) {
    this.html += '
' + label + '  ';
    for (var idx in options) {
        var o = options[idx];
        this.html += '' +
            '<input type="radio" name="' + name + '" id="' + o.id + '" value="' + o.value + '"';
        if ('inputHtml' in o) { this.html += ' ' + o.inputHtml; }
        if ('checked' in o && o.checked === true ) { this.html += ' checked'; }
        this.html += '>' + o.label + '';
    }
    this.html += '
\n';
    return this;
};

/**
 * Add a checkbox to the form.
 *
 * @param {String} id - The id of the form element
 * @param {String} name - The name of the form element
 * @param {String} label - The label text for the form element
 * @param {Boolean} checked - Whether to default to checked or not
 * @return {FormBuilder} this
 */
FormBuilder.prototype.addCheckbox = function(id, name, label, checked) {
    this.html += '
' +
        '' +
        '<input type="checkbox" id="' + id + '" name="' + name + '"';
    if (checked === true) { this.html += ' checked'; }
    this.html += '> ' + label + '
\n';
    return this;
};
<|endoftext|>


<commit_before>module.exports = (client, message, args) =>
{
  let member = message.mentions.members.first();
  if(!member)
  {
    return message.reply("Por favor, informe um nome correto!");
  }
  else
  {
    //member.user. (com busca)
    //message.author. (sem busca)
    //client.user. (bot info)
    //member.user.tag
    message.channel.send({embed:{
          color: 3447003,
          author: {  name: member.nickname, icon_url: member.user.avatarURL },
          title: `Informações do usuário.`,  /*url: "http://google.com",*/ description: `${member.user.tag}`,
          fields:
          [
            { name: `Apelido: `, value: `${member.nickname}` /*value: "[link](http://google.com)."*/ },
            { name: `Nome cadastrado: `, value: `${member.user.username}` },
            { name: `Número do ID: `, value: `${member.user.id}` },
            { name: `Conta verificada: `, value: `${member.user.verified}` },
            { name: `Usuário premium: `, value: `${member.user.premium}` },
            { name: `Celular: `, value: `${member.user.mobile}` },
            { name: `BoT: `, value: `${member.user.bot}` },
            { name: `Data de criação: `, value: `${member.user.createdAt}` }
          ],
          timestamp: new Date(), footer: { icon_url: client.user.avatarURL , text: "© Verificado em" }
      }
    }).then(msg => {
      msg.delete(60000)
    });
  }
  message.delete(60000);
};<commit_msg><commit_after>module.exports = (client, message, args) =>
{
  let member = message.mentions.members.first();
  if(!member)
  {
    return message.reply("Por favor, informe um nome correto!");
  }
  else
  {
    //member.user. (com busca)
    //message.author. (sem busca)
    //client.user. (bot info)
    //member.user.tag
    message.channel.send({embed:{
          color: 3447003,
          author: {  name: member.nickname, icon_url: member.user.avatarURL },
          title: `Informações do usuário.`,  /*url: "http://google.com",*/ description: `${member.user.tag}`,
          fields:
          [
            { name: `Apelido: `, value: `${member.nickname}` /*value: "[link](http://google.com)."*/ },
            { name: `Nome cadastrado: `, value: `${member.user.username}` },
            { name: `Número do ID: `, value: `${member.user.id}` },
            { name: `Conta verificada: `, value: `${member.verified}` },
            { name: `Usuário premium: `, value: `${member.premium}` },
            { name: `Celular: `, value: `${member.mobile}` },
            { name: `BoT: `, value: `${member.user.bot}` },
            { name: `Data de criação: `, value: `${member.user.createdAt}` }
          ],
          timestamp: new Date(), footer: { icon_url: client.user.avatarURL , text: "© Verificado em" }
      }
    }).then(msg => {
      msg.delete(60000)
    });
  }
  message.delete(60000);
};<|endoftext|>


<commit_before>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    clean: {
      docs: ['docs']
    },
    ngdocs: {
      options: {
        dest: 'docs',
        html5Mode: false,
        title: 'FamilySearch Javascript SDK',
        bestMatch: false
      },
      all: ['<%= jshint.files %>']
    },
    jshint: {
      files: ['familysearch-javascript-sdk.js'],
      options: {
        jshintrc: '.jshintrc'
      }
    },
    connect: {
      server: {
        options: {
          port: 9000,
          open: true,
          livereload: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>', 'Gruntfile.js', 'index.html'],
      tasks: ['jshint', 'ngdocs'],
      options: {
        livereload: true,
        spawn: false
      }
    }
  });

  // Load the plugin that provides the documentation task
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-ngdocs');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('server', [
    'connect',
    'watch'
  ]);

  grunt.registerTask('docs', [
    'clean:docs',
    'ngdocs'
  ]);

  grunt.registerTask('build', [
    'jshint',
    'docs'
  ]);

  // Default task(s)
  grunt.registerTask('default', ['build']);
};
<commit_msg>clean contents of docs directory but dont delete docs directory<commit_after>module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    clean: {
      docs: ['docs/*']
    },
    ngdocs: {
      options: {
        dest: 'docs',
        html5Mode: false,
        title: 'FamilySearch Javascript SDK',
        bestMatch: false
      },
      all: ['<%= jshint.files %>']
    },
    jshint: {
      files: ['familysearch-javascript-sdk.js'],
      options: {
        jshintrc: '.jshintrc'
      }
    },
    connect: {
      server: {
        options: {
          port: 9000,
          open: true,
          livereload: true
        }
      }
    },
    watch: {
      files: ['<%= jshint.files %>', 'Gruntfile.js', 'index.html'],
      tasks: ['jshint', 'ngdocs'],
      options: {
        livereload: true,
        spawn: false
      }
    }
  });

  // Load the plugin that provides the documentation task
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-ngdocs');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('server', [
    'connect',
    'watch'
  ]);

  grunt.registerTask('docs', [
    'clean:docs',
    'ngdocs'
  ]);

  grunt.registerTask('build', [
    'jshint',
    'docs'
  ]);

  // Default task(s)
  grunt.registerTask('default', ['build']);
};
<|endoftext|>


<commit_before>'use strict';

var request = require('request'),
    shim    = require('browserify-shim');

module.exports = function (grunt) {
  // show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  var reloadPort = 35729, files;

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    compass: {
      dist: {
        options: {
          sassDir: 'public/css',
          cssDir: 'public/css/build'
        }
      }
    },
    browserify2: {
        entry: './public/js/main.js',
        compile: './public/js/build.js',
        beforeHook: function(bundle) {
          shim(bundle, {
            RxJS: {
              path: './public/components/rxjs.lite.js',
              exports: 'rxjs'
            },
          });
        }
      }
    },
    develop: {
      server: {
        file: 'app.js'
      }
    },
    watch: {
      options: {
        nospawn: true,
        livereload: reloadPort
      },
      server: {
        files: [
          'app.js',
          'routes/*.js'
        ],
        tasks: ['develop', 'delayed-livereload']
      },
      js: {
        files: ['public/js/*.js', './Gruntfile.js'],
        options: {
          livereload: reloadPort
        }
      },
      sass: {
        files: ['public/css/**/*.scss'],
        tasks: ['compass'],
        options: {
          livereload: reloadPort
        }
      },
      browserify: {
        files: ['public/js/*.js'],
        tasks: ['browserify2:compile'],
        options: {
          livereload: reloadPort
        }
      },
      views: {
        files: ['views/*.html'],
        options: {
          livereload: reloadPort
        }
      }
    }
  });

  grunt.config.requires('watch.server.files');
  files = grunt.config('watch.server.files');
  files = grunt.file.expand(files);

  grunt.registerTask('delayed-livereload', 'Live reload after the node server has restarted.', function () {
    var done = this.async();
    setTimeout(function () {
      request.get('http://localhost:' + reloadPort + '/changed?files=' + files.join(','),  function (err, res) {
          var reloaded = !err && res.statusCode === 200;
          if (reloaded) {
            grunt.log.ok('Delayed live reload successful.');
          } else {
            grunt.log.error('Unable to make a delayed live reload.');
          }
          done(reloaded);
        });
    }, 500);
  });

  grunt.registerTask('default', ['develop', 'compass', 'browserify2:compile', 'watch']);
};
<commit_msg>bug in gruntfile<commit_after>'use strict';

var request = require('request'),
    shim    = require('browserify-shim');

module.exports = function (grunt) {
  // show elapsed time at the end
  require('time-grunt')(grunt);
  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

  var reloadPort = 35729, files;

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    compass: {
      dist: {
        options: {
          sassDir: 'public/css',
          cssDir: 'public/css/build'
        }
      }
    },
    browserify2: {
      entry: './public/js/main.js',
      compile: './public/js/build.js',
      beforeHook: function(bundle) {
        shim(bundle, {
          RxJS: {
            path: './public/components/rxjs.lite.js',
            exports: 'rxjs'
          },
        });
      }
    },
    develop: {
      server: {
        file: 'app.js'
      }
    },
    watch: {
      options: {
        nospawn: true,
        livereload: reloadPort
      },
      server: {
        files: [
          'app.js',
          'routes/*.js'
        ],
        tasks: ['develop', 'delayed-livereload']
      },
      js: {
        files: ['public/js/*.js', './Gruntfile.js'],
        options: {
          livereload: reloadPort
        }
      },
      sass: {
        files: ['public/css/**/*.scss'],
        tasks: ['compass'],
        options: {
          livereload: reloadPort
        }
      },
      browserify: {
        files: ['public/js/*.js'],
        tasks: ['browserify2:compile'],
        options: {
          livereload: reloadPort
        }
      },
      views: {
        files: ['views/*.html'],
        options: {
          livereload: reloadPort
        }
      }
    }
  });

  grunt.config.requires('watch.server.files');
  files = grunt.config('watch.server.files');
  files = grunt.file.expand(files);

  grunt.registerTask('delayed-livereload', 'Live reload after the node server has restarted.', function () {
    var done = this.async();
    setTimeout(function () {
      request.get('http://localhost:' + reloadPort + '/changed?files=' + files.join(','),  function (err, res) {
          var reloaded = !err && res.statusCode === 200;
          if (reloaded) {
            grunt.log.ok('Delayed live reload successful.');
          } else {
            grunt.log.error('Unable to make a delayed live reload.');
          }
          done(reloaded);
        });
    }, 500);
  });

  grunt.registerTask('default', ['develop', 'compass', 'browserify2:compile', 'watch']);
};
<|endoftext|>


<commit_before>const gulp = require('gulp');
const csso = require('gulp-csso');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const sass = require('gulp-sass');
const plumber = require('gulp-plumber');
const cp = require('child_process');
const imagemin = require('gulp-imagemin');
const browserSync = require('browser-sync');
const del = require('del');
const html2pdf = require('gulp-html2pdf');
const rename = require('gulp-rename');
const sanitize = require('sanitize-filename');
const babel = require('gulp-babel');

const jekyllCommand = (/^win/.test(process.platform)) ? 'jekyll.bat' : 'jekyll';

// Don't even bother logging, its not worth the effort with pdf gen.
const swallowError = function(err) {
	this.emit('end');
};

/*
 * Build the Jekyll Site
 * runs a child process in node that runs the jekyll commands
 */
gulp.task('jekyll-build', function (done) {
	return cp.spawn(jekyllCommand, ['build', 'pdf'], {stdio: 'inherit'})
		.on('close', done);
});

/*
 * Generate pdf of index page
 */
gulp.task('pdf', function () {
	return gulp
        .src('_site/index.html')
		.pipe(plumber())
        .pipe(html2pdf())
        .on('error', swallowError)
		.pipe(rename(sanitize('DavidDudsonCV.pdf')))
		.pipe(gulp.dest('assets/pdf/'));
});

/*
 * Rebuild Jekyll & reload browserSync
 */
gulp.task('jekyll-rebuild', ['jekyll-build'], function () {
	browserSync.reload();
});

/*
 * Build the jekyll site and launch browser-sync
 */
gulp.task('browser-sync', ['jekyll-build'], function() {
	browserSync({
		server: {
			baseDir: '_site'
		}
	});
});

/*
* Compile and minify sass
*/
gulp.task('sass', function() {
  gulp.src('src/styles/**/*.scss')
    .pipe(plumber())
    .pipe(sass())
    .pipe(csso())
    .pipe(gulp.dest('assets/css'));
});

/*
 * Minify images
 */
gulp.task('imagemin', function() {
	gulp.src('src/img/**/*.{jpg,png,gif}')
		.pipe(plumber())
		.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
		.pipe(gulp.dest('assets/img/'));
});

/**
 * Compile and minify js
 */
gulp.task('js', function(){
	gulp.src('src/js/**/*.js')
		.pipe(plumber())
        .pipe(babel({ presets: ['env'] }))
		.pipe(concat('main.js'))
		.pipe(uglify())
		.pipe(gulp.dest('assets/js/'));
});

gulp.task('watch', function() {
  gulp.watch('src/styles/**/*.scss', ['sass', 'reload']);
  gulp.watch('src/js/**/*.js', ['js']);
  gulp.watch('src/img/**/*.{jpg,png,gif}', ['imagemin']);
  gulp.watch(['*html', '_includes/*html', '_layouts/*.html'], ['jekyll-rebuild']);
  gulp.watch(['_data/*.yml', '_includes/*.yml', '_config.yml'], ['jekyll-rebuild']);
});

gulp.task('reload', ['js', 'pdf', 'jekyll-rebuild']);

gulp.task('build', [ 'js', 'sass', 'jekyll-build']);

gulp.task('default', ['build', 'browser-sync', 'watch']);

gulp.task('clean', function () {
    return del([
        'src/**/*',
        'assets/**/*',
        '_data/**/*',
        '_includes/**/*',
        '_layouts/**/*',
        'gulpfile.js',
        '.DS_Store',
        '.travis.yml',
        'package.json'
    ]);
});

gulp.task('deploy', gulp.series('build', 'clean'));
<commit_msg>Migrate to gulp 4<commit_after>const gulp = require('gulp');
const csso = require('gulp-csso');
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
const sass = require('gulp-sass');
const plumber = require('gulp-plumber');
const cp = require('child_process');
const imagemin = require('gulp-imagemin');
const browserSync = require('browser-sync');
const del = require('del');
const html2pdf = require('gulp-html2pdf');
const rename = require('gulp-rename');
const sanitize = require('sanitize-filename');
const babel = require('gulp-babel');

const jekyllCommand = (/^win/.test(process.platform)) ? 'jekyll.bat' : 'jekyll';

// Don't even bother logging, its not worth the effort with pdf gen.
const swallowError = function(err) {
	this.emit('end');
};

/*
 * Build the Jekyll Site
 * runs a child process in node that runs the jekyll commands
 */
gulp.task('jekyll-build', function (done) {
	return cp.spawn(jekyllCommand, ['build', 'pdf'], {stdio: 'inherit'})
		.on('close', done);
});

/*
 * Generate pdf of index page
 */
gulp.task('pdf', function (cb) {
	gulp.src('_site/index.html')
		.pipe(plumber())
        .pipe(html2pdf())
        .on('error', swallowError)
		.pipe(rename(sanitize('DavidDudsonCV.pdf')))
		.pipe(gulp.dest('assets/pdf/'));
	cb()
});

/*
 * Rebuild Jekyll & reload browserSync
 */
gulp.task('jekyll-rebuild', gulp.series('jekyll-build', function () {
	browserSync.reload();
}));

/*
 * Build the jekyll site and launch browser-sync
 */
gulp.task('browser-sync', gulp.series('jekyll-build', function() {
	browserSync({
		server: {
			baseDir: '_site'
		}
	});
}));

/*
* Compile and minify sass
*/
gulp.task('sass', function(cb) {
  gulp.src('src/styles/**/*.scss')
    .pipe(plumber())
    .pipe(sass())
    .pipe(csso())
    .pipe(gulp.dest('assets/css'));
  cb()
});

/*
 * Minify images
 */
gulp.task('imagemin', function() {
	gulp.src('src/img/**/*.{jpg,png,gif}')
		.pipe(plumber())
		.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
		.pipe(gulp.dest('assets/img/'));
});

/**
 * Compile and minify js
 */
gulp.task('js', function(cb) {
	gulp.src('src/js/**/*.js')
		.pipe(plumber())
        .pipe(babel({ presets: ['env'] }))
		.pipe(concat('main.js'))
		.pipe(uglify())
		.pipe(gulp.dest('assets/js/'));
	cb()
});

gulp.task('watch', function(cb) {
  gulp.watch('src/styles/**/*.scss', gulp.series('sass', 'reload'));
  gulp.watch('src/js/**/*.js', gulp.series('js'));
  gulp.watch('src/img/**/*.{jpg,png,gif}', gulp.series('imagemin'));
  gulp.watch(['*html', '_includes/*html', '_layouts/*.html'], gulp.series('jekyll-rebuild'));
  gulp.watch(['_data/*.yml', '_includes/*.yml', '_config.yml'], gulp.series('jekyll-rebuild'));
  cb()
});

gulp.task('reload', gulp.series(gulp.parallel('js', 'pdf'), 'jekyll-rebuild'));

gulp.task('build', gulp.series(gulp.parallel('js', 'sass'), 'jekyll-build'));

gulp.task('default', gulp.series('build', 'browser-sync', 'watch'));

gulp.task('clean', function (cb) {
    del([
        'src/**/*',
        'assets/**/*',
        '_data/**/*',
        '_includes/**/*',
        '_layouts/**/*',
        'gulpfile.js',
        '.DS_Store',
        '.travis.yml',
        'package.json'
    ]);
    cb()
});

const deploy = gulp.series('build', 'clean');

gulp.task('deploy', deploy);
<|endoftext|>


<commit_before>/*

*/

$L = {
  debug: false,
  images: [],
  initialize: function() {

    // disable default Leaflet click interactions
    map.touchZoom.disable();
    map.doubleClickZoom.disable();
    map.scrollWheelZoom.disable();

    this.initialPos = map.getCenter()

    // upload button
    L.easyButton('fa-file-image-o',
      function (){
        $("#inputimage").click();
      },
      'Upload image'
    );

    // file observer
    $(":file").change(function () {
      if (this.files && this.files[0]) {
        var reader = new FileReader();
        reader.onload = $L.imageIsLoaded;
        reader.readAsDataURL(this.files[0]);
      }
    });

  },

  // set up basic behaviors once the image is loaded
  imageIsLoaded: function(e) {
    // default corners
    corners = [300, 200, 500, 200, 300, 400, 500, 400];

    img = new L.DistortableImage(e.target.result, corners);
    img.bringToFront().addTo(map);

  }
}

// Compute the adjugate of m
function adj(m) {
  return [
    m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4],
    m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5],
    m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3]
  ];
}

// multiply two matrices
function multmm(a, b) {
  var c = Array(9);
  for (var i = 0; i != 3; ++i) {
    for (var j = 0; j != 3; ++j) {
      var cij = 0;
      for (var k = 0; k != 3; ++k) {
        cij += a[3*i + k]*b[3*k + j];
      }
      c[3*i + j] = cij;
    }
  }
  return c;
}

// multiply matrix and vector
function multmv(m, v) {
  return [
    m[0]*v[0] + m[1]*v[1] + m[2]*v[2],
    m[3]*v[0] + m[4]*v[1] + m[5]*v[2],
    m[6]*v[0] + m[7]*v[1] + m[8]*v[2]
  ];
}

function pdbg(m, v) {
  var r = multmv(m, v);
  return r + " (" + r[0]/r[2] + ", " + r[1]/r[2] + ")";
}

function basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) {
  var m = [
    x1, x2, x3,
    y1, y2, y3,
    1,  1,  1
  ];
  var v = multmv(adj(m), [x4, y4, 1]);
  return multmm(m, [
    v[0], 0, 0,
    0, v[1], 0,
    0, 0, v[2]
  ]);
}

function general2DProjection(
  x1s, y1s, x1d, y1d,
  x2s, y2s, x2d, y2d,
  x3s, y3s, x3d, y3d,
  x4s, y4s, x4d, y4d
) {
  var s = basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s);
  var d = basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d);
  return multmm(d, adj(s));
}

function project(m, x, y) {
  var v = multmv(m, [x, y, 1]);
  return [v[0]/v[2], v[1]/v[2]];
}

// use CSS to transform the image
function transform2d(elt, x1, y1, x2, y2, x3, y3, x4, y4) {
  var w = elt.offsetWidth, h = elt.offsetHeight;

  var t = general2DProjection(0,  0, x1, y1,
                              w,  0, x2, y2,
                              0,  h, x3, y3,
                              w,  h, x4, y4
  );

  for(i = 0; i != 9; ++i) t[i] = t[i]/t[8];
  t = [t[0], t[3], 0, t[6],
       t[1], t[4], 0, t[7],
       0   , 0   , 1, 0   ,
       t[2], t[5], 0, t[8]];
  t = "matrix3d(" + t.join(", ") + ")";
  elt.style["-webkit-transform"] = t;
  elt.style["-moz-transform"] = t;
  elt.style["-o-transform"] = t;

  var orix = 0, oriy = 0;
  elt.style["-webkit-transform-origin"] = orix+"px "+oriy+"px";
  elt.style["transform-origin"] = orix+"px "+oriy+"px";
  elt.style.transform = t;
}

L.DistortableImage = L.ImageOverlay.extend({
  _initImage: function () {
    this.img = this._image = L.DomUtil.create('img',
    'leaflet-image-layer ' +  'leaflet-zoom-animated');
    this.img.onclick = this.onclick;
    this.img.onselectstart = L.Util.falseFn;
    this.img.onmousemove = L.Util.falseFn;
    this.img.onload = L.bind(this.fire, this, 'load');
    this.img.src = this._url;
    this.img.alt = this.options.alt;
    this.img.id = this.id;

    // enable dragging -- need to debug drag listener
    draggable = new L.Draggable(this._image);
    draggable.enable();

    // need to update points after drag
    // perhaps define points relative to image location, or measure relative offset of drag, and apply to points

    // not triggering
    this.on('drag', function() {console.log('drag',this);this.drag()});

  },

  initialize: function (url, corners, options) {
    // but the element hasn't yet been made
    this.id = 'image-distort-'+$('.image-distort').length

    var bounds = [];
    this.markers  = []
    // go through four corners
    for(var i = 0; i < 8; i = i+2) {
      // convert to lat/lng
      var a = map.layerPointToLatLng([corners[i],corners[i+1]]);
      var marker = new L.ImageMarker([a.lat, a.lng]).addTo(map);
      marker.parentImage = this
      marker.orderId = i
      this.markers.push(marker);
      bounds.push([a.lat,a.lng]);
      var addidclass = marker._icon;
      addidclass.id= "marker"+i+$('.image-distort').length;
      addidclass.className = addidclass.className + " corner";
    }

    // we should switch this to accept lat/lngs
    this.corners = corners;
    this.defaultZoom = map._zoom; // the zoom level at the time the image was created
    this.lastZoom = map._zoom;
    this.opaque = false;
    this.outlined = false;
    this._url = url;
    this._bounds = L.latLngBounds(bounds);// (LatLngBounds, Object)
    this.initialPos = map.latLngToLayerPoint(map.getCenter())
    // tracking pans
    this.offsetX = 0
    this.offsetY = 0

    // weird, but this lets instances of DistorableImage
    // retain the updateTransform() and other methods:
    this.updateTransform = this.updateTransform
    this.updateCorners = this.updateCorners

    for (i in this.markers) {
      this.markers[i].on('drag',this.distort,this);
    }

    // fix distortion post window resize:
    // resize handler isn't passing scope "this"
    //$(window).resize(this.resetInitialPos,this);

    map.on('zoomend', function() {
      this.initialPos = map.latLngToLayerPoint(map.getCenter())
console.log('new iPos',this.initialPos)
      this.updateCorners()
      this.updateTransform()
    },this)

    map.on('zoomstart', function() {
      // record a new offset, accounting for zoom multiplier:
      this.offsetX +=  Math.pow(2,this.lastZoom-map._zoom) *
                     (map.latLngToLayerPoint( map.getCenter()).x
                    - map.latLngToLayerPoint( $L.initialPos).x)
      this.offsetY +=  Math.pow(2,this.lastZoom-map._zoom) *
                     (map.latLngToLayerPoint( map.getCenter()).y
                    - map.latLngToLayerPoint( $L.initialPos).y)
console.log('new os',this.offsetX,this.offsetY,this.lastZoom-map._zoom,Math.pow(2,this.lastZoom-map._zoom))
      this.lastZoom = map._zoom
    },this)

    map.on('drag',function() {
      this.updateCorners()
      this.updateTransform()
    },this)

    L.setOptions(this, options);
  },

  // update the css transform of the image
  // this is all screwed up; it's not even called
  drag: function() {
console.log('drag begin')
    // update all four when dragging whole image
    // then update
    this.updateCorners()
    this.updateTransform()
console.log('drag')
  },

  // remember 'this' gets context of marker, not image
  distort: function() {
    this.updateCorners()
    this.updateTransform()
  },

  // recalc corners (x,y) from markers (lat,lng)
  updateCorners: function() {
    // diff in element position vs. when first initialized;
    // this fixes the transform if you've panned the map,
    // since the element itself moves when panning
    var dx = -(this.initialPos.x-map.latLngToLayerPoint(map.getCenter()).x)
    var dy = -(this.initialPos.y-map.latLngToLayerPoint(map.getCenter()).y)

    // this accounts for offsets due to panning then zooming:
    dx += this.offsetX
    dy += this.offsetY

    this.corners = []
    for(i=0;i<this.markers.length;i++) {
      var pt = map.latLngToContainerPoint(this.markers[i]._latlng)
      this.corners.push(pt.x+dx,pt.y+dy)
    }
    this.debug()
  },

  updateTransform: function() {
    transform2d(this.img,
      this.corners[0],
      this.corners[1],
      this.corners[2],
      this.corners[3],
      this.corners[4],
      this.corners[5],
      this.corners[6],
      this.corners[7]
    );
    this.debug()
  },

  debug: function() {
    if ($L.debug) {
      $('#debugmarkers').show()
      $('#debug-green').css('left',map.latLngToContainerPoint(map.getCenter()).x)
      $('#debug-green').css('width',map.latLngToContainerPoint(map.getCenter()).x-map.latLngToContainerPoint($L.initialPos).x)
      $('#debug-green').css('top',map.latLngToContainerPoint(map.getCenter()).y)
      $('#debug-green').css('height',map.latLngToContainerPoint(map.getCenter()).y-map.latLngToContainerPoint($L.initialPos).y)

      $('#debug-green').css('right',map.latLngToContainerPoint(this._bounds._southWest).y)
      $('#debugb').css('left',map.latLngToContainerPoint(this._bounds._southWest).x)
      $('#debugb').css('top',map.latLngToContainerPoint(this._bounds._northEast).y)
      $('#debugb').css('width',map.latLngToContainerPoint(this._bounds._northEast).x-map.latLngToContainerPoint(this._bounds._southWest).x)
      $('#debugb').css('height',map.latLngToContainerPoint(this._bounds._southWest).y-map.latLngToContainerPoint(this._bounds._northEast).y)
      $('#debug').css('left',this.initialPos.x)
      $('#debug').css('top',this.initialPos.y)
      for (i=0;i<4;i++) {
        $('#debug'+i).css('left',this.corners[2*i])
        $('#debug'+i).css('top',this.corners[2*i+1])
      }
    }
  },

  toggleOutline: function() {
    this.outlined = !this.outlined;
    if (this.outlined) {
      this.img.setOpacity(0.2);
      this.img.css('border','2px solid black');
    } else {
      this.img.setOpacity(1);
      this.img.css('border', '');
    }
  },

  onclick: function(e) {
    // first, delete existing buttons
    $('#image-distort-transparency').parent().remove();
    $('#image-distort-outline').parent().remove();
    $('#image-distort-delete').parent().remove();

    this.transparencyBtn = L.easyButton('fa-adjust',
       function () {
         var e = $('#'+$('#image-distort-outline')[0].getAttribute('parentImgId'))[0]
         if (e.opacity == 1) {
           L.setOpacity(e,0.7);
           e.setAttribute('opacity',0.7);
         } else {
           L.setOpacity(e,1);
           e.setAttribute('opacity',1);
         }
       },
      'Toggle Image Transparency'
    ).getContainer()//.children[0]
    this.transparencyBtn.id = 'image-distort-outline';
    this.transparencyBtn.setAttribute('parentImgId',this.id)

    this.outlineBtn = L.easyButton('fa-square-o',
      function () {
        outline();
      },
      'Outline'
    ).getContainer().children[0]
    this.outlineBtn.id = 'image-distort-outline';
    this.outlineBtn.setAttribute('parentImgId',this.id)

    this.deleteBtn = L.easyButton('fa-bitbucket',
      function () {
        map.removeLayer($(this.parentImgId));
        for(var i=0; i<4; i++)
        map.removeLayer(this.markers[i]);
      },
     'Delete Image'
    ).getContainer().children[0]
    this.deleteBtn.id = 'image-distort-delete';
    this.deleteBtn.setAttribute('parentImgId',this.id)
  }

})

L.ImageMarker = L.Marker.extend({
  options: {
    pane: 'markerPane',
    icon: new L.Icon({iconUrl:'imagedistort/images/imarker.png'}),
    // title: '',
    // alt: '',
    clickable: true,
    draggable: true, // this causes an error. Why?
    keyboard: true,
    zIndexOffset: 0,
    opacity: 1,
    riseOnHover: true,
    riseOffset: 250
  }
});

<commit_msg>map pan, distort, zoom working reliably<commit_after>/*

*/

$L = {
  debug: false,
  images: [],
  initialize: function() {

    // disable default Leaflet click interactions
    map.touchZoom.disable();
    map.doubleClickZoom.disable();
    map.scrollWheelZoom.disable();

    this.initialPos = map.getCenter()

    // upload button
    L.easyButton('fa-file-image-o',
      function (){
        $("#inputimage").click();
      },
      'Upload image'
    );

    // file observer
    $(":file").change(function () {
      if (this.files && this.files[0]) {
        var reader = new FileReader();
        reader.onload = $L.imageIsLoaded;
        reader.readAsDataURL(this.files[0]);
      }
    });

  },

  // set up basic behaviors once the image is loaded
  imageIsLoaded: function(e) {
    // default corners
    corners = [300, 200, 500, 200, 300, 400, 500, 400];

    img = new L.DistortableImage(e.target.result, corners);
    img.bringToFront().addTo(map);

  }
}

// Compute the adjugate of m
function adj(m) {
  return [
    m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4],
    m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5],
    m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3]
  ];
}

// multiply two matrices
function multmm(a, b) {
  var c = Array(9);
  for (var i = 0; i != 3; ++i) {
    for (var j = 0; j != 3; ++j) {
      var cij = 0;
      for (var k = 0; k != 3; ++k) {
        cij += a[3*i + k]*b[3*k + j];
      }
      c[3*i + j] = cij;
    }
  }
  return c;
}

// multiply matrix and vector
function multmv(m, v) {
  return [
    m[0]*v[0] + m[1]*v[1] + m[2]*v[2],
    m[3]*v[0] + m[4]*v[1] + m[5]*v[2],
    m[6]*v[0] + m[7]*v[1] + m[8]*v[2]
  ];
}

function pdbg(m, v) {
  var r = multmv(m, v);
  return r + " (" + r[0]/r[2] + ", " + r[1]/r[2] + ")";
}

function basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) {
  var m = [
    x1, x2, x3,
    y1, y2, y3,
    1,  1,  1
  ];
  var v = multmv(adj(m), [x4, y4, 1]);
  return multmm(m, [
    v[0], 0, 0,
    0, v[1], 0,
    0, 0, v[2]
  ]);
}

function general2DProjection(
  x1s, y1s, x1d, y1d,
  x2s, y2s, x2d, y2d,
  x3s, y3s, x3d, y3d,
  x4s, y4s, x4d, y4d
) {
  var s = basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s);
  var d = basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d);
  return multmm(d, adj(s));
}

function project(m, x, y) {
  var v = multmv(m, [x, y, 1]);
  return [v[0]/v[2], v[1]/v[2]];
}

// use CSS to transform the image
function transform2d(elt, x1, y1, x2, y2, x3, y3, x4, y4) {
  var w = elt.offsetWidth, h = elt.offsetHeight;

  var t = general2DProjection(0,  0, x1, y1,
                              w,  0, x2, y2,
                              0,  h, x3, y3,
                              w,  h, x4, y4
  );

  for(i = 0; i != 9; ++i) t[i] = t[i]/t[8];
  t = [t[0], t[3], 0, t[6],
       t[1], t[4], 0, t[7],
       0   , 0   , 1, 0   ,
       t[2], t[5], 0, t[8]];
  t = "matrix3d(" + t.join(", ") + ")";
  elt.style["-webkit-transform"] = t;
  elt.style["-moz-transform"] = t;
  elt.style["-o-transform"] = t;

  var orix = 0, oriy = 0;
  elt.style["-webkit-transform-origin"] = orix+"px "+oriy+"px";
  elt.style["transform-origin"] = orix+"px "+oriy+"px";
  elt.style.transform = t;
}

L.DistortableImage = L.ImageOverlay.extend({
  _initImage: function () {
    this.img = this._image = L.DomUtil.create('img',
    'leaflet-image-layer ' +  'leaflet-zoom-animated');
    this.img.onclick = this.onclick;
    this.img.onselectstart = L.Util.falseFn;
    this.img.onmousemove = L.Util.falseFn;
    this.img.onload = L.bind(this.fire, this, 'load');
    this.img.src = this._url;
    this.img.alt = this.options.alt;
    this.img.id = this.id;

    // enable dragging -- need to debug drag listener
    draggable = new L.Draggable(this._image);
    draggable.enable();

    // need to update points after drag
    // perhaps define points relative to image location, or measure relative offset of drag, and apply to points

    // not triggering
    this.on('drag', function() {console.log('drag',this);this.drag()});

  },

  initialize: function (url, corners, options) {
    // but the element hasn't yet been made
    this.id = 'image-distort-'+$('.image-distort').length

    var bounds = [];
    this.markers  = []
    // go through four corners
    for(var i = 0; i < 8; i = i+2) {
      // convert to lat/lng
      var a = map.layerPointToLatLng([corners[i],corners[i+1]]);
      var marker = new L.ImageMarker([a.lat, a.lng]).addTo(map);
      marker.parentImage = this
      marker.orderId = i
      this.markers.push(marker);
      bounds.push([a.lat,a.lng]);
      var addidclass = marker._icon;
      addidclass.id= "marker"+i+$('.image-distort').length;
      addidclass.className = addidclass.className + " corner";
    }

    // we should switch this to accept lat/lngs
    this.corners = corners;
    this.defaultZoom = map._zoom; // the zoom level at the time the image was created
    this.lastZoom = map._zoom;
    this.opaque = false;
    this.outlined = false;
    this._url = url;
    this._bounds = L.latLngBounds(bounds);// (LatLngBounds, Object)
    this.initialPos = map.latLngToLayerPoint(map.getCenter())
    // tracking pans
    this.offsetX = 0
    this.offsetY = 0

    // weird, but this lets instances of DistorableImage
    // retain the updateTransform() and other methods:
    this.updateTransform = this.updateTransform
    this.updateCorners = this.updateCorners

    for (i in this.markers) {
      this.markers[i].on('drag',this.distort,this);
    }

    // fix distortion post window resize:
    // resize handler isn't passing scope "this"
    //$(window).resize(this.resetInitialPos,this);

    map.on('zoomend', function() {
      this.initialPos = map.latLngToLayerPoint(map.getCenter())
      this.updateCorners()
      this.updateTransform()
    },this)

    map.on('zoomstart', function() {
      // add a new offset, accounting for zoom multiplier:
      this.offsetX += (map.latLngToLayerPoint( map.getCenter()).x
                    - this.initialPos.x)
      this.offsetY += (map.latLngToLayerPoint( map.getCenter()).y
                    - this.initialPos.y)
      this.lastZoom = map._zoom
    },this)

    map.on('drag',function() {
      this.updateCorners()
      this.updateTransform()
    },this)

    L.setOptions(this, options);
  },

  // update the css transform of the image
  // this is all screwed up; it's not even called
  drag: function() {
console.log('drag begin')
    // update all four when dragging whole image
    // then update
    this.updateCorners()
    this.updateTransform()
console.log('drag')
  },

  // remember 'this' gets context of marker, not image
  distort: function() {
    this.updateCorners()
    this.updateTransform()
  },

  // recalc corners (x,y) from markers (lat,lng)
  updateCorners: function() {
    // diff in element position vs. when first initialized;
    // this fixes the transform if you've panned the map,
    // since the element itself moves when panning
    var dx = -(this.initialPos.x-map.latLngToLayerPoint(map.getCenter()).x)
    var dy = -(this.initialPos.y-map.latLngToLayerPoint(map.getCenter()).y)

    // this accounts for offsets due to panning then zooming:
    dx += this.offsetX
    dy += this.offsetY

    this.corners = []
    for(i=0;i<this.markers.length;i++) {
      var pt = map.latLngToContainerPoint(this.markers[i]._latlng)
      this.corners.push(pt.x+dx,pt.y+dy)
    }
    this.debug()
  },

  updateTransform: function() {
    transform2d(this.img,
      this.corners[0],
      this.corners[1],
      this.corners[2],
      this.corners[3],
      this.corners[4],
      this.corners[5],
      this.corners[6],
      this.corners[7]
    );
    this.debug()
  },

  debug: function() {
    if ($L.debug) {
      $('#debugmarkers').show()
      $('#debug-green').css('left',map.latLngToContainerPoint(map.getCenter()).x)
      $('#debug-green').css('width',map.latLngToContainerPoint(map.getCenter()).x-map.latLngToContainerPoint($L.initialPos).x)
      $('#debug-green').css('top',map.latLngToContainerPoint(map.getCenter()).y)
      $('#debug-green').css('height',map.latLngToContainerPoint(map.getCenter()).y-map.latLngToContainerPoint($L.initialPos).y)

      $('#debug-green').css('right',map.latLngToContainerPoint(this._bounds._southWest).y)
      $('#debugb').css('left',map.latLngToContainerPoint(this._bounds._southWest).x)
      $('#debugb').css('top',map.latLngToContainerPoint(this._bounds._northEast).y)
      $('#debugb').css('width',map.latLngToContainerPoint(this._bounds._northEast).x-map.latLngToContainerPoint(this._bounds._southWest).x)
      $('#debugb').css('height',map.latLngToContainerPoint(this._bounds._southWest).y-map.latLngToContainerPoint(this._bounds._northEast).y)
      $('#debug').css('left',this.initialPos.x)
      $('#debug').css('top',this.initialPos.y)
      for (i=0;i<4;i++) {
        $('#debug'+i).css('left',this.corners[2*i])
        $('#debug'+i).css('top',this.corners[2*i+1])
      }
    }
  },

  toggleOutline: function() {
    this.outlined = !this.outlined;
    if (this.outlined) {
      this.img.setOpacity(0.2);
      this.img.css('border','2px solid black');
    } else {
      this.img.setOpacity(1);
      this.img.css('border', '');
    }
  },

  onclick: function(e) {
    // first, delete existing buttons
    $('#image-distort-transparency').parent().remove();
    $('#image-distort-outline').parent().remove();
    $('#image-distort-delete').parent().remove();

    this.transparencyBtn = L.easyButton('fa-adjust',
       function () {
         var e = $('#'+$('#image-distort-outline')[0].getAttribute('parentImgId'))[0]
         if (e.opacity == 1) {
           L.setOpacity(e,0.7);
           e.setAttribute('opacity',0.7);
         } else {
           L.setOpacity(e,1);
           e.setAttribute('opacity',1);
         }
       },
      'Toggle Image Transparency'
    ).getContainer()//.children[0]
    this.transparencyBtn.id = 'image-distort-outline';
    this.transparencyBtn.setAttribute('parentImgId',this.id)

    this.outlineBtn = L.easyButton('fa-square-o',
      function () {
        outline();
      },
      'Outline'
    ).getContainer().children[0]
    this.outlineBtn.id = 'image-distort-outline';
    this.outlineBtn.setAttribute('parentImgId',this.id)

    this.deleteBtn = L.easyButton('fa-bitbucket',
      function () {
        map.removeLayer($(this.parentImgId));
        for(var i=0; i<4; i++)
        map.removeLayer(this.markers[i]);
      },
     'Delete Image'
    ).getContainer().children[0]
    this.deleteBtn.id = 'image-distort-delete';
    this.deleteBtn.setAttribute('parentImgId',this.id)
  }

})

L.ImageMarker = L.Marker.extend({
  options: {
    pane: 'markerPane',
    icon: new L.Icon({iconUrl:'imagedistort/images/imarker.png'}),
    // title: '',
    // alt: '',
    clickable: true,
    draggable: true, // this causes an error. Why?
    keyboard: true,
    zIndexOffset: 0,
    opacity: 1,
    riseOnHover: true,
    riseOffset: 250
  }
});

<|endoftext|>


<commit_before>/***
 * Copyright (c) 2015 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * Custom service worker tasks and grunt config.
 * Generates service worker scripts.
 * Relies on nconfig and webpack tasks.
 */
 /*jshint multistr: true */
 /* global global */
'use strict';

var swPrecache = require('sw-precache');
var fs = require('fs');

module.exports = function (grunt) {
  grunt.config('_service_worker', {
    options: {
      // options for sw-precache
      cacheId: '<%= pkg.name %>',
      serviceWorkerScript: '<%= project.src.serviceWorker.precache %>',
      directoryIndex: false,
      stripPrefix: '<%= project.dist.baseDir %>',
      replacePrefix: '<%= project.web.baseDir %>',
      staticFileGlobs: [
        '<%= project.dist.fonts %>/**.*',
        '<%= project.dist.images %>/**.*',
        '<%= project.dist.scripts %>/!(header|inline).*'
      ],
      verbose: true,

      // options for service worker captureData
      captureData: {
        output: '<%= project.src.serviceWorker.data %>',
        assets: [{
          file: '<%= project.src.styles %>/_fonts.scss',
          captures: [{
            global: true,
            matchIndex: 1,
            re: /url\(([^\)]+)\)/ig
          }]
        }],
        api_gets: [{
          file: 'app.js',
          captures: [{
            global: false,
            matchIndex: 1,
            re: /xhrPath\s*\:\s*(?:'|")([^'"]+)/
          }]
        }]
      }
    },
    dev: {
      options: {
        debug: true,
        handleFetch: false
      }
    },
    prod: {
      options: {
        handleFetch: true,
        staticFileGlobsAddons: [
          '<%= project.web.assets.mainScript() %>'
        ]
      }
    }
  });

  /**
   * Process data captures for _service_worker
   * Data captures are data and asset references that only exist in source files.
   * (like web font urls, for example)
   *
   * Writes output file to export capture file data to the service worker.
   *
   * @private
   */
  function captureData (options) {
    var output = {
      debug: options.debug ? true : false,
      cacheId: options.cacheId,
      assets: [],
      api_gets: []
    };
    var reClean = /^(?:\s+|"|')|(?:\s+|"|')$/g;
    var replacement = 'DATA';
    var template = '/** This is a generated file **/\n\
module.exports = JSON.parse(JSON.stringify(\n' + replacement + '\n));';

    /**
     * Report output results
     */
    function report (output, name) {
      if (output.length > 0) {
        output.forEach(function (str) {
          grunt.log.writeln('captureData ' + name + ': '+str);
        });
      } else {
        grunt.log.error('captureData captured no ' + name);
      }
    }

    options = options.captureData;

    if (options) {
      // build output.assets, output.api_gets from respective options
      ['assets', 'api_gets'].forEach(function (item) {
        options[item].forEach(function (input) {
          input.contents = fs.readFileSync(input.file, { encoding: 'utf8' });

          input.captures.forEach(function (capSpec) {
            if (capSpec.global) {
              while ( (capSpec.m = capSpec.re.exec(input.contents)) !== null ) {
                output[item].push(capSpec.m[capSpec.matchIndex]
                  .replace(reClean, ''));
              }
            } else {
              capSpec.m = capSpec.re.exec(input.contents);
              if (capSpec.m) {
                output[item].push(capSpec.m[capSpec.matchIndex]
                  .replace(reClean, ''));
              }
            }
          });
        });
        report(output[item], item);
      });

      fs.writeFileSync(options.output, template.replace(replacement, JSON.stringify(
        output
      )));
    } else {
      grunt.fail.fatal('captureData received no input options');
    }
  }

  /**
   * Custom task to generate dynamic service worker scripts.
   * Must be run from service-worker task.
   *
   * @access private
   */
  grunt.registerMultiTask('_service_worker', function () {
    var done = this.async();
    var options = this.options();

    // capture paths, out-of-project assets and produce data.js
    captureData(options);
    delete options.captureData;

    // produce precache.js
    options.logger = grunt.log.writeln;
    if (options.staticFileGlobsAddons) {
      options.staticFileGlobs = options.staticFileGlobs.concat(options.staticFileGlobsAddons);
    }
    swPrecache.write(options.serviceWorkerScript, options, done);
  });

  /**
   * Custom task to generate the service worker script.
   * Runs nconfig before _service_worker subtask.
   * target must be dev or prod:
   *  service-worker:dev | service-worker:prod
   *
   * @access public
   */
  grunt.registerTask('service-worker', function () {
    var target = this.args.shift();
    var tasks = global._nconfig ? [] : ['nconfig:'+target];

    tasks = tasks.concat([
      'webpack:swReg-'+target,
      '_service_worker:'+target,
      'webpack:sw-'+target
    ]);

    grunt.task.run(tasks);
  });
};
<commit_msg>leave prod debug flag on until further notice<commit_after>/***
 * Copyright (c) 2015 Alex Grant (@localnerve), LocalNerve LLC
 * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms.
 *
 * Custom service worker tasks and grunt config.
 * Generates service worker scripts.
 * Relies on nconfig and webpack tasks.
 */
 /*jshint multistr: true */
 /* global global */
'use strict';

var swPrecache = require('sw-precache');
var fs = require('fs');

module.exports = function (grunt) {
  grunt.config('_service_worker', {
    options: {
      // options for sw-precache
      cacheId: '<%= pkg.name %>',
      serviceWorkerScript: '<%= project.src.serviceWorker.precache %>',
      directoryIndex: false,
      stripPrefix: '<%= project.dist.baseDir %>',
      replacePrefix: '<%= project.web.baseDir %>',
      staticFileGlobs: [
        '<%= project.dist.fonts %>/**.*',
        '<%= project.dist.images %>/**.*',
        '<%= project.dist.scripts %>/!(header|inline).*'
      ],
      verbose: true,

      // options for service worker captureData
      captureData: {
        output: '<%= project.src.serviceWorker.data %>',
        assets: [{
          file: '<%= project.src.styles %>/_fonts.scss',
          captures: [{
            global: true,
            matchIndex: 1,
            re: /url\(([^\)]+)\)/ig
          }]
        }],
        api_gets: [{
          file: 'app.js',
          captures: [{
            global: false,
            matchIndex: 1,
            re: /xhrPath\s*\:\s*(?:'|")([^'"]+)/
          }]
        }]
      }
    },
    dev: {
      options: {
        debug: true,
        handleFetch: false
      }
    },
    prod: {
      options: {
        debug: true,
        handleFetch: true,
        staticFileGlobsAddons: [
          '<%= project.web.assets.mainScript() %>'
        ]
      }
    }
  });

  /**
   * Process data captures for _service_worker
   * Data captures are data and asset references that only exist in source files.
   * (like web font urls, for example)
   *
   * Writes output file to export capture file data to the service worker.
   *
   * @private
   */
  function captureData (options) {
    var output = {
      debug: options.debug ? true : false,
      cacheId: options.cacheId,
      assets: [],
      api_gets: []
    };
    var reClean = /^(?:\s+|"|')|(?:\s+|"|')$/g;
    var replacement = 'DATA';
    var template = '/** This is a generated file **/\n\
module.exports = JSON.parse(JSON.stringify(\n' + replacement + '\n));';

    /**
     * Report output results
     */
    function report (output, name) {
      if (output.length > 0) {
        output.forEach(function (str) {
          grunt.log.writeln('captureData ' + name + ': '+str);
        });
      } else {
        grunt.log.error('captureData captured no ' + name);
      }
    }

    options = options.captureData;

    if (options) {
      // build output.assets, output.api_gets from respective options
      ['assets', 'api_gets'].forEach(function (item) {
        options[item].forEach(function (input) {
          input.contents = fs.readFileSync(input.file, { encoding: 'utf8' });

          input.captures.forEach(function (capSpec) {
            if (capSpec.global) {
              while ( (capSpec.m = capSpec.re.exec(input.contents)) !== null ) {
                output[item].push(capSpec.m[capSpec.matchIndex]
                  .replace(reClean, ''));
              }
            } else {
              capSpec.m = capSpec.re.exec(input.contents);
              if (capSpec.m) {
                output[item].push(capSpec.m[capSpec.matchIndex]
                  .replace(reClean, ''));
              }
            }
          });
        });
        report(output[item], item);
      });

      fs.writeFileSync(options.output, template.replace(replacement, JSON.stringify(
        output
      )));
    } else {
      grunt.fail.fatal('captureData received no input options');
    }
  }

  /**
   * Custom task to generate dynamic service worker scripts.
   * Must be run from service-worker task.
   *
   * @access private
   */
  grunt.registerMultiTask('_service_worker', function () {
    var done = this.async();
    var options = this.options();

    // capture paths, out-of-project assets and produce data.js
    captureData(options);
    delete options.captureData;

    // produce precache.js
    options.logger = grunt.log.writeln;
    if (options.staticFileGlobsAddons) {
      options.staticFileGlobs = options.staticFileGlobs.concat(options.staticFileGlobsAddons);
    }
    swPrecache.write(options.serviceWorkerScript, options, done);
  });

  /**
   * Custom task to generate the service worker script.
   * Runs nconfig before _service_worker subtask.
   * target must be dev or prod:
   *  service-worker:dev | service-worker:prod
   *
   * @access public
   */
  grunt.registerTask('service-worker', function () {
    var target = this.args.shift();
    var tasks = global._nconfig ? [] : ['nconfig:'+target];

    tasks = tasks.concat([
      'webpack:swReg-'+target,
      '_service_worker:'+target,
      'webpack:sw-'+target
    ]);

    grunt.task.run(tasks);
  });
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class IconButton extends Component {
  handleMouseUp = event => {
    this.buttonNode.blur();
    if (this.props.onMouseUp) {
      this.props.onMouseUp(event);
    }
  };

  handleMouseLeave = event => {
    this.buttonNode.blur();
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
  };

  render() {
    const { children, className, disabled, element, href, icon, size, color, type, ...others } = this.props;

    const classNames = cx(
      theme['button'],
      theme['icon-button'],
      theme[color],
      {
        [theme[size]]: theme[size],
      },
      className,
    );

    const props = {
      ...others,
      href,
      ref: node => {
        this.buttonNode = node;
      },
      className: classNames,
      disabled,
      onMouseUp: this.handleMouseUp,
      onMouseLeave: this.handleMouseLeave,
      type: !href ? type : null,
      'data-teamleader-ui': 'button',
    };

    return React.createElement(element, props, icon, children);
  }
}

IconButton.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
  disabled: PropTypes.bool,
  /** A custom element to be rendered */
  element: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
  href: PropTypes.string,
  icon: PropTypes.element,
  onMouseLeave: PropTypes.func,
  onMouseUp: PropTypes.func,
  size: PropTypes.oneOf(['small', 'medium']),
  color: PropTypes.oneOf(['neutral', 'white', 'mint', 'violet', 'ruby', 'gold', 'aqua']),
  type: PropTypes.string,
};

IconButton.defaultProps = {
  className: '',
  element: 'button',
  size: 'medium',
  color: 'neutral',
  type: 'button',
};

export default IconButton;
<commit_msg>Only render 'type' attribute when element is a button<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import theme from './theme.css';

class IconButton extends Component {
  handleMouseUp = event => {
    this.buttonNode.blur();
    if (this.props.onMouseUp) {
      this.props.onMouseUp(event);
    }
  };

  handleMouseLeave = event => {
    this.buttonNode.blur();
    if (this.props.onMouseLeave) {
      this.props.onMouseLeave(event);
    }
  };

  render() {
    const { children, className, disabled, element, href, icon, size, color, type, ...others } = this.props;

    const classNames = cx(
      theme['button'],
      theme['icon-button'],
      theme[color],
      {
        [theme[size]]: theme[size],
      },
      className,
    );

    const props = {
      ...others,
      href,
      ref: node => {
        this.buttonNode = node;
      },
      className: classNames,
      disabled,
      onMouseUp: this.handleMouseUp,
      onMouseLeave: this.handleMouseLeave,
      type: element === 'button' ? type : null,
      'data-teamleader-ui': 'button',
    };

    return React.createElement(element, props, icon, children);
  }
}

IconButton.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
  disabled: PropTypes.bool,
  /** A custom element to be rendered */
  element: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
  href: PropTypes.string,
  icon: PropTypes.element,
  onMouseLeave: PropTypes.func,
  onMouseUp: PropTypes.func,
  size: PropTypes.oneOf(['small', 'medium']),
  color: PropTypes.oneOf(['neutral', 'white', 'mint', 'violet', 'ruby', 'gold', 'aqua']),
  type: PropTypes.string,
};

IconButton.defaultProps = {
  className: '',
  element: 'button',
  size: 'medium',
  color: 'neutral',
  type: 'button',
};

export default IconButton;
<|endoftext|>


<commit_before>console.clear();

import PlaygroundRenderer from './playground-renderer';
import './styles.less'
import { tracker } from './tracker';
import * as t from './transformer';

if (localStorage.getItem('debug') !== null) {
	let debug = (localStorage.getItem('debug') || false) === 'true';
	document.querySelector('#background').className = (debug) ? "show-webcam" :"hide-webcam";
}

let playgroundRender = new PlaygroundRenderer();
document.querySelector('.container')
    .appendChild(playgroundRender);

playgroundRender.subscribeTo(tracker);
<commit_msg>minor fix for debug mode<commit_after>console.clear();

import PlaygroundRenderer from './playground-renderer';
import './styles.less'
import { tracker } from './tracker';
import * as t from './transformer';

if (localStorage.getItem('debug') !== null) {
	let debug = localStorage.getItem('debug') === 'true';
	document.querySelector('#background').className = (debug) ? "show-webcam" :"hide-webcam";
}

let playgroundRender = new PlaygroundRenderer();
document.querySelector('.container')
    .appendChild(playgroundRender);

playgroundRender.subscribeTo(tracker);
<|endoftext|>


<commit_before>'use strict';

//create a file-specific context via a function
(function(Piv, ElectionId) {
    var View = Piv.view

    var headerStyle = {"class": "font-size-1"}

    // page layout
    Piv.html(View.workspace, "h1", "Snapshots", headerStyle)
    var SnapshotsDiv = Piv.div(View.workspace, "Snapshots", "text1")
    Piv.html(View.workspace, "h1", "Candidates", headerStyle)
    var CandidatesDiv = Piv.div(View.workspace, "Candidates", "text1")
    Piv.html(View.workspace, "h1", "Ballots", headerStyle)
    var BallotsDiv = Piv.div(View.workspace, "Ballots", "text1")
    Piv.html(View.workspace, "h1", "Tie-Breaker (Partial Order)", headerStyle)
    var TiePartialDiv = Piv.div(View.workspace, "TiePartial", "text1")
    Piv.html(View.workspace, "h1", "Tie-Breaker (Total Order)", headerStyle)
    var TieTotalDiv = Piv.div(View.workspace, "TieTotal", "text1")
    Piv.html(View.workspace, "h1", "Results", headerStyle)
    var ResultsDiv = Piv.div(View.workspace, "Results", "text1")
    Piv.html(View.workspace, "h1", "Plot", headerStyle)
    var PlotDiv = Piv.div(View.workspace, "Plot", "plot_area text1")

    function main() {
        // generic setup
        View.setHeader("Election Debugger", ElectionId)
        View.statusbar.innerHTML = ""
        Piv.electionsMenu(View.sidenav, ElectionId)
        Piv.removeHrefsForCurrentLoc()

        // start workflow
        getSnapshots()
    }

    function getSnapshots() {
        // fetch snapshots
        Piv.http.get(["/api/elections/" + ElectionId + "/result_snapshots"],
                     gotSnapshots, showErrorMessage)
    }

    function gotSnapshots(snapshots) {
        snapshots.forEach(snapshot => {
            var button = piv.html(SnapshotsDiv, "button", snapshot.id)
            button.onclick = function() {
                getSnapshot(snapshot.id)
            }
        })
    }

    function getSnapshot(SnapshotId) {
        Piv.http.get(["/api/elections/" + ElectionId + "/result_snapshots/" + SnapshotId],
                     gotSnapshot, showErrorMessage)
    }

    function gotSnapshot(snapshot) {
        if (snapshot.format_version < 3) {
            alert("You can only debug snapshots with version 3 or greater.  This one was version " + snapshot.format_version + ".")
            return
        }
        console.log(snapshot)
	var debug = snapshot.result_blob.debug
	var debug_private = snapshot.result_blob.debug_private

	// candidates
        CandidatesDiv.innerHTML = ""
        debug_private.candidates.forEach(candidate => {
            piv.html(CandidatesDiv, "span", candidate.id + ": " + candidate.name + "
")
        })

	// ballots
        BallotsDiv.innerHTML = ""
	var ballots = debug.ballots
        Object.keys(ballots).forEach(elector_id => {
	    var ballot_text = ballots[elector_id]
            piv.html(BallotsDiv, "span", ballot_text + "
")
        })

	// tie breaker, partial and total order
	TiePartialDiv.innerHTML = debug.tie_breaker
	TieTotalDiv.innerHTML = debug.tie_breaker_total

	// results
	var ranks = Array.from(snapshot.result_blob.order.keys())
	console.log(ranks)
	ranks.sort()
	var winners = ranks.map(rank => snapshot.result_blob.order[rank].id)
	ResultsDiv.innerHTML = winners.join(">")

	// plotting
	showPlot()
    }

    function showPlot() {
	PlotDiv.innerHTML = ""
	var AlchemyDiv = Piv.div(PlotDiv, "alchemy")

	var graph_data = {
	    "nodes": [
		{
		    "id": 0,
		    "caption": "TO",
		},
		{
		    "id": 1,
		    "caption": "FROM",
		},
	    ],
	    "edges": [
		{
		    "source": 1,
		    "target": 0,
		},
	    ]
	}

	var config = {
            dataSource: graph_data,

            directedEdges: true,
            backgroundColour: "white",
            nodeCaptionsOnByDefault: true,

            edgeStyle: {"all":{
		"color": "black",
		"opacity": 1,
		"width": 2,
            }},

            nodeStyle: {"all":{
		"borderColor": "black",
		"borderWidth": 2,
		"color": "white",
		"radius": 30,
            }},
	};

	var alchemy = new Alchemy(config)
    }

    function showErrorMessage(error) {
        alert(error)
        Piv.div(View.workspace, "", "w100 text3", "Debug for this election are not currently available.")
        if (!error) return
        Piv.div(View.workspace, "", "100 text3", error.response.data.message)
    }

    main(piv, election)
})(piv, election)
<commit_msg>plot actual graph from ballots<commit_after>'use strict';

//create a file-specific context via a function
(function(Piv, ElectionId) {
    var View = Piv.view

    var headerStyle = {"class": "font-size-1"}

    // page layout
    Piv.html(View.workspace, "h1", "Snapshots", headerStyle)
    var SnapshotsDiv = Piv.div(View.workspace, "Snapshots", "text1")
    Piv.html(View.workspace, "h1", "Candidates", headerStyle)
    var CandidatesDiv = Piv.div(View.workspace, "Candidates", "text1")
    Piv.html(View.workspace, "h1", "Ballots", headerStyle)
    var BallotsDiv = Piv.div(View.workspace, "Ballots", "text1")
    Piv.html(View.workspace, "h1", "Tie-Breaker (Partial Order)", headerStyle)
    var TiePartialDiv = Piv.div(View.workspace, "TiePartial", "text1")
    Piv.html(View.workspace, "h1", "Tie-Breaker (Total Order)", headerStyle)
    var TieTotalDiv = Piv.div(View.workspace, "TieTotal", "text1")
    Piv.html(View.workspace, "h1", "Results", headerStyle)
    var ResultsDiv = Piv.div(View.workspace, "Results", "text1")
    Piv.html(View.workspace, "h1", "Plot", headerStyle)
    var PlotDiv = Piv.div(View.workspace, "Plot", "plot_area text1")

    function main() {
        // generic setup
        View.setHeader("Election Debugger", ElectionId)
        View.statusbar.innerHTML = ""
        Piv.electionsMenu(View.sidenav, ElectionId)
        Piv.removeHrefsForCurrentLoc()

        // start workflow
        getSnapshots()
    }

    function getSnapshots() {
        // fetch snapshots
        Piv.http.get(["/api/elections/" + ElectionId + "/result_snapshots"],
                     gotSnapshots, showErrorMessage)
    }

    function gotSnapshots(snapshots) {
        snapshots.forEach(snapshot => {
            var button = piv.html(SnapshotsDiv, "button", snapshot.id)
            button.onclick = function() {
                getSnapshot(snapshot.id)
            }
        })
    }

    function getSnapshot(SnapshotId) {
        Piv.http.get(["/api/elections/" + ElectionId + "/result_snapshots/" + SnapshotId],
                     gotSnapshot, showErrorMessage)
    }

    function gotSnapshot(snapshot) {
        if (snapshot.format_version < 3) {
            alert("You can only debug snapshots with version 3 or greater.  This one was version " + snapshot.format_version + ".")
            return
        }
        console.log(snapshot)
	var debug = snapshot.result_blob.debug
	var debug_private = snapshot.result_blob.debug_private

	// candidates
        CandidatesDiv.innerHTML = ""
        debug_private.candidates.forEach(candidate => {
            piv.html(CandidatesDiv, "span", candidate.id + ": " + candidate.name + "
")
        })

	// ballots
        BallotsDiv.innerHTML = ""
	var ballots = debug.ballots
        Object.keys(ballots).forEach(elector_id => {
	    var ballot_text = ballots[elector_id]
            piv.html(BallotsDiv, "span", ballot_text + "
")
        })

	// tie breaker, partial and total order
	TiePartialDiv.innerHTML = debug.tie_breaker
	TieTotalDiv.innerHTML = debug.tie_breaker_total

	// results
	var ranks = Array.from(snapshot.result_blob.order.keys())
	ranks.sort()
	var winners = ranks.map(rank => snapshot.result_blob.order[rank].id)
	ResultsDiv.innerHTML = winners.join(">")

	// plotting
	var alchemy_data = ballotsToAlchemyGraph(debug.ballots)
	showPlot(alchemy_data)
    }

    function ballotsToAlchemyGraph(ballots) {
	var nodes = new Set()
	var edge_counts = new Map()

	// compute votes each way, between each pair
	Object.keys(ballots).forEach(elector_id => {
	    var ballot_text = ballots[elector_id]
	    console.log(ballot_text)
	    var ballot = ballot_text.split(">").map(x => x.split("="))

	    for (var i=0; i<ballot.length; i++) {
		var group1 = ballot[i]
		group1.forEach(function(A){
		    nodes.add(A)
		    ballot.slice(i+1).forEach(function(group2) {
			group2.forEach(function(B){
			    var key = [A,B].toString() // A beats B in this ballot
			    var count = edge_counts.has(key) ? edge_counts.get(key) : 0
			    count += 1
			    edge_counts.set(key, count)
			})
		    })
		})
	    }
        })

	// generate list of alchemy edges
	var edges = []
	nodes.forEach(function(A) {
	    nodes.forEach(function(B) {
		// compute margin
		var margin = 0
		var key = [A,B].toString()
		if (edge_counts.has(key)) {
		    margin += edge_counts.get(key)
		}
		var key = [B,A].toString()
		if (edge_counts.has(key)) {
		    margin -= edge_counts.get(key)
		}

		// add edge (if margin!=0) in appropriate direction
		if (margin > 0) {
		    edges.push({source:A, target:B})
		} else if (margin < 0) {
		    edges.push({source:B, target:A})
		}
	    })
	})

	// generate list of alchemy nodes
	nodes = Array.from(nodes)
	nodes = nodes.map(function(id) {
	    return {id:id, caption:id}
	})

	var alchemy_data = {nodes:nodes, edges:edges}
	return alchemy_data
    }

    function showPlot(alchemy_data) {
	PlotDiv.innerHTML = ""
	var AlchemyDiv = Piv.div(PlotDiv, "alchemy")

	var config = {
            dataSource: alchemy_data,

            directedEdges: true,
            backgroundColour: "white",
            nodeCaptionsOnByDefault: true,

            edgeStyle: {"all":{
		"color": "black",
		"opacity": 1,
		"width": 2,
            }},

            nodeStyle: {"all":{
		"borderColor": "black",
		"borderWidth": 2,
		"color": "white",
		"radius": 30,
            }},
	};

	var alchemy = new Alchemy(config)
    }

    function showErrorMessage(error) {
        Piv.div(View.workspace, "", "w100 text3", "Debug for this election are not currently available.")
        if (!error) return
        Piv.div(View.workspace, "", "100 text3", error.response.data.message)
    }

    main(piv, election)
})(piv, election)
<|endoftext|>