<commit_before>import Ember from 'ember';
import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('array-contains', 'helper:array-contains', {
  integration: true
});

test('should not throw error if array undefined or null', function (assert) {
  this.set('array', undefined);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any'}}"));
  assert.equal(this.$().text(), "false", "array should not contain anything");

  this.set('array', null);
  assert.equal(this.$().text(), "false", "array should not contain anything");

});

test('should throw error if array is invalid', function (assert) {
  assert.expect(1);

  this.set('array', 'any');

  try {
    this.render(Ember.HTMLBars.compile("{{array-contains array 'any'}}"));
  }
  catch (error) {
    assert.ok(error && error instanceof Ember.Error, "null array throws Ember error");
  }

});

test('should return true if literal contained', function (assert) {
  this.set('array', ['c', 'string', 0, true, null]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'c'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'c'");

  this.render(Ember.HTMLBars.compile("{{array-contains array 'string'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'string'");

  this.render(Ember.HTMLBars.compile("{{array-contains array 0}}"));
  assert.equal(this.$().text(), "true", "array should contain '0'");

  this.render(Ember.HTMLBars.compile("{{array-contains array true}}"));
  assert.equal(this.$().text(), "true", "array should contain 'true'");

  this.render(Ember.HTMLBars.compile("{{array-contains array null}}"));
  assert.equal(this.$().text(), "true", "array should contain 'null'");

  this.render(Ember.HTMLBars.compile("{{array-contains array undefined}}"));
  assert.equal(this.$().text(), "true", "array should contain 'undefined'");
});

test('should return false if literal not contained', function (assert) {
  this.set('array', ['c', 'string', 0, true, null]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'a'}}"));
  assert.equal(this.$().text(), "false", "array should not contain 'a'");

  this.render(Ember.HTMLBars.compile("{{array-contains array 'foo'}}"));
  assert.equal(this.$().text(), "false", "array should not contain 'foo'");

  this.render(Ember.HTMLBars.compile("{{array-contains array 2}}"));
  assert.equal(this.$().text(), "false", "array should not contain '2'");

  this.render(Ember.HTMLBars.compile("{{array-contains array false}}"));
  assert.equal(this.$().text(), "false", "array should not contain 'false'");
});

test('should return true if native object contained', function (assert) {
  let elem = {id: 1, title: 'any'};
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if native object not contained', function (assert) {
  this.set('array', [{id: 2, title: 'other'}]);
  this.set('elem', {id: 1, title: 'any'});

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when native object change', function (assert) {
  let elem = {id: 1, title: 'any'};
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {id: 1, title: 'any'});

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if Ember object contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if Ember object not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);
  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when Ember object change', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if native object property contained', function (assert) {
  this.set('array', [{id: 1, title: 'any'}]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any' property='title'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'any' title");
});

test('should return false if native object property not contained', function (assert) {
  this.set('array', [{id: 2, title: 'other'}]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any' property='title'}}"));
  assert.equal(this.$().text(), "false", "array should not contain 'any' title");
});

test('should return true if Ember object contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if Ember object not contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when Ember object change', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(Ember.HTMLBars.compile("{{array-contains array elem}}"));
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if Ember object property contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 1, title: 'any'})]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any' property='title'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'any' title");
});

test('should return false if Ember object property not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any' property='title'}}"));
  assert.equal(this.$().text(), "false", "array should not contain 'any' title");
});

test('should rerun test when array replaced', function (assert) {
  this.set('array', ['any']);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'any'");

  this.set('array', ['not']);

  assert.equal(this.$().text(), "false", "array should not contain 'any'");
});

test('should rerun test when array changed', function (assert) {
  let array = ['any'];
  this.set('array', array);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any'}}"));
  assert.equal(this.$().text(), "true", "array should contain 'any'");

  Ember.run(() => {
    array.popObject();
  });

  assert.equal(this.$().text(), "false", "array should not contain 'any'");

  Ember.run(() => {
    array.pushObject('any');
  });

  assert.equal(this.$().text(), "true", "array should contain 'any'");
});

test('should rerun test when property in array changed', function (assert) {
  let object = {id: 1, title: 'any'};
  let array = [object];
  this.set('array', array);

  this.render(Ember.HTMLBars.compile("{{array-contains array 'any' property='title'}}"));
  assert.equal(this.$().text(), "true", "array should contain object with prop 'any'");

  Ember.run(() => {
    Ember.set(object, 'title', 'not');
  });

  assert.equal(this.$().text(), "false", "array should not contain object with prop 'any'");

  Ember.run(() => {
    Ember.set(object, 'title', 'any');
  });

  assert.equal(this.$().text(), "true", "array should contain object with prop 'any'");
});

test('should return true in nested if if Ember object property contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 1, title: 'any'})]);

  this.render(Ember.HTMLBars.compile("{{if (array-contains array 'any' property='title') 'ifTrue' 'ifFalse'}}"));
  assert.equal(this.$().text(), "ifTrue", "array should contain 'any' title");
});

test('should return false in nested if if Ember object property not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);

  this.render(Ember.HTMLBars.compile("{{if (array-contains array 'any' property='title') 'ifTrue' 'ifFalse'}}"));
  assert.equal(this.$().text(), "ifFalse", "array should not contain 'any' title");
});

<commit_msg>refactor(tests): switch from HTMLBars.complile to `hbs`<commit_after>import Ember from 'ember';
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('array-contains', 'helper:array-contains', {
  integration: true
});

test('should not throw error if array undefined or null', function (assert) {
  this.set('array', undefined);

  this.render(hbs`{{array-contains array 'any'}}`);
  assert.equal(this.$().text(), "false", "array should not contain anything");

  this.set('array', null);
  assert.equal(this.$().text(), "false", "array should not contain anything");

});

test('should throw error if array is invalid', function (assert) {
  assert.expect(1);

  this.set('array', 'any');

  try {
    this.render(hbs`{{array-contains array 'any'}}`);
  }
  catch (error) {
    assert.ok(error && error instanceof Ember.Error, "null array throws Ember error");
  }

});

test('should return true if literal contained', function (assert) {
  this.set('array', ['c', 'string', 0, true, null]);

  this.render(hbs`{{array-contains array 'c'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'c'");

  this.render(hbs`{{array-contains array 'string'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'string'");

  this.render(hbs`{{array-contains array 0}}`);
  assert.equal(this.$().text(), "true", "array should contain '0'");

  this.render(hbs`{{array-contains array true}}`);
  assert.equal(this.$().text(), "true", "array should contain 'true'");

  this.render(hbs`{{array-contains array null}}`);
  assert.equal(this.$().text(), "true", "array should contain 'null'");

  this.render(hbs`{{array-contains array undefined}}`);
  assert.equal(this.$().text(), "true", "array should contain 'undefined'");
});

test('should return false if literal not contained', function (assert) {
  this.set('array', ['c', 'string', 0, true, null]);

  this.render(hbs`{{array-contains array 'a'}}`);
  assert.equal(this.$().text(), "false", "array should not contain 'a'");

  this.render(hbs`{{array-contains array 'foo'}}`);
  assert.equal(this.$().text(), "false", "array should not contain 'foo'");

  this.render(hbs`{{array-contains array 2}}`);
  assert.equal(this.$().text(), "false", "array should not contain '2'");

  this.render(hbs`{{array-contains array false}}`);
  assert.equal(this.$().text(), "false", "array should not contain 'false'");
});

test('should return true if native object contained', function (assert) {
  let elem = {id: 1, title: 'any'};
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if native object not contained', function (assert) {
  this.set('array', [{id: 2, title: 'other'}]);
  this.set('elem', {id: 1, title: 'any'});

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when native object change', function (assert) {
  let elem = {id: 1, title: 'any'};
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {id: 1, title: 'any'});

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if Ember object contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if Ember object not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);
  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when Ember object change', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if native object property contained', function (assert) {
  this.set('array', [{id: 1, title: 'any'}]);

  this.render(hbs`{{array-contains array 'any' property='title'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'any' title");
});

test('should return false if native object property not contained', function (assert) {
  this.set('array', [{id: 2, title: 'other'}]);

  this.render(hbs`{{array-contains array 'any' property='title'}}`);
  assert.equal(this.$().text(), "false", "array should not contain 'any' title");
});

test('should return true if Ember object contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");
});

test('should return false if Ember object not contained', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should recompute when Ember object change', function (assert) {
  let elem = Ember.Object.create({id: 1, title: 'any'});
  this.set('array', [elem]);
  this.set('elem', elem);

  this.render(hbs`{{array-contains array elem}}`);
  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', {});

  assert.equal(this.$().text(), "false", "array should not contain elem");

  this.set('elem', elem);

  assert.equal(this.$().text(), "true", "array should contain elem");

  this.set('elem', Ember.Object.create({id: 1, title: 'any'}));

  assert.equal(this.$().text(), "false", "array should not contain elem");
});

test('should return true if Ember object property contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 1, title: 'any'})]);

  this.render(hbs`{{array-contains array 'any' property='title'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'any' title");
});

test('should return false if Ember object property not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);

  this.render(hbs`{{array-contains array 'any' property='title'}}`);
  assert.equal(this.$().text(), "false", "array should not contain 'any' title");
});

test('should rerun test when array replaced', function (assert) {
  this.set('array', ['any']);

  this.render(hbs`{{array-contains array 'any'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'any'");

  this.set('array', ['not']);

  assert.equal(this.$().text(), "false", "array should not contain 'any'");
});

test('should rerun test when array changed', function (assert) {
  let array = ['any'];
  this.set('array', array);

  this.render(hbs`{{array-contains array 'any'}}`);
  assert.equal(this.$().text(), "true", "array should contain 'any'");

  Ember.run(() => {
    array.popObject();
  });

  assert.equal(this.$().text(), "false", "array should not contain 'any'");

  Ember.run(() => {
    array.pushObject('any');
  });

  assert.equal(this.$().text(), "true", "array should contain 'any'");
});

test('should rerun test when property in array changed', function (assert) {
  let object = {id: 1, title: 'any'};
  let array = [object];
  this.set('array', array);

  this.render(hbs`{{array-contains array 'any' property='title'}}`);
  assert.equal(this.$().text(), "true", "array should contain object with prop 'any'");

  Ember.run(() => {
    Ember.set(object, 'title', 'not');
  });

  assert.equal(this.$().text(), "false", "array should not contain object with prop 'any'");

  Ember.run(() => {
    Ember.set(object, 'title', 'any');
  });

  assert.equal(this.$().text(), "true", "array should contain object with prop 'any'");
});

test('should return true in nested if if Ember object property contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 1, title: 'any'})]);

  this.render(hbs`{{if (array-contains array 'any' property='title') 'ifTrue' 'ifFalse'}}`);
  assert.equal(this.$().text(), "ifTrue", "array should contain 'any' title");
});

test('should return false in nested if if Ember object property not contained', function (assert) {
  this.set('array', [Ember.Object.create({id: 2, title: 'other'})]);

  this.render(hbs`{{if (array-contains array 'any' property='title') 'ifTrue' 'ifFalse'}}`);
  assert.equal(this.$().text(), "ifFalse", "array should not contain 'any' title");
});

<|endoftext|>


<commit_before>'use strict';

const Script = require('smooch-bot').Script;
const SefzigBot = "[SefzigBot] ";
const LinkBot = "[LinkBot] ";

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say(SefzigBot+'101100111001101...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say(SefzigBot+'Wenn Andreas gerade online ist, sieht er dieses Gespräch und kann beitreten.')
                .then(() => 'vorname'); /* <-- register */
        }
    },

    vorname: {
        prompt: (bot) => bot.say(SefzigBot+'Wie heissen Sie mit Vornamen?'),
        receive: (bot, message) => {
            var vorname = message.text;
            return bot.setProp('vorname', vorname)
                .then(() => bot.say(SefzigBot+`${vorname}, prima.`))
                .then(() => 'nachname');
        }
    },

    nachname: {
        prompt: (bot) => bot.say(SefzigBot+'Und mit Nachnamen?'),
        receive: (bot, message) => {
            var nachname = message.text;
            return bot.setProp('nachname', nachname)
                .then(() => bot.say(SefzigBot+`${nachname}, danke.`))
                .then(() => bot.say(SefzigBot+'Sie können hier jederzeit eine Nachricht an Andreas eingeben.'))
                .then(() => bot.say(SefzigBot+'Unterhalten Sie sich solange mit mir! Bitte schreiben Sie --bot:'))
                .then(() => 'register');
        }
    },

    register: {
        receive: (bot, message) => {

            var wollen = message.text;
            var befehl = wollen;
            befehl = befehl.trim();
            befehl = befehl.toUpperCase();

         // -----------------
         // System
         // -----------------

            if (befehl == "--BOT")            { bot.say(SefzigBot+'Ich weiß eine Menge über Andreas - und bin sein Ersatz für eine klassische Webseite. Rufen Sie mein Wissen durch Schreiben der mit -- beginnenden Wörter ab!');
                                                bot.say(SefzigBot+'Wollen Sie etwas über seine --Person oder --Neues von ihm erfahren? Oder auf anderem Wege --Kontakt  aufnehmen?'); }
            if (befehl == "--ABBRECHEN")      { bot.say(SefzigBot+'Text: Abbruch.'); }
            if (befehl == "--QRCODE")         { bot.say(SefzigBot+'Diesen Chat mobil öffnen: [Bild:https://zxing.org/w/chart?cht=qr&chs=200x200&chld=L&choe=UTF-8&chl=http%3A%2F%2Fsefzigbot.herokuapp.com%2F]'); }
            if (befehl == "--JAVASCRIPT")     { bot.say(SefzigBot+'[Javascript:test_alert]'); }

         // -----------------
         // Über mich
         // -----------------

            if (befehl == "--PERSON")         { bot.say(SefzigBot+'Andreas ist 38 Jahre alt und lebt in Hamburg. Hier sein --Lebenslauf und ein --Foto.'); }
            if (befehl == "--LEBENSLAUF")     { bot.say(SefzigBot+'Sein Lebenslauf als druckbares PDF-Dokument: %[Lebenslauf.pdf](http://sefzig.net/text/seiten/Lebenslauf/dateien/Andreas_Sefzig_Lebenslauf.pdf)');
                                                bot.say(SefzigBot+'Tabellarisches und ausformuliertes CV: %[Lebenslauf öffnen](http://sefzig.net/link/Lebenslauf/)'); }
            if (befehl == "--FOTO")           { bot.say(SefzigBot+'[Bild:http://sefzig.net/text/seiten/SefzignetStartseite/dateien/SefzignetStartseiteFotoLogos.png] Andreas Sefzig mit Logo'); }
            if (befehl == "--NEUES")          { bot.say(SefzigBot+'Text: Neues.'); }

         // -----------------
         // Links
         // -----------------

            if (befehl == "--LINKS")          { bot.say(LinkBot+'Andreas speichert interessante Links für sich und andere: %[Linkliste öffnen](http://sefzig.net/link/liste/)');
                                                bot.say(LinkBot+'URLs der Link-Verwaltung: --LinkEingabe, --LinkListe, --LinkEinrichten, --LinkAdmin, --LinkUber.'); }
            if (befehl == "--LINKEINGABE")    { bot.say(LinkBot+'Link: Eingabe.'); }
            if (befehl == "--LINKLISTE")      { bot.say(LinkBot+'Link: Liste.'); }
            if (befehl == "--LINKEINRICHTEN") { bot.say(LinkBot+'Link: Einrichten.'); }
            if (befehl == "--LINKADMIN")      { bot.say(LinkBot+'Link: Administration.'); }
            if (befehl == "--LINKUBER")       { bot.say(LinkBot+'Link: Über.'); }

         // -----------------
         // Kontakt
         // -----------------

            if (befehl == "--KONTAKT")        { bot.say(SefzigBot+'Sie erreichen Andreas über andreas@sefzig.net, in seinem --Slackteam, auf --Twitter und auf --Xing. %[Kontakt-Seite öffnen](http://sefzig.net/link/Kontakt/)'); }
            if (befehl == "--SLACKTEAM")      { bot.say(SefzigBot+'Andreas betreibt ein offenes Slack-Team, in dem man sich über Marketing-Technologie unterhält. %[Slack-Team öffnen](http://sefzig.net/link/Slack/)'); }
            if (befehl == "--XING")           { bot.say(SefzigBot+'Andreas ist bei Xing: Er ist seit 2004 angemeldet und gehört mit über 500 Kontakten zu den 5% der bestvernetzten Mitglieder. %[Xing-Profil öffnen](http://sefzig.net/link/XingProfil/)');
                                                bot.say(SefzigBot+'[Bild:http://sefzig.net/text/seiten/SefzigBot/dateien/xing_vernetzt.png]'); }
            if (befehl == "--TWITTER")        { bot.say(SefzigBot+'Andreas ist auf Twitter: Er ist seit 2008 angemeldet, hat beinahe 500 Follower und sah sich kürzlich gezwungen, alle Tweets zu löschen: Neustart 2016. %[Twitter-Profil öffnen](http://sefzig.net/link/TwitterProfil/)');
                                                bot.say(SefzigBot+'Schreiben Sie einen Tweet über @sefzig: %[@sefzig twittern](http://sefzig.net/link/TwitterTweet/)'); }

         // -----------------
         // Vorlage
         // -----------------

            if (befehl == "--VORLAGE")        { bot.say(SefzigBot+'Text: Vorlage.'); }

         // .then(function(){ bot.say(''); })
            return bot.setProp('wollen', wollen)
                .then(() => 'register');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then(() => 'finish');
        }
    }
});
<commit_msg>test_alert<commit_after>'use strict';

const Script = require('smooch-bot').Script;
const SefzigBot = "[SefzigBot] ";
const LinkBot = "[LinkBot] ";

module.exports = new Script({
    processing: {
        prompt: (bot) => bot.say(SefzigBot+'101100111001101...'),
        receive: () => 'processing'
    },

    start: {
        receive: (bot) => {
            return bot.say(SefzigBot+'Wenn Andreas gerade online ist, sieht er dieses Gespräch und kann beitreten.')
                .then(() => 'vorname'); /* <-- register */
        }
    },

    vorname: {
        prompt: (bot) => bot.say(SefzigBot+'Wie heissen Sie mit Vornamen?'),
        receive: (bot, message) => {
            var vorname = message.text;
            return bot.setProp('vorname', vorname)
                .then(() => bot.say(SefzigBot+`${vorname}, prima.`))
                .then(() => 'nachname');
        }
    },

    nachname: {
        prompt: (bot) => bot.say(SefzigBot+'Und mit Nachnamen?'),
        receive: (bot, message) => {
            var nachname = message.text;
            return bot.setProp('nachname', nachname)
                .then(() => bot.say(SefzigBot+`${nachname}, danke.`))
                .then(() => bot.say(SefzigBot+'Sie können hier jederzeit eine Nachricht an Andreas eingeben.'))
                .then(() => bot.say(SefzigBot+'Unterhalten Sie sich solange mit mir! Bitte schreiben Sie --bot:'))
                .then(() => 'register');
        }
    },

    register: {
        receive: (bot, message) => {

            var wollen = message.text;
            var befehl = wollen;
            befehl = befehl.trim();
            befehl = befehl.toUpperCase();

         // -----------------
         // System
         // -----------------

            if (befehl == "--BOT")            { bot.say(SefzigBot+'Ich weiß eine Menge über Andreas - und bin sein Ersatz für eine klassische Webseite. Rufen Sie mein Wissen durch Schreiben der mit -- beginnenden Wörter ab!');
                                                bot.say(SefzigBot+'Wollen Sie etwas über seine --Person oder --Neues von ihm erfahren? Oder auf anderem Wege --Kontakt  aufnehmen?'); }
            if (befehl == "--ABBRECHEN")      { bot.say(SefzigBot+'Text: Abbruch.'); }
            if (befehl == "--QRCODE")         { bot.say(SefzigBot+'Diesen Chat mobil öffnen: [Bild:https://zxing.org/w/chart?cht=qr&chs=200x200&chld=L&choe=UTF-8&chl=http%3A%2F%2Fsefzigbot.herokuapp.com%2F]'); }
            if (befehl == "--JAVASCRIPT")     { bot.say(SefzigBot+'[Javascript:test_console]'); }

         // -----------------
         // Über mich
         // -----------------

            if (befehl == "--PERSON")         { bot.say(SefzigBot+'Andreas ist 38 Jahre alt und lebt in Hamburg. Hier sein --Lebenslauf und ein --Foto.'); }
            if (befehl == "--LEBENSLAUF")     { bot.say(SefzigBot+'Sein Lebenslauf als druckbares PDF-Dokument: %[Lebenslauf.pdf](http://sefzig.net/text/seiten/Lebenslauf/dateien/Andreas_Sefzig_Lebenslauf.pdf)');
                                                bot.say(SefzigBot+'Tabellarisches und ausformuliertes CV: %[Lebenslauf öffnen](http://sefzig.net/link/Lebenslauf/)'); }
            if (befehl == "--FOTO")           { bot.say(SefzigBot+'[Bild:http://sefzig.net/text/seiten/SefzignetStartseite/dateien/SefzignetStartseiteFotoLogos.png] Andreas Sefzig mit Logo'); }
            if (befehl == "--NEUES")          { bot.say(SefzigBot+'Text: Neues.'); }

         // -----------------
         // Links
         // -----------------

            if (befehl == "--LINKS")          { bot.say(LinkBot+'Andreas speichert interessante Links für sich und andere: %[Linkliste öffnen](http://sefzig.net/link/liste/)');
                                                bot.say(LinkBot+'URLs der Link-Verwaltung: --LinkEingabe, --LinkListe, --LinkEinrichten, --LinkAdmin, --LinkUber.'); }
            if (befehl == "--LINKEINGABE")    { bot.say(LinkBot+'Link: Eingabe.'); }
            if (befehl == "--LINKLISTE")      { bot.say(LinkBot+'Link: Liste.'); }
            if (befehl == "--LINKEINRICHTEN") { bot.say(LinkBot+'Link: Einrichten.'); }
            if (befehl == "--LINKADMIN")      { bot.say(LinkBot+'Link: Administration.'); }
            if (befehl == "--LINKUBER")       { bot.say(LinkBot+'Link: Über.'); }

         // -----------------
         // Kontakt
         // -----------------

            if (befehl == "--KONTAKT")        { bot.say(SefzigBot+'Sie erreichen Andreas über andreas@sefzig.net, in seinem --Slackteam, auf --Twitter und auf --Xing. %[Kontakt-Seite öffnen](http://sefzig.net/link/Kontakt/)'); }
            if (befehl == "--SLACKTEAM")      { bot.say(SefzigBot+'Andreas betreibt ein offenes Slack-Team, in dem man sich über Marketing-Technologie unterhält. %[Slack-Team öffnen](http://sefzig.net/link/Slack/)'); }
            if (befehl == "--XING")           { bot.say(SefzigBot+'Andreas ist bei Xing: Er ist seit 2004 angemeldet und gehört mit über 500 Kontakten zu den 5% der bestvernetzten Mitglieder. %[Xing-Profil öffnen](http://sefzig.net/link/XingProfil/)');
                                                bot.say(SefzigBot+'[Bild:http://sefzig.net/text/seiten/SefzigBot/dateien/xing_vernetzt.png]'); }
            if (befehl == "--TWITTER")        { bot.say(SefzigBot+'Andreas ist auf Twitter: Er ist seit 2008 angemeldet, hat beinahe 500 Follower und sah sich kürzlich gezwungen, alle Tweets zu löschen: Neustart 2016. %[Twitter-Profil öffnen](http://sefzig.net/link/TwitterProfil/)');
                                                bot.say(SefzigBot+'Schreiben Sie einen Tweet über @sefzig: %[@sefzig twittern](http://sefzig.net/link/TwitterTweet/)'); }

         // -----------------
         // Vorlage
         // -----------------

            if (befehl == "--VORLAGE")        { bot.say(SefzigBot+'Text: Vorlage.'); }

         // .then(function(){ bot.say(''); })
            return bot.setProp('wollen', wollen)
                .then(() => 'register');
        }
    },

    finish: {
        receive: (bot, message) => {
            return bot.getProp('name')
                .then(() => 'finish');
        }
    }
});
<|endoftext|>


<commit_before>import Multivalue from '../multivalue/Multivalue';
import { delay, convertStringToHTMLElement } from '../../../utils/utils';
import Widgets from '../../../widgets';
import _ from 'lodash';

export default class Input extends Multivalue {
  constructor(component, options, data) {
    super(component, options, data);
    this.triggerUpdateValueAt = _.debounce(this.updateValueAt.bind(this), 100);
  }

  static schema(...extend) {
    return Multivalue.schema({
      widget: {
        type: 'input'
      }
    }, ...extend);
  }

  get inputInfo() {
    const attr = {
      name: this.options.name,
      type: this.component.inputType || 'text',
      class: 'form-control',
      lang: this.options.language
    };

    if (this.component.placeholder) {
      attr.placeholder = this.t(this.component.placeholder);
    }

    if (this.component.tabindex) {
      attr.tabindex = this.component.tabindex;
    }

    if (this.disabled) {
      attr.disabled = 'disabled';
    }

    _.defaults(attr, this.component.attributes);

    return {
      id: this.key,
      type: 'input',
      changeEvent: 'input',
      content: '',
      attr
    };
  }

  get maskOptions() {
    return _.map(this.component.inputMasks, mask => {
      return {
        label: mask.label,
        value: mask.label
      };
    });
  }

  get isMultipleMasksField() {
    return this.component.allowMultipleMasks && !!this.component.inputMasks && !!this.component.inputMasks.length;
  }

  getMaskByName(maskName) {
    const inputMask = _.find(this.component.inputMasks, (inputMask) => {
      return inputMask.label === maskName;
    });
    return inputMask ? inputMask.mask : undefined;
  }

  setInputMask(input, inputMask) {
    return super.setInputMask(input, (inputMask || this.component.inputMask), !this.component.placeholder);
  }

  getMaskOptions() {
    return this.component.inputMasks
      .map(mask => ({
        label: mask.label,
        value: mask.label,
      }));
  }

  getWordCount(value) {
    return value.trim().split(/\s+/).length;
  }

  get remainingWords() {
    const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords'), 10);
    const wordCount = this.getWordCount(this.dataValue);
    return maxWords - wordCount;
  }

  renderElement(value, index) {
    // Double quotes cause the input value to close so replace them with html quote char.
    if (value && typeof value === 'string') {
      value = value.replace(/"/g, '"');
    }
    const info = this.inputInfo;
    info.attr = info.attr || {};
    info.attr.value = this.getValueAsString(this.formatValue(this.parseValue(value)));
    if (this.isMultipleMasksField) {
      info.attr.class += ' formio-multiple-mask-input';
    }
    // This should be in the calendar widget but it doesn't have access to renderTemplate.
    if (this.component.widget && this.component.widget.type === 'calendar') {
      const calendarIcon = this.renderTemplate('icon', {
        ref: 'icon',
        // After font-awesome would be updated to v5.x, "clock-o" should be replaced with "clock"
        className: this.iconClass(this.component.enableDate || this.component.widget.enableDate ? 'calendar' : 'clock-o'),
        styles: '',
        content: ''
      }).trim();
      if (this.component.prefix !== calendarIcon) {
        // converting string to HTML markup to render correctly DateTime component in portal.form.io
        this.component.suffix = convertStringToHTMLElement(calendarIcon, '[ref="icon"]');
      }
    }

    return this.isMultipleMasksField
      ? this.renderTemplate('multipleMasksInput', {
        input: info,
        value,
        index,
        selectOptions: this.getMaskOptions() || [],
      })
      : this.renderTemplate('input', {
        input: info,
        value: this.formatValue(this.parseValue(value)),
        index
      });
  }

  setCounter(type, element, count, max) {
    if (max) {
      const remaining = max - count;
      if (remaining > 0) {
        this.removeClass(element, 'text-danger');
      }
      else {
        this.addClass(element, 'text-danger');
      }
      this.setContent(element, this.t(`{{ remaining }} ${type} remaining.`, {
        remaining: remaining
      }));
    }
    else {
      this.setContent(element, this.t(`{{ count }} ${type}`, {
        count: count
      }));
    }
  }

  updateValueAt(value, flags, index) {
    flags = flags || {};
    if (_.get(this.component, 'showWordCount', false)) {
      if (this.refs.wordcount && this.refs.wordcount[index]) {
        const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords', 0), 10);
        this.setCounter(this.t('words'), this.refs.wordcount[index], this.getWordCount(value), maxWords);
      }
    }
    if (_.get(this.component, 'showCharCount', false)) {
      if (this.refs.charcount && this.refs.charcount[index]) {
        const maxChars = _.parseInt(_.get(this.component, 'validate.maxLength', 0), 10);
        this.setCounter(this.t('characters'), this.refs.charcount[index], value.length, maxChars);
      }
    }
  }

  getValueAt(index) {
    const input = this.performInputMapping(this.refs.input[index]);
    if (input && input.widget) {
      return input.widget.getValue();
    }
    return input ? input.value : undefined;
  }

  updateValue(value, flags, index) {
    flags = flags || {};
    const changed = super.updateValue(value, flags);
    this.triggerUpdateValueAt(this.dataValue, flags, index);
    return changed;
  }

  parseValue(value) {
    return value;
  }

  formatValue(value) {
    return value;
  }

  attach(element) {
    this.loadRefs(element, {
      charcount: 'multiple',
      wordcount: 'multiple',
      prefix: 'multiple',
      suffix: 'multiple'
    });
    return super.attach(element);
  }

  getWidget(index) {
    index = index || 0;
    if (this.refs.input && this.refs.input[index]) {
      return this.refs.input[index].widget;
    }
    return null;
  }

  getValueAsString(value, options) {
    return super.getValueAsString(this.getWidgetValueAsString(value, options), options);
  }

  attachElement(element, index) {
    super.attachElement(element, index);
    if (element.widget) {
      element.widget.destroy();
    }
    // Attach the widget.
    element.widget = this.createWidget(index);
    if (element.widget) {
      element.widget.attach(element);
      if (this.refs.prefix && this.refs.prefix[index]) {
        element.widget.addPrefix(this.refs.prefix[index]);
      }
      if (this.refs.suffix && this.refs.suffix[index]) {
        element.widget.addSuffix(this.refs.suffix[index]);
      }
    }

    // Add focus and blur events.
    this.addFocusBlurEvents(element);

    if (this.options.submitOnEnter) {
      this.addEventListener(element, 'keypress', (event) => {
        const key = event.keyCode || event.which;
        if (key === 13) {
          event.preventDefault();
          event.stopPropagation();
          this.emit('submitButton');
        }
      });
    }
  }

  /**
   * Creates an instance of a widget for this component.
   *
   * @return {null}
   */
  createWidget(index) {
    // Return null if no widget is found.
    if (!this.component.widget) {
      return null;
    }

    // Get the widget settings.
    const settings = (typeof this.component.widget === 'string') ? {
      type: this.component.widget
    } : this.component.widget;

    // Make sure we have a widget.
    if (!Widgets.hasOwnProperty(settings.type)) {
      return null;
    }

    // Create the widget.
    const widget = new Widgets[settings.type](settings, this.component);
    widget.on('update', () => this.updateValue(widget.getValue(), {
      modified: true
    }, index), true);
    widget.on('redraw', () => this.redraw(), true);
    return widget;
  }

  detach() {
    super.detach();
    if (this.refs && this.refs.input) {
      for (let i = 0; i <= this.refs.input.length; i++) {
        const widget = this.getWidget(i);
        if (widget) {
          widget.destroy();
        }
      }
    }
  }

  addFocusBlurEvents(element) {
    this.addEventListener(element, 'focus', () => {
      if (this.root.focusedComponent !== this) {
        if (this.root.pendingBlur) {
          this.root.pendingBlur();
        }

        this.root.focusedComponent = this;

        this.emit('focus', this);
      }
      else if (this.root.focusedComponent === this && this.root.pendingBlur) {
        this.root.pendingBlur.cancel();
        this.root.pendingBlur = null;
      }
    });
    this.addEventListener(element, 'blur', () => {
      this.root.pendingBlur = delay(() => {
        this.emit('blur', this);
        if (this.component.validateOn === 'blur') {
          this.root.triggerChange({}, {
            instance: this,
            component: this.component,
            value: this.dataValue,
            flags: {}
          });
        }
        this.root.focusedComponent = null;
        this.root.pendingBlur = null;
      });
    });
  }
}
<commit_msg>Fix Webhook Action authentication part autocompleting<commit_after>import Multivalue from '../multivalue/Multivalue';
import { delay, convertStringToHTMLElement } from '../../../utils/utils';
import Widgets from '../../../widgets';
import _ from 'lodash';

export default class Input extends Multivalue {
  constructor(component, options, data) {
    super(component, options, data);
    this.triggerUpdateValueAt = _.debounce(this.updateValueAt.bind(this), 100);
  }

  static schema(...extend) {
    return Multivalue.schema({
      widget: {
        type: 'input'
      }
    }, ...extend);
  }

  get inputInfo() {
    const attr = {
      name: this.options.name,
      type: this.component.inputType || 'text',
      class: 'form-control',
      lang: this.options.language
    };

    if (this.component.placeholder) {
      attr.placeholder = this.t(this.component.placeholder);
    }

    if (this.component.tabindex) {
      attr.tabindex = this.component.tabindex;
    }

    if (this.disabled) {
      attr.disabled = 'disabled';
    }

    if (this.component.autocomplete) {
      attr.autocomplete = this.t(this.component.autocomplete);
    }

    _.defaults(attr, this.component.attributes);

    return {
      id: this.key,
      type: 'input',
      changeEvent: 'input',
      content: '',
      attr
    };
  }

  get maskOptions() {
    return _.map(this.component.inputMasks, mask => {
      return {
        label: mask.label,
        value: mask.label
      };
    });
  }

  get isMultipleMasksField() {
    return this.component.allowMultipleMasks && !!this.component.inputMasks && !!this.component.inputMasks.length;
  }

  getMaskByName(maskName) {
    const inputMask = _.find(this.component.inputMasks, (inputMask) => {
      return inputMask.label === maskName;
    });
    return inputMask ? inputMask.mask : undefined;
  }

  setInputMask(input, inputMask) {
    return super.setInputMask(input, (inputMask || this.component.inputMask), !this.component.placeholder);
  }

  getMaskOptions() {
    return this.component.inputMasks
      .map(mask => ({
        label: mask.label,
        value: mask.label,
      }));
  }

  getWordCount(value) {
    return value.trim().split(/\s+/).length;
  }

  get remainingWords() {
    const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords'), 10);
    const wordCount = this.getWordCount(this.dataValue);
    return maxWords - wordCount;
  }

  renderElement(value, index) {
    // Double quotes cause the input value to close so replace them with html quote char.
    if (value && typeof value === 'string') {
      value = value.replace(/"/g, '"');
    }
    const info = this.inputInfo;
    info.attr = info.attr || {};
    info.attr.value = this.getValueAsString(this.formatValue(this.parseValue(value)));
    if (this.isMultipleMasksField) {
      info.attr.class += ' formio-multiple-mask-input';
    }
    // This should be in the calendar widget but it doesn't have access to renderTemplate.
    if (this.component.widget && this.component.widget.type === 'calendar') {
      const calendarIcon = this.renderTemplate('icon', {
        ref: 'icon',
        // After font-awesome would be updated to v5.x, "clock-o" should be replaced with "clock"
        className: this.iconClass(this.component.enableDate || this.component.widget.enableDate ? 'calendar' : 'clock-o'),
        styles: '',
        content: ''
      }).trim();
      if (this.component.prefix !== calendarIcon) {
        // converting string to HTML markup to render correctly DateTime component in portal.form.io
        this.component.suffix = convertStringToHTMLElement(calendarIcon, '[ref="icon"]');
      }
    }

    return this.isMultipleMasksField
      ? this.renderTemplate('multipleMasksInput', {
        input: info,
        value,
        index,
        selectOptions: this.getMaskOptions() || [],
      })
      : this.renderTemplate('input', {
        input: info,
        value: this.formatValue(this.parseValue(value)),
        index
      });
  }

  setCounter(type, element, count, max) {
    if (max) {
      const remaining = max - count;
      if (remaining > 0) {
        this.removeClass(element, 'text-danger');
      }
      else {
        this.addClass(element, 'text-danger');
      }
      this.setContent(element, this.t(`{{ remaining }} ${type} remaining.`, {
        remaining: remaining
      }));
    }
    else {
      this.setContent(element, this.t(`{{ count }} ${type}`, {
        count: count
      }));
    }
  }

  updateValueAt(value, flags, index) {
    flags = flags || {};
    if (_.get(this.component, 'showWordCount', false)) {
      if (this.refs.wordcount && this.refs.wordcount[index]) {
        const maxWords = _.parseInt(_.get(this.component, 'validate.maxWords', 0), 10);
        this.setCounter(this.t('words'), this.refs.wordcount[index], this.getWordCount(value), maxWords);
      }
    }
    if (_.get(this.component, 'showCharCount', false)) {
      if (this.refs.charcount && this.refs.charcount[index]) {
        const maxChars = _.parseInt(_.get(this.component, 'validate.maxLength', 0), 10);
        this.setCounter(this.t('characters'), this.refs.charcount[index], value.length, maxChars);
      }
    }
  }

  getValueAt(index) {
    const input = this.performInputMapping(this.refs.input[index]);
    if (input && input.widget) {
      return input.widget.getValue();
    }
    return input ? input.value : undefined;
  }

  updateValue(value, flags, index) {
    flags = flags || {};
    const changed = super.updateValue(value, flags);
    this.triggerUpdateValueAt(this.dataValue, flags, index);
    return changed;
  }

  parseValue(value) {
    return value;
  }

  formatValue(value) {
    return value;
  }

  attach(element) {
    this.loadRefs(element, {
      charcount: 'multiple',
      wordcount: 'multiple',
      prefix: 'multiple',
      suffix: 'multiple'
    });
    return super.attach(element);
  }

  getWidget(index) {
    index = index || 0;
    if (this.refs.input && this.refs.input[index]) {
      return this.refs.input[index].widget;
    }
    return null;
  }

  getValueAsString(value, options) {
    return super.getValueAsString(this.getWidgetValueAsString(value, options), options);
  }

  attachElement(element, index) {
    super.attachElement(element, index);
    if (element.widget) {
      element.widget.destroy();
    }
    // Attach the widget.
    element.widget = this.createWidget(index);
    if (element.widget) {
      element.widget.attach(element);
      if (this.refs.prefix && this.refs.prefix[index]) {
        element.widget.addPrefix(this.refs.prefix[index]);
      }
      if (this.refs.suffix && this.refs.suffix[index]) {
        element.widget.addSuffix(this.refs.suffix[index]);
      }
    }

    // Add focus and blur events.
    this.addFocusBlurEvents(element);

    if (this.options.submitOnEnter) {
      this.addEventListener(element, 'keypress', (event) => {
        const key = event.keyCode || event.which;
        if (key === 13) {
          event.preventDefault();
          event.stopPropagation();
          this.emit('submitButton');
        }
      });
    }
  }

  /**
   * Creates an instance of a widget for this component.
   *
   * @return {null}
   */
  createWidget(index) {
    // Return null if no widget is found.
    if (!this.component.widget) {
      return null;
    }

    // Get the widget settings.
    const settings = (typeof this.component.widget === 'string') ? {
      type: this.component.widget
    } : this.component.widget;

    // Make sure we have a widget.
    if (!Widgets.hasOwnProperty(settings.type)) {
      return null;
    }

    // Create the widget.
    const widget = new Widgets[settings.type](settings, this.component);
    widget.on('update', () => this.updateValue(widget.getValue(), {
      modified: true
    }, index), true);
    widget.on('redraw', () => this.redraw(), true);
    return widget;
  }

  detach() {
    super.detach();
    if (this.refs && this.refs.input) {
      for (let i = 0; i <= this.refs.input.length; i++) {
        const widget = this.getWidget(i);
        if (widget) {
          widget.destroy();
        }
      }
    }
  }

  addFocusBlurEvents(element) {
    this.addEventListener(element, 'focus', () => {
      if (this.root.focusedComponent !== this) {
        if (this.root.pendingBlur) {
          this.root.pendingBlur();
        }

        this.root.focusedComponent = this;

        this.emit('focus', this);
      }
      else if (this.root.focusedComponent === this && this.root.pendingBlur) {
        this.root.pendingBlur.cancel();
        this.root.pendingBlur = null;
      }
    });
    this.addEventListener(element, 'blur', () => {
      this.root.pendingBlur = delay(() => {
        this.emit('blur', this);
        if (this.component.validateOn === 'blur') {
          this.root.triggerChange({}, {
            instance: this,
            component: this.component,
            value: this.dataValue,
            flags: {}
          });
        }
        this.root.focusedComponent = null;
        this.root.pendingBlur = null;
      });
    });
  }
}
<|endoftext|>


<commit_before>
angular.module('material.animations')

.directive('inkRipple', [
  '$materialInkRipple',
  InkRippleDirective
])

.factory('$materialInkRipple', [
  '$window',
  '$$rAF',
  '$materialEffects',
  '$timeout',
  InkRippleService
]);

function InkRippleDirective($materialInkRipple) {
  return function(scope, element, attr) {
    if (attr.inkRipple == 'checkbox') {
      $materialInkRipple.attachCheckboxBehavior(element);
    } else {
      $materialInkRipple.attachButtonBehavior(element);
    }
  };
}

function InkRippleService($window, $$rAF, $materialEffects, $timeout) {

  return {
    attachButtonBehavior: attachButtonBehavior,
    attachCheckboxBehavior: attachCheckboxBehavior,
    attach: attach,
  };

  function attachButtonBehavior(element) {
    return attach(element, {
      mousedown: true,
      center: false,
      animationDuration: 350,
      mousedownPauseTime: 175,
      animationName: 'inkRippleButton',
      animationTimingFunction: 'linear'
    });
  }

  function attachCheckboxBehavior(element) {
    return attach(element, {
      mousedown: true,
      center: true,
      animationDuration: 300,
      mousedownPauseTime: 180,
      animationName: 'inkRippleCheckbox',
      animationTimingFunction: 'linear'
    });
  }

  function attach(element, options) {
    options = angular.extend({
      mousedown: true,
      hover: true,
      focus: true,
      center: false,
      animationDuration: 300,
      mousedownPauseTime: 150,
      animationName: '',
      animationTimingFunction: 'linear'
    }, options || {});

    var rippleContainer;
    var node = element[0];

    if (options.mousedown) {
      enableMousedown();
    }

    function rippleIsAllowed() {
      return !element.controller('noink') && !Util.isDisabled(element);
    }

    var hasTouch = !!('ontouchend' in document);
    function enableMousedown() {
      // TODO fix this. doesn't support touch AND click devices (eg chrome pixel)
      var POINTERDOWN_EVENT = hasTouch ? 'touchstart' : 'mousedown';
      var POINTERUP_EVENT = hasTouch ? 'touchend touchcancel' : 'mouseup mouseleave';

      if (!rippleIsAllowed()) return;

      element.on(POINTERDOWN_EVENT, onPointerDown);

      var pointerIsDown;
      function onPointerDown(ev) {
        if (pointerIsDown) return;
        pointerIsDown = true;

        element.one(POINTERUP_EVENT, function() {
          pointerIsDown = false;
        });

        var rippleEl = createRippleFromEvent(ev);

        var pointerCheckTimeout = $timeout(
          pauseRippleIfPointerDown,
          options.mousedownPauseTime,
          false
        );

        rippleEl.on('$destroy', cancelPointerCheck);

        function pauseRippleIfPointerDown() {
          if (pointerIsDown) {
            rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'paused');
            element.one(POINTERUP_EVENT, function() {
              rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'running');
            });
          }
        }
        function cancelPointerCheck() {
          $timeout.cancel(pointerCheckTimeout);
        }
      }
    }

    function createRippleFromEvent(ev) {
      ev = ev.touches ? ev.touches[0] : ev;
      return createRipple(ev.pageX, ev.pageY, true);
    }
    function createRipple(left, top, positionsAreAbsolute) {

      var rippleEl = angular.element('
')
        .css(
          $materialEffects.ANIMATION_DURATION,
          options.animationDuration + 'ms'
        )
        .css(
          $materialEffects.ANIMATION_NAME,
          options.animationName
        )
        .css(
          $materialEffects.ANIMATION_TIMING,
          options.animationTimingFunction
        )
        .on($materialEffects.ANIMATIONEND_EVENT, function() {
          rippleEl.remove();
        });

      if (!rippleContainer) {
        rippleContainer = angular.element('
');
        element.append(rippleContainer);
      }
      rippleContainer.append(rippleEl);

      if (options.center) {
        left = rippleContainer.prop('offsetWidth') / 2;
        top = rippleContainer.prop('offsetHeight') / 2;
      } else if (positionsAreAbsolute) {
        var elementRect = node.getBoundingClientRect();
        left -= elementRect.left;
        top -= elementRect.top;
      }

      var finalSize = rippleContainer.prop('offsetWidth');

      // TODO don't use px, make setRippleCss fix that
      var css = {
        'background-color': $window.getComputedStyle(rippleEl[0]).color ||
          $window.getComputedStyle(node).color,
        'border-radius': (finalSize / 2) + 'px',

        left: (left - finalSize / 2) + 'px',
        width: finalSize + 'px',

        top: (top - finalSize / 2) + 'px',
        height: finalSize + 'px',
      };
      css[$materialEffects.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
      rippleEl.css(css);

      return rippleEl;
    }
  }

}
<commit_msg>fix(ripple): fix bug with ripple and many clicks<commit_after>
angular.module('material.animations')

.directive('inkRipple', [
  '$materialInkRipple',
  InkRippleDirective
])

.factory('$materialInkRipple', [
  '$window',
  '$$rAF',
  '$materialEffects',
  '$timeout',
  InkRippleService
]);

function InkRippleDirective($materialInkRipple) {
  return function(scope, element, attr) {
    if (attr.inkRipple == 'checkbox') {
      $materialInkRipple.attachCheckboxBehavior(element);
    } else {
      $materialInkRipple.attachButtonBehavior(element);
    }
  };
}

function InkRippleService($window, $$rAF, $materialEffects, $timeout) {

  // TODO fix this. doesn't support touch AND click devices (eg chrome pixel)
  var hasTouch = !!('ontouchend' in document);
  var POINTERDOWN_EVENT = hasTouch ? 'touchstart' : 'mousedown';
  var POINTERUP_EVENT = hasTouch ? 'touchend touchcancel' : 'mouseup mouseleave';

  return {
    attachButtonBehavior: attachButtonBehavior,
    attachCheckboxBehavior: attachCheckboxBehavior,
    attach: attach,
  };

  function attachButtonBehavior(element) {
    return attach(element, {
      mousedown: true,
      center: false,
      animationDuration: 350,
      mousedownPauseTime: 175,
      animationName: 'inkRippleButton',
      animationTimingFunction: 'linear'
    });
  }

  function attachCheckboxBehavior(element) {
    return attach(element, {
      mousedown: true,
      center: true,
      animationDuration: 300,
      mousedownPauseTime: 180,
      animationName: 'inkRippleCheckbox',
      animationTimingFunction: 'linear'
    });
  }

  function attach(element, options) {
    options = angular.extend({
      mousedown: true,
      hover: true,
      focus: true,
      center: false,
      animationDuration: 300,
      mousedownPauseTime: 150,
      animationName: '',
      animationTimingFunction: 'linear'
    }, options || {});

    var rippleContainer;
    var node = element[0];

    if (options.mousedown) {
      enableMousedown();
    }

    function rippleIsAllowed() {
      return !element.controller('noink') && !Util.isDisabled(element);
    }

    function enableMousedown() {
      element.on(POINTERDOWN_EVENT, onPointerDown);

      function onPointerDown(ev) {
        if (!rippleIsAllowed()) return;

        // Stop listening to pointer down for now, until the user lifts his finger/mouse
        element.off(POINTERDOWN_EVENT, onPointerDown);

        var rippleEl = createRippleFromEvent(ev);
        var ripplePauseTimeout = $timeout(pauseRipple, options.mousedownPauseTime, false);

        rippleEl.on('$destroy', cancelRipplePause);
        element.one(POINTERUP_EVENT, onPointerUp);

        function onPointerUp() {
          cancelRipplePause();
          rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'running');
          element.on(POINTERDOWN_EVENT, onPointerDown);
        }
        function pauseRipple() {
          rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'paused');
        }
        function cancelRipplePause() {
          $timeout.cancel(ripplePauseTimeout);
        }
      }
    }

    function createRippleFromEvent(ev) {
      ev = ev.touches ? ev.touches[0] : ev;
      return createRipple(ev.pageX, ev.pageY, true);
    }
    function createRipple(left, top, positionsAreAbsolute) {

      var rippleEl = angular.element('
')
        .css(
          $materialEffects.ANIMATION_DURATION,
          options.animationDuration + 'ms'
        )
        .css(
          $materialEffects.ANIMATION_NAME,
          options.animationName
        )
        .css(
          $materialEffects.ANIMATION_TIMING,
          options.animationTimingFunction
        )
        .on($materialEffects.ANIMATIONEND_EVENT, function() {
          rippleEl.remove();
        });

      if (!rippleContainer) {
        rippleContainer = angular.element('
');
        element.append(rippleContainer);
      }
      rippleContainer.append(rippleEl);

      if (options.center) {
        left = rippleContainer.prop('offsetWidth') / 2;
        top = rippleContainer.prop('offsetHeight') / 2;
      } else if (positionsAreAbsolute) {
        var elementRect = node.getBoundingClientRect();
        left -= elementRect.left;
        top -= elementRect.top;
      }

      var finalSize = rippleContainer.prop('offsetWidth');

      // TODO don't use px, make setRippleCss fix that
      var css = {
        'background-color': $window.getComputedStyle(rippleEl[0]).color ||
          $window.getComputedStyle(node).color,
        'border-radius': (finalSize / 2) + 'px',

        left: (left - finalSize / 2) + 'px',
        width: finalSize + 'px',

        top: (top - finalSize / 2) + 'px',
        height: finalSize + 'px',
      };
      css[$materialEffects.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
      rippleEl.css(css);

      return rippleEl;
    }
  }

}
<|endoftext|>


<commit_before>import React from 'react';
import Chart from 'components/chart';
import ClipShape from 'components/clip-shape';
import HorizontalBar from 'components/horizontal-bar';
import Range from 'ranges/range';
import LinearDomain from 'domains/linear-domain';
import LinearScale from 'scales/linear-scale';
import { numericDescending } from 'utils/sort-util';
import Shape from 'constants/shape';
import getChartHeight from 'decorators/get-chart-height';
import getChartWidth from 'decorators/get-chart-width';

@getChartHeight
@getChartWidth
export default class HorizontalBarChart extends Chart {

  static propTypes = {
    barHeight: React.PropTypes.number.isRequired,
    data: React.PropTypes.array.isRequired,
    detailIconHeight: React.PropTypes.number.isRequired,
    height: React.PropTypes.number.isRequired,
    iconHeight: React.PropTypes.number.isRequired,
    iconShape: React.PropTypes.string.isRequired,
    iconWidth: React.PropTypes.number.isRequired,
    sortFunction: React.PropTypes.func.isRequired,
    sortKey: React.PropTypes.string.isRequired,
    startAtZero: React.PropTypes.bool.isRequired,
    verticalSpacing: React.PropTypes.number.isRequired,
    width: React.PropTypes.number.isRequired,
  }

  static defaultProps = {
    barHeight: 40,
    data: [],
    detailIconHeight: 20,
    height: 0,
    iconHeight: 40,
    iconWidth: 40,
    iconShape: Shape.ELLIPSE,
    sortFunction: numericDescending,
    sortKey: 'value',
    startAtZero: false,
    verticalSpacing: 6,
    width: 0,
  }

  state = {
    data: [],
    height: this.props.height,
    iconClipPathId: 'iconClipPath',
    scale: Function,
    width: this.props.width,
  }

  getData(data, sortFunction, sortKey) {
    return sortFunction(data.slice(), sortKey);
  }

  getScale(data, startAtZero, width) {
    return new LinearScale(
      new LinearDomain([{data: data}], startAtZero),
      new Range(width),
    );
  }

  setStateFromProps(props) {
    const node = React.findDOMNode(this);
    const height = this.getChartHeight(props.height, node);
    const width = this.getChartWidth(props.width, node);
    const data = this.getData(props.data, props.sortFunction, props.sortKey);
    const scale = this.getScale(data, props.startAtZero, width);
    this.setState({
      data: data,
      height: height,
      scale: scale,
      width: width,
    });
  }

  render() {
    return (
      <svg
        className={'horizontal-bar-chart'}
        height={this.state.height}
        width={this.state.width}>

        <defs>
          <ClipShape
            height={this.props.iconHeight}
            id={this.state.iconClipPathId}
            type={this.props.iconShape}
            width={this.props.iconHeight} />
        </defs>

        <g className={'horizontal-bar-container'}>
          {this.state.data.map((datum, index) => (
            <HorizontalBar
              corners={datum.corners}
              detailIcon={datum.detailIcon}
              detailIconHeight={this.props.detailIconHeight}
              detailLabel={datum.detailLabel}
              height={this.props.barHeight}
              icon={datum.icon}
              iconClipPathId={this.state.iconClipPathId}
              iconHeight={this.props.iconHeight}
              iconWidth={this.props.iconWidth}
              index={index}
              key={index}
              label={datum.label}
              scale={this.state.scale}
              style={datum.style}
              value={datum.value}
              verticalSpacing={this.props.verticalSpacing}
              width={this.state.width} />
          ))}
        </g>

      </svg>
    );
  }

}
<commit_msg>Calculate horizontal bar chart height from data.<commit_after>import React from 'react';
import Chart from 'components/chart';
import ClipShape from 'components/clip-shape';
import HorizontalBar from 'components/horizontal-bar';
import Range from 'ranges/range';
import LinearDomain from 'domains/linear-domain';
import LinearScale from 'scales/linear-scale';
import { numericDescending } from 'utils/sort-util';
import { isValid } from 'validators/number-validator';
import Shape from 'constants/shape';
import getChartWidth from 'decorators/get-chart-width';

@getChartWidth
export default class HorizontalBarChart extends Chart {

  static propTypes = {
    barHeight: React.PropTypes.number.isRequired,
    data: React.PropTypes.array.isRequired,
    detailIconHeight: React.PropTypes.number.isRequired,
    height: React.PropTypes.number.isRequired,
    iconHeight: React.PropTypes.number.isRequired,
    iconShape: React.PropTypes.string.isRequired,
    iconWidth: React.PropTypes.number.isRequired,
    sortFunction: React.PropTypes.func.isRequired,
    sortKey: React.PropTypes.string.isRequired,
    startAtZero: React.PropTypes.bool.isRequired,
    verticalSpacing: React.PropTypes.number.isRequired,
    width: React.PropTypes.number.isRequired,
  }

  static defaultProps = {
    barHeight: 40,
    data: [],
    detailIconHeight: 20,
    height: 0,
    iconHeight: 40,
    iconWidth: 40,
    iconShape: Shape.ELLIPSE,
    sortFunction: numericDescending,
    sortKey: 'value',
    startAtZero: false,
    verticalSpacing: 6,
    width: 0,
  }

  state = {
    data: [],
    height: this.props.height,
    iconClipPathId: 'iconClipPath',
    scale: Function,
    width: this.props.width,
  }

  getChartHeight(height, data, barHeight, verticalSpacing) {
    if (isValid(height) && height > 0) {
      return Math.ceil(height);
    }
    if (data && data.length) {
      return Math.ceil((barHeight + verticalSpacing) * data.length);
    }
    return 0;
  }

  getData(data, sortFunction, sortKey) {
    return sortFunction(data.slice(), sortKey);
  }

  getScale(data, startAtZero, width) {
    return new LinearScale(
      new LinearDomain([{data: data}], startAtZero),
      new Range(width),
    );
  }

  setStateFromProps(props) {
    const data = this.getData(
      props.data,
      props.sortFunction,
      props.sortKey);
    const height = this.getChartHeight(
      props.height,
      data,
      props.barHeight,
      props.verticalSpacing);
    const node = React.findDOMNode(this);
    const width = this.getChartWidth(
      props.width,
      node);
    const scale = this.getScale(
      data,
      props.startAtZero,
      width);
    this.setState({
      data: data,
      height: height,
      scale: scale,
      width: width,
    });
  }

  render() {
    return (
      <svg
        className={'horizontal-bar-chart'}
        height={this.state.height}
        width={this.state.width}>

        <defs>
          <ClipShape
            height={this.props.iconHeight}
            id={this.state.iconClipPathId}
            type={this.props.iconShape}
            width={this.props.iconHeight} />
        </defs>

        <g className={'horizontal-bar-container'}>
          {this.state.data.map((datum, index) => (
            <HorizontalBar
              corners={datum.corners}
              detailIcon={datum.detailIcon}
              detailIconHeight={this.props.detailIconHeight}
              detailLabel={datum.detailLabel}
              height={this.props.barHeight}
              icon={datum.icon}
              iconClipPathId={this.state.iconClipPathId}
              iconHeight={this.props.iconHeight}
              iconWidth={this.props.iconWidth}
              index={index}
              key={index}
              label={datum.label}
              scale={this.state.scale}
              style={datum.style}
              value={datum.value}
              verticalSpacing={this.props.verticalSpacing}
              width={this.state.width} />
          ))}
        </g>

      </svg>
    );
  }

}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { selectors, actions } from 'reducers/tasks.reducer'
import { connect } from 'react-redux'
import {bindActionCreators} from 'redux'

import {renderAsyncSelect as AsyncSelect} from 'components/shared/FormFields'
import {searchUsers} from 'services/selectHelpers'
import {Link} from 'react-router'

import ProgressBar from 'react-toolbox/lib/progress_bar/ProgressBar'
import IconButton from 'react-toolbox/lib/button/IconButton'
import Input from 'react-toolbox/lib/input/Input'
import Button from 'react-toolbox/lib/button/Button'
import Initials from './Initials'
import BackButton from 'components/shared/BackButton'
import ConfirmDeleteButton from 'components/shared/ConfirmDeleteButton'
import styled from 'styled-components'

const SpaceBetween = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
`
const NameInput = styled.input`
  flex: 1;
  font-size: 1.5em;
  margin: 1rem 0;
`
const TextArea = styled.textarea`
  font-family: inherit;
  font-size: inherit;
  resize: vertical;
  display: block;
  height: 120px;
  overflow-x: hidden;
  padding: 8px;
  width: 100%;
`
const List = styled.ul`
  list-style: none;
  padding: 0;
  margin: 1rem;
  margin-top: 2rem;
  opacity: ${props => props.loading ? 0.5 : 1};
`
const CommentBody = styled.p`
  background: white;
  border: 1px solid #ccc;
  padding: 1em;
  margin-top: 8px;
  margin-bottom: 4px;
`
const CommentActions = styled.div`
  display: flex;
  justify-content: flex-end;
  align-items: center;
  .material-icons {
    color: #666;
  }
  .icon-delete {
    cursor: pointer;
  }
  .icon-delete:hover {
    color: var(--palette-red-500);
  }
`

class TaskForm extends Component {
  state = {
    asignee: null,
    estimatedTime: '00:00',
    description: '',
    name: '',
    newComment: '',
    editModes: {
      description: false,
      name: false
    }
  }
  componentDidMount() {
    const {task, actions, routeParams} = this.props
    if(!task) {
      actions.fetchOne(routeParams._id)
      .then(res => res.value)
      .then(task => this.resetTask(task))
    } else {
      this.resetTask(task)
    }
  }
  resetTask(task) {
    this.setState({
      name: task.name || '',
      description: task.description || '',
      estimatedTime: task.estimatedTime || '',
      asignee: task.asignee && {
        value: task.asignee._id,
        label: task.asignee.full_name
      }
    })
  }
  handleChange = name => (ev) => {
    const {value} = ev.target
    this.setState({ [name]: value })
  }
  handleNameKeyDown = (ev) => {
    const enterKeycode = 13
    const escapeKeycode = 27
    const keycode = ev.keyCode
    if(keycode === enterKeycode || keycode === escapeKeycode) {
      this.editName()
    }
  }
  editName = () => {
    const data = {
      ...this.props.task,
      name: this.state.name
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({editModes: {name: false}})
    })
  }
  editDescription = () => {
    const data = {
      ...this.props.task,
      description: this.state.description
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({editModes: {description: false}})
    })
  }
  editTimeAndAsignee() {
    const {task, actions} = this.props
    const {estimatedTime, asignee} = this.state
    const data = {
      ...task,
      estimatedTime,
      asignee: asignee.value
    }
    actions.save(data, true)
  }
  deleteTask = () => {
    const {task, actions, router} = this.props
    actions.delete(task)
    .then(() => {
      router.push(`/projects/${task.project}/tasks`)
    })
  }
  addComment(ev) {
    ev.preventDefault()
    const newComment = {
      authorName: this.props.username,
      authorId: this.props.userid,
      body: this.state.newComment
    }
    const data = {
      ...this.props.task,
      comments: [
        ...this.props.task.comments,
        newComment
      ]
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({newComment: ''})
    })
  }
  deleteComment(index) {
    const {comments} = this.props.task
    const data = {
      ...this.props.task,
      comments: [
        ...comments.slice(0, index),
        ...comments.slice(index+1, comments.length)
      ]
    }
    this.props.actions.save(data, true)
  }
  getInitials(name) {
    return name.split(' ').map(str => str[0])
  }
  formatDate(dateString) {
    const time = new Date(dateString).toLocaleTimeString()
    const date = new Date(dateString).toLocaleDateString()
    return `el ${date} a las ${time}`
  }
  userOwnsComment(comment) {
    return this.props.userid === comment.authorId
  }
  renderTaskForm () {
    const {asignee, estimatedTime, description, name, editModes} = this.state
    const {task = {}} = this.props
    return (

        <SpaceBetween>
          {editModes.name ? (
            <NameInput
              type="text"
              value={name}
              autoFocus
              onKeyDown={this.handleNameKeyDown}
              onBlur={this.editName}
              onChange={this.handleChange("name")} />
          ) : (

 this.setState({editModes: {name: true}})}>
              {task.name}

          )}
          <IconButton
            onClick={() => this.setState({
              editModes: {name: !editModes.name}
            })}
            title={editModes.name ? 'Completar edicion':'Editar nombre de la tarea'}
            icon={editModes.name ? 'done':'edit'} />
        </SpaceBetween>
        <SpaceBetween>

            Descripción

          <IconButton
            onClick={() => this.setState({
              editModes: {description: !editModes.description}
            })}
            icon={editModes.description ? 'done':'edit'}
            title={editModes.description ? 'Completar edición':'Editar descripcion'} />
        </SpaceBetween>
        {editModes.description ? (
          <TextArea
            id="description"
            value={description}
            autoFocus
            onBlur={this.editDescription}
            onChange={this.handleChange("description")}
          />
        ) : (

{description}

        )}
        <Input
          icon="timer"
          type="text"
          label="Tiempo estimado"
          hint="00:00"
          pattern="[0-9]{2}:[0-9]{2}"
          value={estimatedTime}
          onChange={estimatedTime => this.setState({estimatedTime})} />
        <AsyncSelect
          name="asignee"
          icon="person_outline"
          label="Asignado a"
          className="select"
          placeholder="Escribe para buscar"
          loadOptions={searchUsers}
          meta={{}}
          input={{
            value: asignee,
            onChange: asignee => this.setState({asignee})
          }}
          style={{flex: 1}}
        />

          <Button primary raised onClick={() => this.editTimeAndAsignee()}>
            Guardar
          </Button>
          <Link to={`/projects/${task.project}/tasks`}>
            <Button>Cancelar</Button>
          </Link>

          <ConfirmDeleteButton button={(
            <Button style={{color: 'var(--palette-red-500)'}}
                    title="Borrar tarea">
              Borrar
            </Button>
          )} onDelete={this.deleteTask}></ConfirmDeleteButton>

    );
  }
  renderCommentsForm() {
    const {username="", loading} = this.props
    return (

Comentarios

        <form style={{display: 'flex'}} onSubmit={ev => this.addComment(ev)}>

            <Initials>{this.getInitials(username)}</Initials>

            <TextArea
              placeholder="Escribe tu comentario aqui"
              value={this.state.newComment}
              onChange={this.handleChange("newComment")} />
            <Button style={{marginTop: '8px'}}
                    disabled={loading || !this.state.newComment}
                    type="submit" primary raised>
              {loading ? 'Cargando...':'Añadir'}
            </Button>

        </form>

    )
  }
  renderCommentList() {
    const {comments = []} = this.props.task
    return (
      <List loading={this.props.loading}>
        {comments.map((comment, index) => (

            <SpaceBetween>

                <Initials>
                  {this.getInitials(comment.authorName)}
                </Initials>

                  {comment.authorName}

                {this.formatDate(comment.publishedAt)}

            </SpaceBetween>
            <CommentBody>{comment.body}</CommentBody>
            <CommentActions>
              {this.userOwnsComment(comment) ? (
                 this.deleteComment(index)}
                   title="Borrar comentario">close
              ) : null}
            </CommentActions>

        ))}
      </List>
    );
  }
  render() {
    const {loading, task} = this.props
    return (

        <BackButton style={{margin: '.5em'}} router={this.props.router} />
        {loading && (

            <ProgressBar type='circular' mode='indeterminate' />

              Cargando ...

        )}
        {task && this.renderTaskForm()}
        {task && this.renderCommentsForm()}
        {task && this.renderCommentList()}

    )
  }
}

export default connect(
  (state, props) => {
    const id = props.routeParams._id
    const task = selectors.getOne(state, id)
    const loading = selectors.getLoading(state)
    const username = state.auth.full_name
    const userid = state.auth._id
    return {task, loading, username, userid}
  },
  dispatch => ({
    actions: bindActionCreators(actions, dispatch)
  })
)(TaskForm)
<commit_msg>feat: add time link to task form<commit_after>import React, { Component } from 'react';
import { selectors, actions } from 'reducers/tasks.reducer'
import { connect } from 'react-redux'
import {bindActionCreators} from 'redux'

import {renderAsyncSelect as AsyncSelect} from 'components/shared/FormFields'
import {searchUsers} from 'services/selectHelpers'
import {Link} from 'react-router'

import ProgressBar from 'react-toolbox/lib/progress_bar/ProgressBar'
import IconButton from 'react-toolbox/lib/button/IconButton'
import Input from 'react-toolbox/lib/input/Input'
import Button from 'react-toolbox/lib/button/Button'
import Initials from './Initials'
import BackButton from 'components/shared/BackButton'
import ConfirmDeleteButton from 'components/shared/ConfirmDeleteButton'
import styled from 'styled-components'

const SpaceBetween = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
`
const NameInput = styled.input`
  flex: 1;
  font-size: 1.5em;
  margin: 1rem 0;
`
const TextArea = styled.textarea`
  font-family: inherit;
  font-size: inherit;
  resize: vertical;
  display: block;
  height: 120px;
  overflow-x: hidden;
  padding: 8px;
  width: 100%;
`
const List = styled.ul`
  list-style: none;
  padding: 0;
  margin: 1rem;
  margin-top: 2rem;
  opacity: ${props => props.loading ? 0.5 : 1};
`
const CommentBody = styled.p`
  background: white;
  border: 1px solid #ccc;
  padding: 1em;
  margin-top: 8px;
  margin-bottom: 4px;
`
const CommentActions = styled.div`
  display: flex;
  justify-content: flex-end;
  align-items: center;
  .material-icons {
    color: #666;
  }
  .icon-delete {
    cursor: pointer;
  }
  .icon-delete:hover {
    color: var(--palette-red-500);
  }
`

class TaskForm extends Component {
  state = {
    asignee: null,
    estimatedTime: '00:00',
    description: '',
    name: '',
    newComment: '',
    editModes: {
      description: false,
      name: false
    }
  }
  componentDidMount() {
    const {task, actions, routeParams} = this.props
    if(!task) {
      actions.fetchOne(routeParams._id)
      .then(res => res.value)
      .then(task => this.resetTask(task))
    } else {
      this.resetTask(task)
    }
  }
  resetTask(task) {
    this.setState({
      name: task.name || '',
      description: task.description || '',
      estimatedTime: task.estimatedTime || '',
      asignee: task.asignee && {
        value: task.asignee._id,
        label: task.asignee.full_name
      }
    })
  }
  handleChange = name => (ev) => {
    const {value} = ev.target
    this.setState({ [name]: value })
  }
  handleNameKeyDown = (ev) => {
    const enterKeycode = 13
    const escapeKeycode = 27
    const keycode = ev.keyCode
    if(keycode === enterKeycode || keycode === escapeKeycode) {
      this.editName()
    }
  }
  editName = () => {
    const data = {
      ...this.props.task,
      name: this.state.name
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({editModes: {name: false}})
    })
  }
  editDescription = () => {
    const data = {
      ...this.props.task,
      description: this.state.description
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({editModes: {description: false}})
    })
  }
  editTimeAndAsignee() {
    const {task, actions} = this.props
    const {estimatedTime, asignee} = this.state
    const data = {
      ...task,
      estimatedTime,
      asignee: asignee.value
    }
    actions.save(data, true)
  }
  deleteTask = () => {
    const {task, actions, router} = this.props
    actions.delete(task)
    .then(() => {
      router.push(`/projects/${task.project}/tasks`)
    })
  }
  addComment(ev) {
    ev.preventDefault()
    const newComment = {
      authorName: this.props.username,
      authorId: this.props.userid,
      body: this.state.newComment
    }
    const data = {
      ...this.props.task,
      comments: [
        ...this.props.task.comments,
        newComment
      ]
    }
    this.props.actions.save(data, true)
    .then(() => {
      this.setState({newComment: ''})
    })
  }
  deleteComment(index) {
    const {comments} = this.props.task
    const data = {
      ...this.props.task,
      comments: [
        ...comments.slice(0, index),
        ...comments.slice(index+1, comments.length)
      ]
    }
    this.props.actions.save(data, true)
  }
  getInitials(name) {
    return name.split(' ').map(str => str[0])
  }
  formatDate(dateString) {
    const time = new Date(dateString).toLocaleTimeString()
    const date = new Date(dateString).toLocaleDateString()
    return `el ${date} a las ${time}`
  }
  userOwnsComment(comment) {
    return this.props.userid === comment.authorId
  }
  renderTaskForm () {
    const {asignee, estimatedTime, description, name, editModes} = this.state
    const {task = {}} = this.props
    return (

        <SpaceBetween>
          {editModes.name ? (
            <NameInput
              type="text"
              value={name}
              autoFocus
              onKeyDown={this.handleNameKeyDown}
              onBlur={this.editName}
              onChange={this.handleChange("name")} />
          ) : (

 this.setState({editModes: {name: true}})}>
              {task.name}

          )}
          <IconButton
            onClick={() => this.setState({
              editModes: {name: !editModes.name}
            })}
            title={editModes.name ? 'Completar edicion':'Editar nombre de la tarea'}
            icon={editModes.name ? 'done':'edit'} />
        </SpaceBetween>
        <SpaceBetween>

            Descripción

          <IconButton
            onClick={() => this.setState({
              editModes: {description: !editModes.description}
            })}
            icon={editModes.description ? 'done':'edit'}
            title={editModes.description ? 'Completar edición':'Editar descripcion'} />
        </SpaceBetween>
        {editModes.description ? (
          <TextArea
            id="description"
            value={description}
            autoFocus
            onBlur={this.editDescription}
            onChange={this.handleChange("description")}
          />
        ) : (

{description}

        )}
        <Input
          icon="timer"
          type="text"
          label="Tiempo estimado"
          hint="00:00"
          pattern="[0-9]{2}:[0-9]{2}"
          value={estimatedTime}
          onChange={estimatedTime => this.setState({estimatedTime})} />
        <AsyncSelect
          name="asignee"
          icon="person_outline"
          label="Asignado a"
          className="select"
          placeholder="Escribe para buscar"
          loadOptions={searchUsers}
          meta={{}}
          input={{
            value: asignee,
            onChange: asignee => this.setState({asignee})
          }}
          style={{flex: 1}}
        />

          <Button primary raised onClick={() => this.editTimeAndAsignee()}>
            Guardar
          </Button>
          <Link to={`/projects/${task.project}/tasks`}>
            <Button>Cancelar</Button>
          </Link>

          <ConfirmDeleteButton button={(
            <Button style={{color: 'var(--palette-red-500)'}}
                    title="Borrar tarea">
              Borrar
            </Button>
          )} onDelete={this.deleteTask}></ConfirmDeleteButton>

    );
  }
  renderCommentsForm() {
    const {username="", loading} = this.props
    return (

Comentarios

        <form style={{display: 'flex'}} onSubmit={ev => this.addComment(ev)}>

            <Initials>{this.getInitials(username)}</Initials>

            <TextArea
              placeholder="Escribe tu comentario aqui"
              value={this.state.newComment}
              onChange={this.handleChange("newComment")} />
            <Button style={{marginTop: '8px'}}
                    disabled={loading || !this.state.newComment}
                    type="submit" primary raised>
              {loading ? 'Cargando...':'Añadir'}
            </Button>

        </form>

    )
  }
  renderCommentList() {
    const {comments = []} = this.props.task
    return (
      <List loading={this.props.loading}>
        {comments.map((comment, index) => (

            <SpaceBetween>

                <Initials>
                  {this.getInitials(comment.authorName)}
                </Initials>

                  {comment.authorName}

                {this.formatDate(comment.publishedAt)}

            </SpaceBetween>
            <CommentBody>{comment.body}</CommentBody>
            <CommentActions>
              {this.userOwnsComment(comment) ? (
                 this.deleteComment(index)}
                   title="Borrar comentario">close
              ) : null}
            </CommentActions>

        ))}
      </List>
    );
  }
  render() {
    const {loading, task} = this.props
    return (

        <SpaceBetween>
          <BackButton
            style={{margin: '.5em'}}
            router={this.props.router} />
          {task && (
            <Link to={`/tasks/${task._id}/times`}>
              <Button style={{
                padding: '0 5px',
                marginRight: '5px',
                minWidth: 0,
                textAlign: 'left',
                textTransform: 'none'
              }} icon="timer">Tiempos</Button>
            </Link>
          )}
        </SpaceBetween>
        {loading && (

            <ProgressBar type='circular' mode='indeterminate' />

              Cargando ...

        )}
        {task && this.renderTaskForm()}
        {task && this.renderCommentsForm()}
        {task && this.renderCommentList()}

    )
  }
}

export default connect(
  (state, props) => {
    const id = props.routeParams._id
    const task = selectors.getOne(state, id)
    const loading = selectors.getLoading(state)
    const username = state.auth.full_name
    const userid = state.auth._id
    return {task, loading, username, userid}
  },
  dispatch => ({
    actions: bindActionCreators(actions, dispatch)
  })
)(TaskForm)
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc function
 * @name dashboardJsApp.controller:MainCtrl
 * @description
 * # MainCtrl
 * Controller of the dashboardJsApp
 */
angular.module('dashboardJsApp')
	.controller('Visualisation_activeusers_Ctrl', ['$scope', 'RequestService', 'Course', 'AuthService', function ($scope, RequestService, Course, AuthService) {
		$scope.auth = AuthService;
        $scope.$parent.state = "loading";

        $scope.APIBASE = APIBASE;

        $scope.refresh = false;
        $scope.importantDates = [];

        $scope.refreshData = function() {
            $scope.$parent.state = "loading";
            $scope.refresh = true;
            $scope.loadData();
            $scope.refresh = false;
        }

        $scope.loadData = function () {

            var refresh = '';

            if($scope.refresh) {
                refresh = '?refreshcache=true';
            }

			if ($scope.auth.isAuthenticated()) {
				RequestService.async('/students/active/' + Course.currentCourse + '/'+refresh).then(function(data) {

                    RequestService.async('/meta/courseinfo/').then(function(info) {
                        for (var key in info) {
                            if (info[key]['id'] === Course.currentCourse || Course.currentCourse == 'allcourses') {
                                if ('start' in info[key]) {
                                    $scope.importantDates.push({ 'name': 'Course Start for ' + info[key]['display_name'], 'date': info[key]['start'] });
                                }

                                if ('end' in info[key]) {
                                    $scope.importantDates.push({ 'name': 'Course End for' + info[key]['display_name'], 'date': info[key]['end'] });
                                }
                            }
                        }

                        var formattedDailyData = [{ name: 'Daily', data: [] }];
                        var formattedWeeklyData = [{ name: 'Weekly', data: [] }];

                        for (var day in data['days']) {
                            formattedDailyData[0].data.push({ date: day, value: data['days'][day] });
                        }

                        for (var week in data['weeks']) {
                            formattedWeeklyData[0].data.push({ date: week, value: data['weeks'][week] });
                        }

                        $scope.dailyData = formattedDailyData;
                        $scope.weeklyData = formattedWeeklyData;
                        $scope.pc = {}
                        RequestService.async('/students/personcourse/' + Course.currentCourse + '/'+refresh).then(function(pcdata) {
                            $scope.pc.enrolled = 0;
                            $scope.pc.viewed = 0;
                            $scope.pc.explored = 0;
                            $scope.pc.certified = 0;
                            $scope.pc.onechapter = 0;
                            $scope.pc.threechapters = 0;

                            $scope.pc.viewed_stupercent = 0;
                            $scope.pc.explored_stupercent = 0;
                            $scope.pc.certified_stupercent = 0;
                            $scope.pc.onechapter_stupercent = 0;
                            $scope.pc.threechapters_stupercent = 0;

                            $scope.pc.threechapters_lastcol = 0;
                            $scope.pc.explored_lastcol = 0;
                            $scope.pc.certified_lastcol = 0;

                            for(var person in pcdata) {
                                $scope.pc.enrolled += 1;
                                if(pcdata[person].certified) {
                                    $scope.pc.certified += 1;
                                }
                                if(pcdata[person].viewed) {
                                    $scope.pc.viewed += 1;
                                }
                                if(pcdata[person].explored) {
                                    $scope.pc.explored += 1;
                                }
                                if(pcdata[person].nchapters > 2) {
                                    $scope.pc.threechapters += 1;
                                }
                            }
                            $scope.pc.viewed_stupercent = Math.round($scope.pc.viewed/$scope.pc.enrolled*100)+"%";
                            $scope.pc.explored_stupercent = Math.round($scope.pc.explored/$scope.pc.enrolled*100)+"%";
                            $scope.pc.certified_stupercent = Math.round($scope.pc.certified/$scope.pc.enrolled*100)+"%";
                            $scope.pc.onechapter_stupercent = Math.round($scope.pc.onechapter/$scope.pc.enrolled*100)+"%";
                            $scope.pc.threechapters_stupercent = Math.round($scope.pc.threechapters/$scope.pc.enrolled*100)+"%";

                            $scope.pc.threechapters_lastcol = Math.round($scope.pc.threechapters/$scope.pc.viewed*100)+"%";
                            $scope.pc.explored_lastcol = Math.round($scope.pc.explored/$scope.pc.threechapters*100)+"%";
                            $scope.pc.certified_lastcol = Math.round($scope.pc.certified/$scope.pc.explored*100)+"%";

                            console.log($scope.pc.enrolled);
                        });

                        $scope.$parent.state = "running";
                    });
				});
			}
        }

		$scope.$watch('auth.isAuthenticated()', $scope.loadData, true);
	}]);<commit_msg>Fixed a typo<commit_after>'use strict';

/**
 * @ngdoc function
 * @name dashboardJsApp.controller:MainCtrl
 * @description
 * # MainCtrl
 * Controller of the dashboardJsApp
 */
angular.module('dashboardJsApp')
	.controller('Visualisation_activeusers_Ctrl', ['$scope', 'RequestService', 'Course', 'AuthService', function ($scope, RequestService, Course, AuthService) {
		$scope.auth = AuthService;
        $scope.$parent.state = "loading";

        $scope.APIBASE = APIBASE;

        $scope.refresh = false;
        $scope.importantDates = [];

        $scope.refreshData = function() {
            $scope.$parent.state = "loading";
            $scope.refresh = true;
            $scope.loadData();
            $scope.refresh = false;
        }

        $scope.loadData = function () {

            var refresh = '';

            if($scope.refresh) {
                refresh = '?refreshcache=true';
            }

			if ($scope.auth.isAuthenticated()) {
				RequestService.async('/students/active/' + Course.currentCourse + '/'+refresh).then(function(data) {

                    RequestService.async('/meta/courseinfo/').then(function(info) {
                        for (var key in info) {
                            if (info[key]['id'] === Course.currentCourse || Course.currentCourse == 'allcourses') {
                                if ('start' in info[key]) {
                                    $scope.importantDates.push({ 'name': 'Course Start for ' + info[key]['display_name'], 'date': info[key]['start'] });
                                }

                                if ('end' in info[key]) {
                                    $scope.importantDates.push({ 'name': 'Course End for ' + info[key]['display_name'], 'date': info[key]['end'] });
                                }
                            }
                        }

                        var formattedDailyData = [{ name: 'Daily', data: [] }];
                        var formattedWeeklyData = [{ name: 'Weekly', data: [] }];

                        for (var day in data['days']) {
                            formattedDailyData[0].data.push({ date: day, value: data['days'][day] });
                        }

                        for (var week in data['weeks']) {
                            formattedWeeklyData[0].data.push({ date: week, value: data['weeks'][week] });
                        }

                        $scope.dailyData = formattedDailyData;
                        $scope.weeklyData = formattedWeeklyData;
                        $scope.pc = {}
                        RequestService.async('/students/personcourse/' + Course.currentCourse + '/'+refresh).then(function(pcdata) {
                            $scope.pc.enrolled = 0;
                            $scope.pc.viewed = 0;
                            $scope.pc.explored = 0;
                            $scope.pc.certified = 0;
                            $scope.pc.onechapter = 0;
                            $scope.pc.threechapters = 0;

                            $scope.pc.viewed_stupercent = 0;
                            $scope.pc.explored_stupercent = 0;
                            $scope.pc.certified_stupercent = 0;
                            $scope.pc.onechapter_stupercent = 0;
                            $scope.pc.threechapters_stupercent = 0;

                            $scope.pc.threechapters_lastcol = 0;
                            $scope.pc.explored_lastcol = 0;
                            $scope.pc.certified_lastcol = 0;

                            for(var person in pcdata) {
                                $scope.pc.enrolled += 1;
                                if(pcdata[person].certified) {
                                    $scope.pc.certified += 1;
                                }
                                if(pcdata[person].viewed) {
                                    $scope.pc.viewed += 1;
                                }
                                if(pcdata[person].explored) {
                                    $scope.pc.explored += 1;
                                }
                                if(pcdata[person].nchapters > 2) {
                                    $scope.pc.threechapters += 1;
                                }
                            }
                            $scope.pc.viewed_stupercent = Math.round($scope.pc.viewed/$scope.pc.enrolled*100)+"%";
                            $scope.pc.explored_stupercent = Math.round($scope.pc.explored/$scope.pc.enrolled*100)+"%";
                            $scope.pc.certified_stupercent = Math.round($scope.pc.certified/$scope.pc.enrolled*100)+"%";
                            $scope.pc.onechapter_stupercent = Math.round($scope.pc.onechapter/$scope.pc.enrolled*100)+"%";
                            $scope.pc.threechapters_stupercent = Math.round($scope.pc.threechapters/$scope.pc.enrolled*100)+"%";

                            $scope.pc.threechapters_lastcol = Math.round($scope.pc.threechapters/$scope.pc.viewed*100)+"%";
                            $scope.pc.explored_lastcol = Math.round($scope.pc.explored/$scope.pc.threechapters*100)+"%";
                            $scope.pc.certified_lastcol = Math.round($scope.pc.certified/$scope.pc.explored*100)+"%";

                            console.log($scope.pc.enrolled);
                        });

                        $scope.$parent.state = "running";
                    });
				});
			}
        }

		$scope.$watch('auth.isAuthenticated()', $scope.loadData, true);
	}]);<|endoftext|>