<commit_before>/**
 Jasmine Reporter that outputs test results to the browser console.
 Useful for running in a headless environment such as PhantomJs, ZombieJs etc.

 Usage:
 // From your html file that loads jasmine:
 jasmine.getEnv().execute();
*/

(function() {
  var ConsoleReporter,
      root = this;

  if (!jasmine) {
    throw "jasmine library isn't loaded!";
  }

  var ANSI = {};
  ANSI.color_map = {
      "green" : 32,
      "red"   : 31
  };

  ANSI.colorize_text = function(text, color) {
    var color_code = this.color_map[color];
    return "\033[" + color_code + "m" + text + "\033[0m";
  };

  ConsoleReporter = function() {
    if (!console || !console.log) { throw "console isn't present!"; }
    this.status = this.statuses.stopped;
  };

  var proto = ConsoleReporter.prototype;
  proto.statuses = {
    stopped : "stopped",
    running : "running",
    fail    : "fail",
    success : "success",
    skipped_or_pending : "skipped or pending"
  };

  proto.reportRunnerStarting = proto.jasmineStarted = function(runner) {
    this.status = this.statuses.running;
    this.start_time = (new Date()).getTime();
    this.executed_specs = 0;
    this.passed_specs = 0;
    this.skipped_or_pending_specs = 0;
    this.log("Starting...");
  };

  proto.reportRunnerResults = proto.jasmineDone = function(runner) {
    var failed = this.executed_specs - this.passed_specs;
    var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, ");
    var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
    var skipped_or_pending_str = this.skipped_or_pending_specs ? this.skipped_or_pending_specs + ' skipped or pending, ' : '';

    var color = (failed > 0)? "red" : "green";
    var dur = (new Date()).getTime() - this.start_time;

    this.log("");
    this.log("Finished");
    this.log("-----------------");
    this.log(spec_str + skipped_or_pending_str + fail_str + (dur/1000) + "s.", color);

    this.status = (failed > 0)? this.statuses.fail : this.statuses.success;

    /* Print something that signals that testing is over so that headless browsers
       like PhantomJs know when to terminate. */
    this.log("");
    this.log("ConsoleReporter finished");
  };

  proto.reportSpecStarting = proto.specStarted = function(spec) {
    this.executed_specs++;
  };

  proto.reportSpecResults = proto.specDone = function(spec) {
    if(spec.results) { //jasmine 1.x
      var specResult = spec.results()
      if(specResult.skipped) {
        this.skipped_or_pending_specs++;
        return;
      } else if(specResult.passed()) {
        this.passed_specs++;
        return;
      }
    } else { //jasmine 2.x
      if(spec.status === "passed") {
        this.passed_specs++;
        return;
      } else if(spec.status !== "failed") {
        this.skipped_or_pending_specs++;
        //Skipped or Pending
        return;
      }
    }

    var fullName, failedExpectations;
    if(spec.suite) { //jasmine 1.x
      fullName = spec.suite.description + " " + spec.description;
      failedExpectations = spec.results().getItems().map(function(expectation){
        console.log(JSON.stringify(expectation))
        return "  " + expectation.message +
               (expectation.trace && expectation.trace.stack ? "\n    " + expectation.trace.stack : "" );
      });
    } else { //jasmine 2.x
      fullName = spec.fullName;
      failedExpectations = spec.failedExpectations.map(function(expectation){
        if(expectation.message === "undefined: undefined") {
          return "  An unstructured exception was thrown (use `new Error(message)` for better output).";
        } else {
          return "  " + expectation.message + "\n" +
                 "    " + expectation.stack;
        }
      });
    }

    this.log(fullName, "red");
    this.log(failedExpectations.join("\n\n"), "red");
  };

  proto.log = function(str, color) {
    var text = (color != undefined)? ANSI.colorize_text(str, color) : str;
    console.log(text)
  };

  jasmine.ConsoleReporter = ConsoleReporter;
  jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
})();
<commit_msg>revert PR #150 - was causing incorrect failed count when pending or skipped specs are present<commit_after>/**
 Jasmine Reporter that outputs test results to the browser console.
 Useful for running in a headless environment such as PhantomJs, ZombieJs etc.

 Usage:
 // From your html file that loads jasmine:
 jasmine.getEnv().execute();
*/

(function() {
  var ConsoleReporter,
      root = this;

  if (!jasmine) {
    throw "jasmine library isn't loaded!";
  }

  var ANSI = {};
  ANSI.color_map = {
      "green" : 32,
      "red"   : 31
  };

  ANSI.colorize_text = function(text, color) {
    var color_code = this.color_map[color];
    return "\033[" + color_code + "m" + text + "\033[0m";
  };

  ConsoleReporter = function() {
    if (!console || !console.log) { throw "console isn't present!"; }
    this.status = this.statuses.stopped;
  };

  var proto = ConsoleReporter.prototype;
  proto.statuses = {
    stopped : "stopped",
    running : "running",
    fail    : "fail",
    success : "success",
    skipped_or_pending : "skipped or pending"
  };

  proto.reportRunnerStarting = proto.jasmineStarted = function(runner) {
    this.status = this.statuses.running;
    this.start_time = (new Date()).getTime();
    this.executed_specs = 0;
    this.passed_specs = 0;
    this.skipped_or_pending_specs = 0;
    this.log("Starting...");
  };

  proto.reportRunnerResults = proto.jasmineDone = function(runner) {
    var failed = this.executed_specs - this.passed_specs - this.skipped_or_pending_specs;
    var spec_str = this.executed_specs + (this.executed_specs === 1 ? " spec, " : " specs, ");
    var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
    var skipped_or_pending_str = this.skipped_or_pending_specs ? this.skipped_or_pending_specs + ' skipped or pending, ' : '';

    var color = (failed > 0)? "red" : "green";
    var dur = (new Date()).getTime() - this.start_time;

    this.log("");
    this.log("Finished");
    this.log("-----------------");
    this.log(spec_str + skipped_or_pending_str + fail_str + (dur/1000) + "s.", color);

    this.status = (failed > 0)? this.statuses.fail : this.statuses.success;

    /* Print something that signals that testing is over so that headless browsers
       like PhantomJs know when to terminate. */
    this.log("");
    this.log("ConsoleReporter finished");
  };

  proto.reportSpecStarting = proto.specStarted = function(spec) {
    this.executed_specs++;
  };

  proto.reportSpecResults = proto.specDone = function(spec) {
    if(spec.results) { //jasmine 1.x
      var specResult = spec.results()
      if(specResult.skipped) {
        this.skipped_or_pending_specs++;
        return;
      } else if(specResult.passed()) {
        this.passed_specs++;
        return;
      }
    } else { //jasmine 2.x
      if(spec.status === "passed") {
        this.passed_specs++;
        return;
      } else if(spec.status !== "failed") {
        this.skipped_or_pending_specs++;
        //Skipped or Pending
        return;
      }
    }

    var fullName, failedExpectations;
    if(spec.suite) { //jasmine 1.x
      fullName = spec.suite.description + " " + spec.description;
      failedExpectations = spec.results().getItems().map(function(expectation){
        console.log(JSON.stringify(expectation))
        return "  " + expectation.message +
               (expectation.trace && expectation.trace.stack ? "\n    " + expectation.trace.stack : "" );
      });
    } else { //jasmine 2.x
      fullName = spec.fullName;
      failedExpectations = spec.failedExpectations.map(function(expectation){
        if(expectation.message === "undefined: undefined") {
          return "  An unstructured exception was thrown (use `new Error(message)` for better output).";
        } else {
          return "  " + expectation.message + "\n" +
                 "    " + expectation.stack;
        }
      });
    }

    this.log(fullName, "red");
    this.log(failedExpectations.join("\n\n"), "red");
  };

  proto.log = function(str, color) {
    var text = (color != undefined)? ANSI.colorize_text(str, color) : str;
    console.log(text)
  };

  jasmine.ConsoleReporter = ConsoleReporter;
  jasmine.getEnv().addReporter(new jasmine.ConsoleReporter());
})();
<|endoftext|>


<commit_before>var fullproof = (function(NAMESPACE) {

	NAMESPACE.french = NAMESPACE.french||{};

	NAMESPACE.french.simpleform = (function(){

		var suffix_removals_verbs_raw = [
				                       // Below, common verbs suffix first
				    [/.../, /er(ai([st]?|ent)|i?(on[ts]|ez))$/, "e"],
				    [/.../, /ass(i?(ez?|ons)|e(nt|s)?)$/, "e"], // asse, asses, assez, assiez, assies*, if root length >= 3
				    [/.../, /assions$/, "e"], // assions if root lengh>=3
				    [/.../, /assent$/, "e"],   // assent if root lengh>=3

				    [/endr(ez?|ai[st]?|on[st])$/, "ã"],		// endrez, endrai, endrais, endrait, endrons, endront

				    [/.../, /iss(i?(ons|ez)|ai[st]?|ant(es?)?|es?)$/, "" ], // issions, issiez, issais, issait, issai, issant, issante, issantes, isses

				    [/irai(s|(en)?t)?$/, ""], // irai, irait, irais, iraient

				    [/.../, /e?oi(re?|t|s|ent)$/, ""],  // eoir, eoire, oir, oire, oit, ois, oient

				    [/.../, /aient$/, ""],     // removes aient
				    [/.../, /a[mt]es$/, ""], // removes ames, ates
				    [/i?ons$/, ""],   // removes ons, ions
				    [/ait$/, ""],     // removes ait
				    [/ent$/, ""],     // removes ent
				    [/i?e[rz]$/, "e"] // removes er, ez, iez

				];

				var suffix_removals_nouns_raw = [
				    [/inages?$/, "1"],	// "copinage" > "cop1"
				    [/.../, /ages?$/, ""], // "habillage" > "habill"
				    [/.../, /[aoie]tions?$/, ""], // "déclaration" > "déclar", not "nation"
				    [/og(ies?|ues?)$/, "og"], // "philologie" -> "philolog", "philologue" -> "philolog"
				    [/t(rices?|euses?)$/, "ter"], // "fédératrice" -> "fédérater","flatteuse" -> "flatter" (eur is -> er by another rule)
				    [/.../, /e(uses?|ries?|urs?)$/, "er"], // euse, euses, eries, eries, eur (flatteuse, flatterie, flatteur)
				    [/utions$/, "u"], // "pollution", "attribution" ! produces a "u", because "uer"$ is not removed (but "er"$ is).
				    [/[ae]n[cs]es?$/, "ãS"], // prudence" -> "prudã", "tolérance" -> "tolérã"
				    [/..al/, /ites?$/, ""], // // "anormalite" -> "anormal"
				    [/[ea]mment]/, "ã"], // prudemment -> "prudã"
					//
					//not processed:
					//* usion$ : not an interesting simplification, as there are not
					//  enough nominal cases. i.e. "diffusion", but "illusion",
					// "exclusion", "contusion", etc.
				    [/ives?$/, "if"], // // "consécutives" -> con
				    [/istes?$/, "isme"], // maybe a bit aggressive ?
				    [/ables?$/, ""], // "chiffrable" -> "chiffr". aggressive ?
				    [/[^ae]/, /ines?$/, "1"] // "citadine"->"citadin"
				 ];

				var phonetic_transforms_raw = [

				   [/n/, /t/, /iel/, "S"],
				   [false, /t/, /i[oea]/, "S"],

				   // the A LETTER
				   [false, /ain/, /[^aeiouymn].*|$/, "1" ], // copain->cop1, complainte->compl1te
				   [/ai(s$)?/, "e"],
				   [false, /am/, /[^aeiouymn].*|$/, "ã" ], //  crampe->crãpe
				   [/aux?$/, "al"], // tribunaux->tribunal
				   [/e?au(x$)?/, "o"], // beaux->bo, bateau->bato, journaux->journo
				   [/an(te?s?|s)$/, "ã"], //
				   [false, /an/, /[^aeiouymn].*|$/, "ã" ],
				   [/r[dt]s?$/, "r"],
					// Process the e letter
					// The e letter is probably the most complicated of all
				   [false, /ein/, /[^aeiouymn].*|$/, "1"], // frein, teint
				   [/e[ui]/, "e"],// peine, pleurer, bleu
				   [/en[td]$/, "ã"], // client, prend, fend
				   [/i/, /en/, /[^aeiouymn].*|$/, "1"], // norvégien, rien
				   [false, /en/, /[^aeiouymn].*|$/, "ã"], // tente->tãte
				   [/ets?$/, "e"], // violet, triplets
				   [false, /e/, /o/, ""], // like surseoir

					// Process the i letter

				   [/ier(s|es?)?$/, ""], // ier, iere, iere, ieres
				   [false, /i[nm]/, /[^aeiouymn].*|$/, "1"], // malintentionné->mal1tentionné
				   [/ill/, "y"], // paille->paye, rouille->rouye

					// Process the o letter
				   [false, /on/, /[^aeiouyhnm].*|$/, "ô"],
				   [false, /ouin/, /[^aeiouymn].*|$/, "o1"],
				   [/oe(u(d$)?)?/, "e"],

					// Process the u letter
				   [false, /un/, /[^aeiouymn].*|$/, "1"],
				   [/u[st]$/, "u"], // "résidus", "crut" TODO better remove /[st]$/ ?

					// Process the y letter
				   [/yer$/, "i"], // "ennuyer"->ennui, "appuyer"->appui
				   [/[^aeiouy]/, /ym/, /[^aeiouy].*|$/, "1"], // "symbole", "nymphe", "sympa"
				   [/[^aeiouy]/, /yn/, /[^aeiouynm].*|$/, "1"], // "syndicat", "synchro"
				   [/[^aeiouy]/, /y/, "i"],  // "dynamite"

				   [/[aeiouy]/, /s/, /[aeiouy]/, "z"],
				   [/sc?h/, "ch"],

				   [/gu/, "g"],
				   [false, /g/, /[^aorl].*/, "j"],

				   [/ph/, "f"],
				   [/[^t]/, /t/, /ion/, "ss"],

				   [/qu?/, "k"],
				   [false, /c/, /[auorlt]/, "k"],

				   [/[aeiou]/, /s/, /[aeiou]/, "z"],
				   [/[^c]/, /h/, ""],
				   [/^h/, ""],

				   [/[oiua]/, /t$/, false, ""],

				   [/es?$/, ""], // final e

				   //plural
				   [/[xs]$/, ""]

				];

		function post_process_arrays(arr) {
			var result = [];
			for (var i=0; i<arr.length; ++i) {
				var obj = arr[i];
				if (obj) {
					switch(obj.length) {
					case 2:
						result.push([new RegExp("(.*)("+obj[0].source+")(.*)"),obj[1]]);
						break;
					case 3:
						result.push([new RegExp( (obj[0]?"(.*"+obj[0].source+")":"(.*)") + "("+obj[1].source+")" + "(.*)"),obj[2]]);
						break;
					case 4:
						result.push([new RegExp( (obj[0]?"(.*"+obj[0].source+")":"(.*)") + "("+obj[1].source+")" + (obj[2]?"("+obj[2].source+".*)":"(.*)")),obj[3]]);
						break;
					}
				}
			}
			return result;
		}

		var suffix_removals_verbs = post_process_arrays(suffix_removals_verbs_raw);
		var suffix_removals_nouns = post_process_arrays(suffix_removals_nouns_raw);
		var phonetic_transforms = post_process_arrays(phonetic_transforms_raw);

		function apply_regexp_array(word, regarray, stopOnFirstMatch) {
			var org = word;
//			console.log("==== applying rules on " + word + " ========");

			for (var i=0; i<regarray.length; ++i) {

				var res = regarray[i][0].exec(word);
				if (res) {
//					console.log("matched rule " + regarray[i][0].source + " -> " + regarray[i][1] + ", length: " + res.length);
//					console.log("re: " + regarray[i][0].lastIndex + " / " + res.index);
//					console.log(res);

					var p1 = res[1];
					var p2 = regarray[i][1];
					var p3 = res[res.length-1];
					word = p1 + p2 + p3;

//					console.log("word is now " + word + "  (" + p1 +" + " + p2 + " + " + p3 + "), before: " + org);

					if (stopOnFirstMatch) {
					 i = regarray.length;
					}
				}
			}
			return word;
		}

		return function(word, verbs, nouns, phonetic) {
			verbs = verbs===undefined?true:verbs;
			nouns = nouns===undefined?true:nouns;
			phonetic = phonetic===undefined?true:phonetic;

			if (verbs) {
				word = apply_regexp_array(word, suffix_removals_verbs, true);
			}
			if (nouns) {
				word = apply_regexp_array(word, suffix_removals_nouns, true);
			}
			if (phonetic) {
				word = apply_regexp_array(word, phonetic_transforms, false);
			}
			return NAMESPACE.normalizer.remove_duplicate_letters(word.toLowerCase());
		};
	})();

	return NAMESPACE;
})(fullproof||{});
<commit_msg>use strict<commit_after>var fullproof = (function(NAMESPACE) {
    "use strict";

    NAMESPACE.french = NAMESPACE.french||{};

    NAMESPACE.french.simpleform = (function(){

        var suffix_removals_verbs_raw = [
            // Below, common verbs suffix first
            [/.../, /er(ai([st]?|ent)|i?(on[ts]|ez))$/, "e"],
            [/.../, /ass(i?(ez?|ons)|e(nt|s)?)$/, "e"], // asse, asses, assez, assiez, assies*, if root length >= 3
            [/.../, /assions$/, "e"], // assions if root lengh>=3
            [/.../, /assent$/, "e"],   // assent if root lengh>=3

            [/endr(ez?|ai[st]?|on[st])$/, "ã"],		// endrez, endrai, endrais, endrait, endrons, endront

            [/.../, /iss(i?(ons|ez)|ai[st]?|ant(es?)?|es?)$/, "" ], // issions, issiez, issais, issait, issai, issant, issante, issantes, isses

            [/irai(s|(en)?t)?$/, ""], // irai, irait, irais, iraient

            [/.../, /e?oi(re?|t|s|ent)$/, ""],  // eoir, eoire, oir, oire, oit, ois, oient

            [/.../, /aient$/, ""],     // removes aient
            [/.../, /a[mt]es$/, ""], // removes ames, ates
            [/i?ons$/, ""],   // removes ons, ions
            [/ait$/, ""],     // removes ait
            [/ent$/, ""],     // removes ent
            [/i?e[rz]$/, "e"] // removes er, ez, iez

        ];

        var suffix_removals_nouns_raw = [
            [/inages?$/, "1"],	// "copinage" > "cop1"
            [/.../, /ages?$/, ""], // "habillage" > "habill"
            [/.../, /[aoie]tions?$/, ""], // "déclaration" > "déclar", not "nation"
            [/og(ies?|ues?)$/, "og"], // "philologie" -> "philolog", "philologue" -> "philolog"
            [/t(rices?|euses?)$/, "ter"], // "fédératrice" -> "fédérater","flatteuse" -> "flatter" (eur is -> er by another rule)
            [/.../, /e(uses?|ries?|urs?)$/, "er"], // euse, euses, eries, eries, eur (flatteuse, flatterie, flatteur)
            [/utions$/, "u"], // "pollution", "attribution" ! produces a "u", because "uer"$ is not removed (but "er"$ is).
            [/[ae]n[cs]es?$/, "ãS"], // prudence" -> "prudã", "tolérance" -> "tolérã"
            [/..al/, /ites?$/, ""], // // "anormalite" -> "anormal"
            [/[ea]mment/, "ã"], // prudemment -> "prudã"
            //
            //not processed:
            //* usion$ : not an interesting simplification, as there are not
            //  enough nominal cases. i.e. "diffusion", but "illusion",
            // "exclusion", "contusion", etc.
            [/ives?$/, "if"], // // "consécutives" -> con
            [/istes?$/, "isme"], // maybe a bit aggressive ?
            [/ables?$/, ""], // "chiffrable" -> "chiffr". aggressive ?
            [/[^ae]/, /ines?$/, "1"] // "citadine"->"citadin"
        ];

        var phonetic_transforms_raw = [

            [/n/, /t/, /iel/, "S"],
            [false, /t/, /i[oea]/, "S"],

            // the A LETTER
            [false, /ain/, /[^aeiouymn].*|$/, "1" ], // copain->cop1, complainte->compl1te
            [/ai(s$)?/, "e"],
            [false, /am/, /[^aeiouymn].*|$/, "ã" ], //  crampe->crãpe
            [/aux?$/, "al"], // tribunaux->tribunal
            [/e?au(x$)?/, "o"], // beaux->bo, bateau->bato, journaux->journo
            [/an(te?s?|s)$/, "ã"], //
            [false, /an/, /[^aeiouymn].*|$/, "ã" ],
            [/r[dt]s?$/, "r"],
            // Process the e letter
            // The e letter is probably the most complicated of all
            [false, /ein/, /[^aeiouymn].*|$/, "1"], // frein, teint
            [/e[ui]/, "e"],// peine, pleurer, bleu
            [/en[td]$/, "ã"], // client, prend, fend
            [/i/, /en/, /[^aeiouymn].*|$/, "1"], // norvégien, rien
            [false, /en/, /[^aeiouymn].*|$/, "ã"], // tente->tãte
            [/ets?$/, "e"], // violet, triplets
            [false, /e/, /o/, ""], // like surseoir

            // Process the i letter

            [/ier(s|es?)?$/, ""], // ier, iere, iere, ieres
            [false, /i[nm]/, /[^aeiouymn].*|$/, "1"], // malintentionné->mal1tentionné
            [/ill/, "y"], // paille->paye, rouille->rouye

            // Process the o letter
            [false, /on/, /[^aeiouyhnm].*|$/, "ô"],
            [false, /ouin/, /[^aeiouymn].*|$/, "o1"],
            [/oe(u(d$)?)?/, "e"],

            // Process the u letter
            [false, /un/, /[^aeiouymn].*|$/, "1"],
            [/u[st]$/, "u"], // "résidus", "crut" TODO better remove /[st]$/ ?

            // Process the y letter
            [/yer$/, "i"], // "ennuyer"->ennui, "appuyer"->appui
            [/[^aeiouy]/, /ym/, /[^aeiouy].*|$/, "1"], // "symbole", "nymphe", "sympa"
            [/[^aeiouy]/, /yn/, /[^aeiouynm].*|$/, "1"], // "syndicat", "synchro"
            [/[^aeiouy]/, /y/, "i"],  // "dynamite"

            [/[aeiouy]/, /s/, /[aeiouy]/, "z"],
            [/sc?h/, "ch"],

            [/gu/, "g"],
            [false, /g/, /[^aorl].*/, "j"],

            [/ph/, "f"],
            [/[^t]/, /t/, /ion/, "ss"],

            [/qu?/, "k"],
            [false, /c/, /[auorlt]/, "k"],

            [/[aeiou]/, /s/, /[aeiou]/, "z"],
            [/[^c]/, /h/, ""],
            [/^h/, ""],

            [/[oiua]/, /t$/, false, ""],

            [/es?$/, ""], // final e

            //plural
            [/[xs]$/, ""]

        ];

        function post_process_arrays(arr) {
            var result = [];
            for (var i=0; i<arr.length; ++i) {
                var obj = arr[i];
                if (obj) {
                    switch(obj.length) {
                        case 2:
                            result.push([new RegExp("(.*)("+obj[0].source+")(.*)"),obj[1]]);
                            break;
                        case 3:
                            result.push([new RegExp( (obj[0]?"(.*"+obj[0].source+")":"(.*)") + "("+obj[1].source+")" + "(.*)"),obj[2]]);
                            break;
                        case 4:
                            result.push([new RegExp( (obj[0]?"(.*"+obj[0].source+")":"(.*)") + "("+obj[1].source+")" + (obj[2]?"("+obj[2].source+".*)":"(.*)")),obj[3]]);
                            break;
                    }
                }
            }
            return result;
        }

        var suffix_removals_verbs = post_process_arrays(suffix_removals_verbs_raw);
        var suffix_removals_nouns = post_process_arrays(suffix_removals_nouns_raw);
        var phonetic_transforms = post_process_arrays(phonetic_transforms_raw);

        function apply_regexp_array(word, regarray, stopOnFirstMatch) {
//            var org = word;
//			console.log("==== applying rules on " + word + " ========");

            for (var i=0; i<regarray.length; ++i) {

                var res = regarray[i][0].exec(word);
                if (res) {
//					console.log("matched rule " + regarray[i][0].source + " -> " + regarray[i][1] + ", length: " + res.length);
//					console.log("re: " + regarray[i][0].lastIndex + " / " + res.index);
//					console.log(res);

                    var p1 = res[1];
                    var p2 = regarray[i][1];
                    var p3 = res[res.length-1];
                    word = p1 + p2 + p3;

//					console.log("word is now " + word + "  (" + p1 +" + " + p2 + " + " + p3 + "), before: " + org);

                    if (stopOnFirstMatch) {
                        i = regarray.length;
                    }
                }
            }
            return word;
        }

        return function(word, verbs, nouns, phonetic) {
            verbs = verbs===undefined?true:verbs;
            nouns = nouns===undefined?true:nouns;
            phonetic = phonetic===undefined?true:phonetic;

            if (verbs) {
                word = apply_regexp_array(word, suffix_removals_verbs, true);
            }
            if (nouns) {
                word = apply_regexp_array(word, suffix_removals_nouns, true);
            }
            if (phonetic) {
                word = apply_regexp_array(word, phonetic_transforms, false);
            }
            return NAMESPACE.normalizer.remove_duplicate_letters(word.toLowerCase());
        };
    })();

    return NAMESPACE;
})(fullproof||{});
<|endoftext|>


<commit_before>import {browserHistory as history} from 'react-router'
import {createRoutes} from 'react-router/lib/RouteUtils'
import {hasRole} from 'focus-core/user'

const navigate = (url) => (history.push(`${__BASE_URL__}${url?url:''}`));

const filterByRoles = function filterByRoles(routes) {
    return createRoutes(routes).reduce(
        function filterRouteByRoles(acc, current, index, array) {
            if(current.roles && !hasRole(current.roles)) {
                return acc;
            }
            const currentRoute = current;
            if(currentRoute.childRoutes) {
                currentRoute.childRoutes = filterByRoles(currentRoute.childRoutes)
            }
            return acc.concat(currentRoute);
        }
  , []);
};

export default {
    navigate,
    filterByRoles
}
<commit_msg>[route utilities] Add todo and reformat.<commit_after>import {browserHistory as history} from 'react-router'
import {createRoutes} from 'react-router/lib/RouteUtils'
import {hasRole} from 'focus-core/user'

const navigate = url => history.push(`${__BASE_URL__}${url?url:''}`);

const filterByRoles =  routes => {
    return createRoutes(routes).reduce( (acc, current, index, array) => {
            if(current.roles && !hasRole(current.roles)) {
                return acc;
            }
            const currentRoute = current;
            if(currentRoute.childRoutes) {
                currentRoute.childRoutes = filterByRoles(currentRoute.childRoutes)
            }
            return [...acc, currentRoute];
        }
  , []);
};

// TODO: Maybe two exports will be better ?
export default {
    navigate,
    filterByRoles
}
<|endoftext|>


<commit_before>import R from 'ramda';
import Selectors from '../selectors';
import * as PIN_DIRECTION from '../constants/pinDirection';
import * as SIZES from '../constants/sizes';

const getMaxSidePinCount = (pins) => R.pipe(
  R.values,
  R.groupBy((pin) => pin.direction),
  R.values,
  R.reduce((p, c) => R.max(p, c.length || 0), 0)
)(pins);
const getPinsWidth = (count, withMargins) => {
  const marginCount = (withMargins) ? count + 1 : count - 1;
  return (marginCount * SIZES.PIN.margin) + (count * SIZES.PIN.radius * 2);
};
const getSidesPinCount = (pins) => R.pipe(
  R.values,
  R.groupBy((pin) => pin.direction),
  R.values,
  R.reduce((p, group) => {
    const direction = group[0].direction;
    return R.assoc(direction, group.length, p);
  }, {
    [PIN_DIRECTION.INPUT]: 0,
    [PIN_DIRECTION.OUTPUT]: 0,
  })
)(pins);
const getPinListWidths = (counts) => ({
  [PIN_DIRECTION.INPUT]: getPinsWidth(counts[PIN_DIRECTION.INPUT], false),
  [PIN_DIRECTION.OUTPUT]: getPinsWidth(counts[PIN_DIRECTION.OUTPUT], false),
});
const getNodeWidth = (pins) => {
  const pinsCount = getMaxSidePinCount(pins);
  let nodeWidth = getPinsWidth(pinsCount, true);
  if (nodeWidth < SIZES.NODE.minWidth) {
    nodeWidth = SIZES.NODE.minWidth;
  }
  return nodeWidth;
};
const getPinPosition = (pins, nodeWidth) => {
  const pinsCount = getSidesPinCount(pins);
  const pinsWidth = getPinListWidths(pinsCount);
  return R.pipe(
    R.values,
    R.groupBy((pin) => pin.direction),
    R.map((group) => {
      const vOffset = {
        [PIN_DIRECTION.INPUT]: SIZES.NODE.padding.y - SIZES.PIN.radius,
        [PIN_DIRECTION.OUTPUT]: SIZES.NODE.minHeight + SIZES.NODE.padding.y - SIZES.PIN.radius,
      };
      let offset = 0;

      return R.map((pin) => {
        const r = R.merge(
          pin,
          {
            position: {
              x: (nodeWidth - pinsWidth[pin.direction]) / 2 + offset,
              y: vOffset[pin.direction],
            },
            radius: SIZES.PIN.radius,
          }
        );

        offset += SIZES.PIN.margin + SIZES.PIN.radius * 2;

        return r;
      }, group);
    }),
    R.values,
    R.flatten,
    R.reduce((p, pin) => R.assoc(pin.id, pin, p), {})
  )(pins);
};

export default {
  getNodeWidth,
  getPinsWidth,
  getPinListWidths,
  getPinPosition,
  getSidesPinCount,
  getMaxSidePinCount,
};
<commit_msg>refactor(patchUtils): remove import of Selectors. It's excesive now.<commit_after>import R from 'ramda';
import * as PIN_DIRECTION from '../constants/pinDirection';
import * as SIZES from '../constants/sizes';

const getMaxSidePinCount = (pins) => R.pipe(
  R.values,
  R.groupBy((pin) => pin.direction),
  R.values,
  R.reduce((p, c) => R.max(p, c.length || 0), 0)
)(pins);
const getPinsWidth = (count, withMargins) => {
  const marginCount = (withMargins) ? count + 1 : count - 1;
  return (marginCount * SIZES.PIN.margin) + (count * SIZES.PIN.radius * 2);
};
const getSidesPinCount = (pins) => R.pipe(
  R.values,
  R.groupBy((pin) => pin.direction),
  R.values,
  R.reduce((p, group) => {
    const direction = group[0].direction;
    return R.assoc(direction, group.length, p);
  }, {
    [PIN_DIRECTION.INPUT]: 0,
    [PIN_DIRECTION.OUTPUT]: 0,
  })
)(pins);
const getPinListWidths = (counts) => ({
  [PIN_DIRECTION.INPUT]: getPinsWidth(counts[PIN_DIRECTION.INPUT], false),
  [PIN_DIRECTION.OUTPUT]: getPinsWidth(counts[PIN_DIRECTION.OUTPUT], false),
});
const getNodeWidth = (pins) => {
  const pinsCount = getMaxSidePinCount(pins);
  let nodeWidth = getPinsWidth(pinsCount, true);
  if (nodeWidth < SIZES.NODE.minWidth) {
    nodeWidth = SIZES.NODE.minWidth;
  }
  return nodeWidth;
};
const getPinPosition = (pins, nodeWidth) => {
  const pinsCount = getSidesPinCount(pins);
  const pinsWidth = getPinListWidths(pinsCount);
  return R.pipe(
    R.values,
    R.groupBy((pin) => pin.direction),
    R.map((group) => {
      const vOffset = {
        [PIN_DIRECTION.INPUT]: SIZES.NODE.padding.y - SIZES.PIN.radius,
        [PIN_DIRECTION.OUTPUT]: SIZES.NODE.minHeight + SIZES.NODE.padding.y - SIZES.PIN.radius,
      };
      let offset = 0;

      return R.map((pin) => {
        const r = R.merge(
          pin,
          {
            position: {
              x: (nodeWidth - pinsWidth[pin.direction]) / 2 + offset,
              y: vOffset[pin.direction],
            },
            radius: SIZES.PIN.radius,
          }
        );

        offset += SIZES.PIN.margin + SIZES.PIN.radius * 2;

        return r;
      }, group);
    }),
    R.values,
    R.flatten,
    R.reduce((p, pin) => R.assoc(pin.id, pin, p), {})
  )(pins);
};

export default {
  getNodeWidth,
  getPinsWidth,
  getPinListWidths,
  getPinPosition,
  getSidesPinCount,
  getMaxSidePinCount,
};
<|endoftext|>


<commit_before>/**
 * @preserve FastClick: polyfill to remove click delays on browsers with touch UIs.
 *
 * @version 0.6.4
 * @codingstandard ftlabs-jsv2
 * @copyright The Financial Times Limited [All Rights Reserved]
 * @license MIT License (see LICENSE.txt)
 */

/*jslint browser:true, node:true*/
/*global define, Event, Node*/

/**
 * Instantiate fast-clicking listeners on the specificed layer.
 *
 * @constructor
 * @param {Element} layer The layer to listen on
 */
function FastClick(layer) {
    'use strict';
    var oldOnClick, self = this;

    /**
     * Whether a click is currently being tracked.
     *
     * @type boolean
     */
    this.trackingClick = false;

    /**
     * Timestamp for when when click tracking started.
     *
     * @type number
     */
    this.trackingClickStart = 0;

    /**
     * The element being tracked for a click.
     *
     * @type EventTarget
     */
    this.targetElement = null;

    /**
     * X-coordinate of touch start event.
     *
     * @type number
     */
    this.touchStartX = 0;

    /**
     * Y-coordinate of touch start event.
     *
     * @type number
     */
    this.touchStartY = 0;

    /**
     * ID of the last touch, retrieved from Touch.identifier.
     *
     * @type number
     */
    this.lastTouchIdentifier = 0;

    /**
     * The FastClick layer.
     *
     * @type Element
     */
    this.layer = layer;

    if (!layer || !layer.nodeType) {
        throw new TypeError('Layer must be a document node');
    }

    /** @type function() */
    this.onClick = function() { return FastClick.prototype.onClick.apply(self, arguments); };

    /** @type function() */
    this.onMouse = function() { return FastClick.prototype.onMouse.apply(self, arguments); };

    /** @type function() */
    this.onTouchStart = function() { return FastClick.prototype.onTouchStart.apply(self, arguments); };

    /** @type function() */
    this.onTouchEnd = function() { return FastClick.prototype.onTouchEnd.apply(self, arguments); };

    /** @type function() */
    this.onTouchCancel = function() { return FastClick.prototype.onTouchCancel.apply(self, arguments); };

    if (FastClick.notNeeded()) {
        return;
    }

    // Set up event handlers as required
    if (this.deviceIsAndroid) {
        layer.addEventListener('mouseover', this.onMouse, true);
        layer.addEventListener('mousedown', this.onMouse, true);
        layer.addEventListener('mouseup', this.onMouse, true);
    }

    layer.addEventListener('click', this.onClick, true);
    layer.addEventListener('touchstart', this.onTouchStart, false);
    layer.addEventListener('touchend', this.onTouchEnd, false);
    layer.addEventListener('touchcancel', this.onTouchCancel, false);

    // Hack is required for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
    // which is how FastClick normally stops click events bubbling to callbacks registered on the FastClick
    // layer when they are cancelled.
    if (!Event.prototype.stopImmediatePropagation) {
        layer.removeEventListener = function(type, callback, capture) {
            var rmv = Node.prototype.removeEventListener;
            if (type === 'click') {
                rmv.call(layer, type, callback.hijacked || callback, capture);
            } else {
                rmv.call(layer, type, callback, capture);
            }
        };

        layer.addEventListener = function(type, callback, capture) {
            var adv = Node.prototype.addEventListener;
            if (type === 'click') {
                adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) {
                    if (!event.propagationStopped) {
                        callback(event);
                    }
                }), capture);
            } else {
                adv.call(layer, type, callback, capture);
            }
        };
    }

    // If a handler is already declared in the element's onclick attribute, it will be fired before
    // FastClick's onClick handler. Fix this by pulling out the user-defined handler function and
    // adding it as listener.
    if (typeof layer.onclick === 'function') {

        // Android browser on at least 3.2 requires a new reference to the function in layer.onclick
        // - the old one won't work if passed to addEventListener directly.
        oldOnClick = layer.onclick;
        layer.addEventListener('click', function(event) {
            oldOnClick(event);
        }, false);
        layer.onclick = null;
    }
}

/**
 * Android requires exceptions.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsAndroid = navigator.userAgent.indexOf('Android') > 0;

/**
 * iOS requires exceptions.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent);

/**
 * iOS 4 requires an exception for select elements.
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOS4 = FastClick.prototype.deviceIsIOS && (/OS 4_\d(_\d)?/).test(navigator.userAgent);

/**
 * iOS 6.0(+?) requires the target element to be manually derived
 *
 * @type boolean
 */
FastClick.prototype.deviceIsIOSWithBadTarget = FastClick.prototype.deviceIsIOS && (/OS ([6-9]|\d{2})_\d/).test(navigator.userAgent);

/**
 * Determine whether a given element requires a native click.
 *
 * @param {EventTarget|Element} target Target DOM element
 * @returns {boolean} Returns true if the element needs a native click
 */
FastClick.prototype.needsClick = function(target) {
    'use strict';
    var nodeName = target.nodeName.toLowerCase();

    if (nodeName === 'button' || nodeName === 'input') {

        // File inputs need real clicks on iOS 6 due to a browser bug (issue #68)
        // Don't send a synthetic click to disabled inputs (issue #62)
        if ((this.deviceIsIOS && target.type === 'file') || target.disabled) {
            return true;
        }
    } else if (nodeName === 'label' || nodeName === 'video') {
        return true;
    }

    return (/\bneedsclick\b/).test(target.className);
};

/**
 * Determine whether a given element requires a call to focus to simulate click into element.
 *
 * @param {EventTarget|Element} target Target DOM element
 * @returns {boolean} Returns true if the element requires a call to focus to simulate native click.
 */
FastClick.prototype.needsFocus = function(target) {
    'use strict';
    switch (target.nodeName.toLowerCase()) {
        case 'textarea':
        case 'select':
            return true;
        case 'input':
            switch (target.type) {
                case 'button':
                case 'checkbox':
                case 'file':
                case 'image':
                case 'radio':
                case 'submit':
                    return false;
            }

            // No point in attempting to focus disabled inputs
            return !target.disabled && !target.readOnly;
        default:
            return (/\bneedsfocus\b/).test(target.className);
    }
};

/**
 * Send a click event to the specified element.
 *
 * @param {EventTarget|Element} targetElement
 * @param {Event} event
 */
FastClick.prototype.sendClick = function(targetElement, event) {
    'use strict';
    var clickEvent, touch;

    // On some Android devices activeElement needs to be blurred otherwise the synthetic click will have no effect (#24)
    if (document.activeElement && document.activeElement !== targetElement) {
        document.activeElement.blur();
    }

    touch = event.changedTouches[0];

    // Synthesise a click event, with an extra attribute so it can be tracked
    clickEvent = document.createEvent('MouseEvents');
    clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
    clickEvent.forwardedTouchEvent = true;
    targetElement.dispatchEvent(clickEvent);
};

/**
 * @param {EventTarget|Element} targetElement
 */
FastClick.prototype.focus = function(targetElement) {
    'use strict';
    var length;

    if (this.deviceIsIOS && targetElement.setSelectionRange) {
        length = targetElement.value.length;
        targetElement.setSelectionRange(length, length);
    } else {
        targetElement.focus();
    }
};

/**
 * Check whether the given target element is a child of a scrollable layer and if so, set a flag on it.
 *
 * @param {EventTarget|Element} targetElement
 */
FastClick.prototype.updateScrollParent = function(targetElement) {
    'use strict';
    var scrollParent, parentElement;

    scrollParent = targetElement.fastClickScrollParent;

    // Attempt to discover whether the target element is contained within a scrollable layer. Re-check if the
    // target element was moved to another parent.
    if (!scrollParent || !scrollParent.contains(targetElement)) {
        parentElement = targetElement;
        do {
            if (parentElement.scrollHeight > parentElement.offsetHeight) {
                scrollParent = parentElement;
                targetElement.fastClickScrollParent = parentElement;
                break;
            }

            parentElement = parentElement.parentElement;
        } while (parentElement);
    }

    // Always update the scroll top tracker if possible.
    if (scrollParent) {
        scrollParent.fastClickLastScrollTop = scrollParent.scrollTop;
    }
};

/**
 * @param {EventTarget} targetElement
 * @returns {Element|EventTarget}
 */
FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) {
    'use strict';

    // On some older browsers (notably Safari on iOS 4.1 - see issue #56) the event target may be a text node.
    if (eventTarget.nodeType === Node.TEXT_NODE) {
        return eventTarget.parentNode;
    }

    return eventTarget;
};

/**
 * On touch start, record the position and scroll offset.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onTouchStart = function(event) {
    'use strict';
    var targetElement, touch, selection;

    targetElement = this.getTargetElementFromEventTarget(event.target);
    touch = event.targetTouches[0];

    if (this.deviceIsIOS) {

        // Only trusted events will deselect text on iOS (issue #49)
        selection = window.getSelection();
        if (selection.rangeCount && !selection.isCollapsed) {
            return true;
        }

        if (!this.deviceIsIOS4) {

            // Weird things happen on iOS when an alert or confirm dialog is opened from a click event callback (issue #23):
            // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
            // with the same identifier as the touch event that previously triggered the click that triggered the alert.
            // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an
            // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform.
            if (touch.identifier === this.lastTouchIdentifier) {
                event.preventDefault();
                return false;
            }

            this.lastTouchIdentifier = touch.identifier;

            // If the target element is a child of a scrollable layer (using -webkit-overflow-scrolling: touch) and:
            // 1) the user does a fling scroll on the scrollable layer
            // 2) the user stops the fling scroll with another tap
            // then the event.target of the last 'touchend' event will be the element that was under the user's finger
            // when the fling scroll was started, causing FastClick to send a click event to that layer - unless a check
            // is made to ensure that a parent layer was not scrolled before sending a synthetic click (issue #42).
            this.updateScrollParent(targetElement);
        }
    }

    this.trackingClick = true;
    this.trackingClickStart = event.timeStamp;
    this.targetElement = targetElement;

    this.touchStartX = touch.pageX;
    this.touchStartY = touch.pageY;

    // Prevent phantom clicks on fast double-tap (issue #36)
    if ((event.timeStamp - this.lastClickTime) < 200) {
        event.preventDefault();
    }

    return true;
};

/**
 * Based on a touchmove event object, check whether the touch has moved past a boundary since it started.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.touchHasMoved = function(event) {
    'use strict';
    var touch = event.changedTouches[0];

    if (Math.abs(touch.pageX - this.touchStartX) > 10 || Math.abs(touch.pageY - this.touchStartY) > 10) {
        return true;
    }

    return false;
};

/**
 * Attempt to find the labelled control for the given label element.
 *
 * @param {EventTarget|HTMLLabelElement} labelElement
 * @returns {Element|null}
 */
FastClick.prototype.findControl = function(labelElement) {
    'use strict';

    // Fast path for newer browsers supporting the HTML5 control attribute
    if (labelElement.control !== undefined) {
        return labelElement.control;
    }

    // All browsers under test that support touch events also support the HTML5 htmlFor attribute
    if (labelElement.htmlFor) {
        return document.getElementById(labelElement.htmlFor);
    }

    // If no for attribute exists, attempt to retrieve the first labellable descendant element
    // the list of which is defined here: http://www.w3.org/TR/html5/forms.html#category-label
    return labelElement.querySelector('button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea');
};

/**
 * On touch end, determine whether to send a click event at once.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onTouchEnd = function(event) {
    'use strict';
    var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement;

    // If the touch has moved, cancel the click tracking
    if (this.touchHasMoved(event)) {
        this.trackingClick = false;
        this.targetElement = null;
    }

    if (!this.trackingClick) {
        return true;
    }

    // Prevent phantom clicks on fast double-tap (issue #36)
    if ((event.timeStamp - this.lastClickTime) < 200) {
        this.cancelNextClick = true;
        return true;
    }

    this.lastClickTime = event.timeStamp;

    trackingClickStart = this.trackingClickStart;
    this.trackingClick = false;
    this.trackingClickStart = 0;

    // On some iOS devices, the targetElement supplied with the event is invalid if the layer
    // is performing a transition or scroll, and has to be re-detected manually. Note that
    // for this to function correctly, it must be called *after* the event target is checked!
    // See issue #57; also filed as rdar://13048589 .
    if (this.deviceIsIOSWithBadTarget) {
        touch = event.changedTouches[0];
        targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset);
    }

    targetTagName = targetElement.tagName.toLowerCase();
    if (targetTagName === 'label') {
        forElement = this.findControl(targetElement);
        if (forElement) {
            this.focus(targetElement);
            if (this.deviceIsAndroid) {
                return false;
            }

            targetElement = forElement;
        }
    } else if (this.needsFocus(targetElement)) {

        // Case 1: If the touch started a while ago (best guess is 100ms based on tests for issue #36) then focus will be triggered anyway. Return early and unset the target element reference so that the subsequent click will be allowed through.
        // Case 2: Without this exception for input elements tapped when the document is contained in an iframe, then any inputted text won't be visible even though the value attribute is updated as the user types (issue #37).
        if ((event.timeStamp - trackingClickStart) > 100 || (this.deviceIsIOS && window.top !== window && targetTagName === 'input')) {
            this.targetElement = null;
            return false;
        }

        this.focus(targetElement);

        // Select elements need the event to go through on iOS 4, otherwise the selector menu won't open.
        if (!this.deviceIsIOS4 || targetTagName !== 'select') {
            this.targetElement = null;
            event.preventDefault();
        }

        return false;
    }

    if (this.deviceIsIOS && !this.deviceIsIOS4) {

        // Don't send a synthetic click event if the target element is contained within a parent layer that was scrolled
        // and this tap is being used to stop the scrolling (usually initiated by a fling - issue #42).
        scrollParent = targetElement.fastClickScrollParent;
        if (scrollParent && scrollParent.fastClickLastScrollTop !== scrollParent.scrollTop) {
            return true;
        }
    }

    // Prevent the actual click from going though - unless the target node is marked as requiring
    // real clicks or if it is in the whitelist in which case only non-programmatic clicks are permitted.
    if (!this.needsClick(targetElement)) {
        event.preventDefault();
        this.sendClick(targetElement, event);
    }

    return false;
};

/**
 * On touch cancel, stop tracking the click.
 *
 * @returns {void}
 */
FastClick.prototype.onTouchCancel = function() {
    'use strict';
    this.trackingClick = false;
    this.targetElement = null;
};

/**
 * Determine mouse events which should be permitted.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onMouse = function(event) {
    'use strict';

    // If a target element was never set (because a touch event was never fired) allow the event
    if (!this.targetElement) {
        return true;
    }

    if (event.forwardedTouchEvent) {
        return true;
    }

    // Programmatically generated events targeting a specific element should be permitted
    if (!event.cancelable) {
        return true;
    }

    // Derive and check the target element to see whether the mouse event needs to be permitted;
    // unless explicitly enabled, prevent non-touch click events from triggering actions,
    // to prevent ghost/doubleclicks.
    if (!this.needsClick(this.targetElement) || this.cancelNextClick) {

        // Prevent any user-added listeners declared on FastClick element from being fired.
        if (event.stopImmediatePropagation) {
            event.stopImmediatePropagation();
        } else {

            // Part of the hack for browsers that don't support Event#stopImmediatePropagation (e.g. Android 2)
            event.propagationStopped = true;
        }

        // Cancel the event
        event.stopPropagation();
        event.preventDefault();

        return false;
    }

    // If the mouse event is permitted, return true for the action to go through.
    return true;
};

/**
 * On actual clicks, determine whether this is a touch-generated click, a click action occurring
 * naturally after a delay after a touch (which needs to be cancelled to avoid duplication), or
 * an actual click which should be permitted.
 *
 * @param {Event} event
 * @returns {boolean}
 */
FastClick.prototype.onClick = function(event) {
    'use strict';
    var permitted;

    // It's possible for another FastClick-like library delivered with third-party code to fire a click event before FastClick does (issue #44). In that case, set the click-tracking flag back to false and return early. This will cause onTouchEnd to return early.
    if (this.trackingClick) {
        this.targetElement = null;
        this.trackingClick = false;
        return true;
    }

    // Very odd behaviour on iOS (issue #18): if a submit element is present inside a form and the user hits enter in the iOS simulator or clicks the Go button on the pop-up OS keyboard the a kind of 'fake' click event will be triggered with the submit-type input element as the target.
    if (event.target.type === 'submit' && event.detail === 0) {
        return true;
    }

    permitted = this.onMouse(event);

    // Only unset targetElement if the click is not permitted. This will ensure that the check for !targetElement in onMouse fails and the browser's click doesn't go through.
    if (!permitted) {
        this.targetElement = null;
    }

    // If clicks are permitted, return true for the action to go through.
    return permitted;
};

/**
 * Remove all FastClick's event listeners.
 *
 * @returns {void}
 */
FastClick.prototype.destroy = function() {
    'use strict';
    var layer = this.layer;

    if (this.deviceIsAndroid) {
        layer.removeEventListener('mouseover', this.onMouse, true);
        layer.removeEventListener('mousedown', this.onMouse, true);
        layer.removeEventListener('mouseup', this.onMouse, true);
    }

    layer.removeEventListener('click', this.onClick, true);
    layer.removeEventListener('touchstart', this.onTouchStart, false);
    layer.removeEventListener('touchend', this.onTouchEnd, false);
    layer.removeEventListener('touchcancel', this.onTouchCancel, false);
};

FastClick.notNeeded = function() {
    'use strict';
    var metaViewport;

    // Devices that don't support touch don't need FastClick
    if (typeof window.ontouchstart === 'undefined') {
        return true;
    }

    if ((/Chrome\/[0-9]+/).test(navigator.userAgent)) {

        // Chrome on Android with user-scalable="no" doesn't need FastClick (issue #89)
        if (FastClick.prototype.deviceIsAndroid) {
            metaViewport = document.querySelector('meta[name=viewport]');
            if (metaViewport && metaViewport.content.indexOf('user-scalable=no') !== -1) {
                return true;
            }

            // Chrome desktop doesn't need FastClick (issue #15)
        } else {
            return true;
        }
    }

    return false;
};

/**
 * Factory method for creating a FastClick object
 *
 * @param {Element} layer The layer to listen on
 */
FastClick.attach = function(layer) {
    'use strict';
    return new FastClick(layer);
};

if (typeof define !== 'undefined' && define.amd) {

    // AMD. Register as an anonymous module.
    define(function() {
        'use strict';
        return FastClick;
    });
} else if (typeof module !== 'undefined' && module.exports) {
    module.exports = FastClick.attach;
    module.exports.FastClick = FastClick;
} else {
    window.FastClick = FastClick;
}<commit_msg>Cleanup #1<commit_after><|endoftext|>


<commit_before>"use strict";
var assert = require('assert');

var syllableCount = function(word) {
  var syllables = 0;

  var inSyllableRun = false;

  for (var i=0;i<word.length;++i) {
    if (isVowel(word[i]) && !inSyllableRun) {
      syllables++;
      inSyllableRun = true;
    } else {
      inSyllableRun = false;
    }
  }

  return syllables;
};

var isVowel = function(c) {
  return "aeiouy".indexOf(c) !== -1;
};

var splitWords = function(words) {
  return words.split(' ');
};

var countSyllables = function(words) {
  var s = splitWords(words);
  var cs = s.map(syllableCount);

  return cs.reduce(function(x,y) { return x + y});
};

describe('haiku', function() {
  describe('syllables', function() {
    it('contiguous sequence of vowels is one syllable', function() {
      assert.equal(1, syllableCount('o'));
      assert.equal(1, syllableCount('oa'));
      assert.equal(1, syllableCount('at'));
      assert.equal(1, syllableCount('bab'));
    });

    it('broken by consonants is multiple syllables', function() {
      assert.equal(2, syllableCount('aba'));
      assert.equal(3, syllableCount('banana'));
      assert.equal(2, syllableCount('code'));
    });

    it('should split sentences into words', function() {
      assert.deepEqual(['the','quick','brown','fox'],
                       splitWords('the quick brown fox'));
    });

    it('should count syllables in a phrase', function() {
      assert.equal(4, countSyllables('the quick brown fox'));
    });

    it('should split on / for phrases', function() {
      assert.deepEqual(['one two three', 'four five', 'six seven'],
                       splitPhrase('one two three/four five/six seven'));

    });

    it('should have an understanding of vowels', function() {
      var vowels = "aeiouy";
      for (var i=0;i<vowels.length;++i) {
        assert(isVowel(vowels[i]));
      }

      var consonants = "bcdfghjklmnpqrstvwxz";
      for (var i=0;i<consonants.length;++i) {
        assert(!isVowel(consonants[i]));
      }
    });
  });
});
<commit_msg>all tests passing<commit_after>"use strict";
var assert = require('assert');

var syllableCount = function(word) {
  var syllables = 0;

  var inSyllableRun = false;

  for (var i=0;i<word.length;++i) {
    if (isVowel(word[i]) && !inSyllableRun) {
      syllables++;
      inSyllableRun = true;
    } else {
      inSyllableRun = false;
    }
  }

  return syllables;
};

var isVowel = function(c) {
  return "aeiouy".indexOf(c) !== -1;
};

var splitWords = function(words) {
  return words.split(' ');
};

var splitPhrase = function(phrase) {
  return phrase.split('/');
};

var countSyllables = function(words) {
  var s = splitWords(words);
  var cs = s.map(syllableCount);

  return cs.reduce(function(x,y) { return x + y});
};

describe('haiku', function() {
  describe('syllables', function() {
    it('contiguous sequence of vowels is one syllable', function() {
      assert.equal(1, syllableCount('o'));
      assert.equal(1, syllableCount('oa'));
      assert.equal(1, syllableCount('at'));
      assert.equal(1, syllableCount('bab'));
    });

    it('broken by consonants is multiple syllables', function() {
      assert.equal(2, syllableCount('aba'));
      assert.equal(3, syllableCount('banana'));
      assert.equal(2, syllableCount('code'));
    });

    it('should split sentences into words', function() {
      assert.deepEqual(['the','quick','brown','fox'],
                       splitWords('the quick brown fox'));
    });

    it('should count syllables in a phrase', function() {
      assert.equal(4, countSyllables('the quick brown fox'));
    });

    it('should split on / for phrases', function() {
      assert.deepEqual(['one two three', 'four five', 'six seven'],
                       splitPhrase('one two three/four five/six seven'));

    });

    it('should have an understanding of vowels', function() {
      var vowels = "aeiouy";
      for (var i=0;i<vowels.length;++i) {
        assert(isVowel(vowels[i]));
      }

      var consonants = "bcdfghjklmnpqrstvwxz";
      for (var i=0;i<consonants.length;++i) {
        assert(!isVowel(consonants[i]));
      }
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

Module.register("1", {

  result: {},
  defaults: {
    updateInterval: 30000,
	  showHighLow: true,
    highLowColor: true
  },

  getStyles: function() {
    return ["Hako-ticker.css"];
  },

  start: function() {
    console.log("Hako-ticker.js: start function running");
    this.getTickers();
    this.scheduleUpdate();
  },

  getDom: function() {
    console.log("Hako-ticker.js: getDom function running");
    var wrapper = document.createElement("ticker");
    wrapper.className = 'medium bright';
    wrapper.className = 'ticker';

    var data = this.result;

    var symbolElement =  document.createElement("span");
    var breakElement =  document.createElement("br");

    for (i = 0; i < data.length; i++) {
      var symbol = data[i].pair_name;
      //var lastPrice = data.buy_price;
      var highPrice = data[i].buy_price;
      var lowPrice = data[i].sell_price;

      var priceElement = document.createElement("span");
      var lowElement = document.createElement("span");
      var highElement = document.createElement("span");

      symbolElement.innerHTML = symbol + ' $';
      wrapper.appendChild(symbolElement);
      priceElement.innerHTML = highPrice;
      wrapper.appendChild(priceElement);
      wrapper.appendChild(breakElement);
      lowElement.className = 'small';
      lowElement.innerHTML = '$' + lowPrice + '&nbsp  ';

      highElement.className = 'small';
      highElement.innerHTML = '$' + highPrice;

      lowElement.className = 'small down';
      highElement.className = 'small up';

      wrapper.appendChild(lowElement);
      wrapper.appendChild(highElement);
    }

    return wrapper;
  },

  scheduleUpdate: function(delay) {
    console.log("Hako-ticker.js: scheduleUpdate function running");
    var nextLoad = this.config.updateInterval;
    if (typeof delay !== "undefined" && delay >= 0) {
      nextLoad = delay;
    }

    var self = this;
    setInterval(function() {
      self.getTickers();
    }, nextLoad);
  },

  getTickers: function () {
    console.log("Hako-ticker.js: getTickers");
    var url = 'https://www.coinhako.com/api/v1/wallet/supported_currencies';
	  this.sendSocketNotification('GET_DATA', url);
  },

  socketNotificationReceived: function(notification, payload, payload2) {
    console.log("Hako-ticker.js: socketNotificationReceived");
    if (notification === "DATA_RESULT") {
      this.result = payload;
      this.updateDom(self.config.fadeSpeed);
    }
  },

});
<commit_msg>Hako-ticker: Proper module registration<commit_after>'use strict';

Module.register("hako-ticker", {

  result: {},
  defaults: {
    updateInterval: 30000,
	  showHighLow: true,
    highLowColor: true
  },

  getStyles: function() {
    return ["Hako-ticker.css"];
  },

  start: function() {
    console.log("Hako-ticker.js: start function running");
    this.getTickers();
    this.scheduleUpdate();
  },

  getDom: function() {
    console.log("Hako-ticker.js: getDom function running");
    var wrapper = document.createElement("ticker");
    wrapper.className = 'medium bright';
    wrapper.className = 'ticker';

    var data = this.result;

    var symbolElement =  document.createElement("span");
    var breakElement =  document.createElement("br");

    for (i = 0; i < data.length; i++) {
      var symbol = data[i].pair_name;
      //var lastPrice = data.buy_price;
      var highPrice = data[i].buy_price;
      var lowPrice = data[i].sell_price;

      var priceElement = document.createElement("span");
      var lowElement = document.createElement("span");
      var highElement = document.createElement("span");

      symbolElement.innerHTML = symbol + ' $';
      wrapper.appendChild(symbolElement);
      priceElement.innerHTML = highPrice;
      wrapper.appendChild(priceElement);
      wrapper.appendChild(breakElement);
      lowElement.className = 'small';
      lowElement.innerHTML = '$' + lowPrice + '&nbsp  ';

      highElement.className = 'small';
      highElement.innerHTML = '$' + highPrice;

      lowElement.className = 'small down';
      highElement.className = 'small up';

      wrapper.appendChild(lowElement);
      wrapper.appendChild(highElement);
    }

    return wrapper;
  },

  scheduleUpdate: function(delay) {
    console.log("Hako-ticker.js: scheduleUpdate function running");
    var nextLoad = this.config.updateInterval;
    if (typeof delay !== "undefined" && delay >= 0) {
      nextLoad = delay;
    }

    var self = this;
    setInterval(function() {
      self.getTickers();
    }, nextLoad);
  },

  getTickers: function () {
    console.log("Hako-ticker.js: getTickers");
    var url = 'https://www.coinhako.com/api/v1/wallet/supported_currencies';
	  this.sendSocketNotification('GET_DATA', url);
  },

  socketNotificationReceived: function(notification, payload, payload2) {
    console.log("Hako-ticker.js: socketNotificationReceived");
    if (notification === "DATA_RESULT") {
      this.result = payload;
      this.updateDom(self.config.fadeSpeed);
    }
  },

});
<|endoftext|>


<commit_before>var util = require('util');
var EventEmitter = require('events').EventEmitter;

function GenieMini(id) {
    this.btServiceIds = ['f000474d04514000b000000000000000'];

    this._id = id;
    this._gmCh = null;
    this._dev = null;
    this._moving = false;
    this._angle = null;
    this._position = 0;
    this._currentMove = null;
    this._stepsPerDegree = 186.56716418;

    this.orientation = null;
}

util.inherits(GenieMini, EventEmitter);

GenieMini.prototype._connectBt = function(btPeripheral, callback) {
    var self = this;
    btPeripheral.connect(function(err1) {
        if (err1) {
            if (callback) callback(false);
            return;
        }
        console.log('GenieMini(' + self._id + '): connecting via BLE');
        setTimeout(function(){
            btPeripheral.discoverServices(self.btServiceIds, function(err2, services) {
                if (services && services[0]) {
                    services[0].discoverCharacteristics([], function(err, characteristics) {
                        //console.log("characteristics:", characteristics);
                        self._gmCh = null;
                        self._dev = null;
                        for (var i = 0; i < characteristics.length; i++) {
                            ch = characteristics[i];
                            console.log("ch.uuid", ch.uuid);
                            if (ch.uuid == "2") {
                                self._gmCh = ch;
                            }
                        }
                        if (self._gmCh) {
                            try {
                                self._gmCh.write(new Buffer("012600", 'hex'));
                                self._gmCh.subscribe(function(){
                                    self._dev = btPeripheral;
                                    self._dev.connected = true;
                                    self.connected = true;
                                    self._dev.type = "bt";
                                    self._gmCh.on('data', function(data, isNotification) {
                                        self._parseIncoming(data);
                                    });
                                    console.log("GenieMini(" + self._id + "): connected!");
                                    self._init();
                                    if (callback) callback(true);
                                });
                            } catch(err3) {
                                btPeripheral.disconnect();
                                console.log("GenieMini(" + self._id + "): exception while connecting: ", err3);
                                if (callback) callback(false);
                            }
                        } else {
                            console.log("GenieMini(" + self._id + "): couldn't locate characteristics, disconnecting... ", err);
                            btPeripheral.disconnect();
                            if (callback) callback(false);
                        }
                    });
                } else {
                    console.log("GenieMini(" + self._id + "): couldn't locate services, disconnecting... ", err2);
                    btPeripheral.disconnect();
                    if (callback) callback(false);
                }

            });
        }, 1000);

        btPeripheral.once('disconnect', function() {
            console.log("GenieMini(" + self._id + "): disconnected");
            self._dev = null;
            self.connected = false;
            //clearInterval(self.statusIntervalHandle);
            self.emit("status", self.getStatus());
        });

    });
}

GenieMini.prototype._init = function() {
    var self = this;
    var dataBuf = new Buffer(4);
    dataBuf.fill(0);
    dataBuf.writeInt32LE(0x000B, 0);
    //self.statusIntervalHandle = setInterval(function(){
    self._write(0x001E, dataBuf, function(err) { // checks orientation
        var tries = 0;
        var waitForOrientation = function() {
            tries++;
            if(self.orientation || tries > 5) {
                self.emit("status", self.getStatus());
            } else {
                setTimeout(waitForOrientation, 500);
            }
        }
        waitForOrientation();
    });
    //}, 5000);
}

GenieMini.prototype._parseIncoming = function(data) {
    var id = data.readUInt16LE(1);
    if(id == 0x0060 || id == 0x0061) { // moving steps
        var state = data.readUInt8(7);
        var steps = data.readInt16LE(3);
        this._moving = (state == 0x01);
        console.log("GenieMini(" + this._id + "): moving: ", this._moving, ", steps:", steps, "id:", id);
    } else if(id == 0x000B || id == 0x0052) { // current state
        var state = data.readUInt8(4);
        var angle = data.readUInt16LE(12);
        this._angle = angle;
        this._moving = (state == 0x01);
        console.log("GenieMini(" + this._id + "): moving: ", this._moving, ", angle:", angle, "id:", id);
        if(this._currentMove != null && this._lastAngle != null) {
            if(Math.abs(angle - this._lastAngle) < 180) {
                this._currentMove += angle - this._lastAngle;
            } else {
                if(this._lastAngle < angle) {
                    this._currentMove += 359 - (angle - this._lastAngle);
                } else {
                    this._currentMove -= 359 - (this._lastAngle - angle);
                }
            }
            console.log("GenieMini(" + this._id + "): _currentMove", this._currentMove);
        }
        this._lastAngle = angle;
    } else if(id == 0x0025) {
        var orientation = data.readUInt8(3);
        var origOrientation = this.orientation;
        if(orientation == 0x90) this.orientation = 'pan';
        if(orientation == 0x94) this.orientation = 'tilt';
        if(origOrientation != this.orientation) {
            console.log("GenieMini(" + this._id + "): orientation:", this.orientation);
            self.emit("status", self.getStatus());
        }
    } else {
        console.log("GenieMini(" + this._id + "): unknown id", id, "data", data);
    }
}

GenieMini.prototype.connect = function(device, callback) {
    if (device && device.connect) {
        this._moving = false;
        console.log("GenieMini(" + this._id + "): connecting...");
        this._connectBt(device, callback);
    } else {
        console.log("GenieMini(" + this._id + "): invalid device, cannot connect");
        if (callback) callback("invalid device");
    }
}

GenieMini.prototype.disconnect = function() {
    console.log("GenieMini(" + this._id + "): disconnecting...");
    if (this._dev && this._dev.connected) {
        this.connected = false;
        this._dev.connected = false;
        this._dev.disconnect();
    } else {
        this._dev = null;
    }
    this.emit("status", this.getStatus());
}

GenieMini.prototype.getStatus = function() {
    var type = (this._dev && this._dev.type) ? this._dev.type : null;
    return {
        connected: this._dev && this._dev.connected,
        connectionType: type,
        motor1: this._dev && this._dev.connected,
        motor1pos: this._position / this._stepsPerDegree,
        orientation: this.orientation
    }
}

GenieMini.prototype.disable = function(motor) {
    this._enabled = false;
}

GenieMini.prototype.enable = function(motor) {
    this._enabled = true;
}

GenieMini.prototype.move = function(motor, degrees, callback) {
    if (this._moving) {
        if (callback) callback("GenieMini(" + this._id + "): motor already running");
        return console.log("GenieMini(" + this._id + "): motor already running");
    }
    var steps = Math.round(degrees * this._stepsPerDegree);
    if(!steps) {
        console.log("GenieMini(" + this._id + "): NOT moving motor", steps, "steps, (", degrees, " degrees)");
        return callback && callback(null, this._position / this._stepsPerDegree);
    }
    console.log("GenieMini(" + this._id + "): moving motor", steps, "steps");
    if(!this._enabled) this.enable();

    var dataBuf = new Buffer(4);
    dataBuf.fill(0);
    dataBuf.writeInt32LE(steps, 0);
    this._moving = true;

    var self = this;
    self._write(0x005E, dataBuf, function(err) {
        if (!err) {
            var check = function() {
                setTimeout(function() {
                    if(self._moving === undefined) return check();;
                    if(self._moving) {
                        check(); // keep checking until stop
                    } else {
                        self._position += steps;
                        console.log("GenieMini(" + self._id + "): position:", self._position);
                        if (callback) callback(null, self._position / self._stepsPerDegree);
                    }
                }, 200);
            }
            check();
        } else {
            if (callback) callback(err);
            self._moving = false;
        }
    });
}
//<Buffer 01 5e 00 00 00 03 a4 00>
GenieMini.prototype._write = function(command, dataBuf, callback) {
    var template = new Buffer("010000", 'hex');
    var len = dataBuf ? dataBuf.length : 0;
    var COMMAND = 0;

    template.writeUInt16LE(parseInt(command), 1);

    var cmd;
    if (dataBuf) {
        cmd = Buffer.concat([template, dataBuf]);
    } else {
        cmd = template;
    }

    //console.log("GenieMini(" + this._id + "): sending data", cmd);

    try {
        this._gmCh.write(cmd);
        callback && callback();
    } catch(err) {
        callback && callback(err);
    }
}

GenieMini.prototype.constantMove = function(motor, speed, callback) {
    console.log("GenieMini(" + this._id + "): moving motor at speed ", speed, "%");
    if(!this._enabled) this.enable();

    var dataBuf = new Buffer(3);
    var self = this;
    dataBuf.fill(0);
    dataBuf.writeInt8(Math.round(speed), 0);
    dataBuf.writeUInt16LE(speed ? 0x01 : 0x00, 1);
    this._moving = true;

    if(this._watchdog) {
        clearTimeout(this._watchdog);
        this._watchdog = null;
    }
    if(speed) {
        this._watchdog = setTimeout(function(){
            console.log("GenieMini(" + self._id + "): stopping via watchdog");
            self.constantMove(motor, 0);
        }, 3000);
    }

    var sendMove = function() {
        self._write(0x003F, dataBuf, function(err) {
            if (!err) {
                var check = function() {
                    if(this._endTimer) clearTimeout(this._endTimer);
                    if(!speed) {
                        self._endTimer = setTimeout(function() {
                            if(self._moving === undefined) return;
                            if(self._moving) {
                                check(); // keep checking until stop
                            } else {
                                console.log("GenieMini(" + self._id + "): adding", self._currentMove, "° to position");
                                self._position += self._currentMove * self._stepsPerDegree;
                                self._currentMove = null;
                                if (callback) callback(null, self._position / self._stepsPerDegree);
                            }
                        }, 200);
                    } else {
                        if (callback) callback(null, null);
                    }
                }
                check();
            } else {
                if (callback) callback(err);
                self._moving = false;
            }
        });
    }

    var self = this;
    if(this._endTimer) clearTimeout(this._endTimer);
    if(self._currentMove == null) {
        self._lastAngle = null;
        self._currentMove = 0;
        console.log("GenieMini(" + self._id + "): starting move at speed, saving position");
        var d = new Buffer(2);
        d.fill(0);
        d.writeUInt16LE(0x000B, 0);
        this._moving = true;
        self._write(0x001E, d, function(err) {
            sendMove();
        });
    } else {
        sendMove();
    }
}

GenieMini.prototype.resetMotorPosition = function(motor, callback) {
    var self = this;
    var check = function() {
        if(self._moving) {
            setTimeout(check, 200); // keep checking until stop
        } else {
            self._position = 0;
            if (callback) callback();
        }
    }
    check();
}

GenieMini.prototype.setMotorPosition = function(motor, position, callback) {
    var self = this;
    var check = function() {
        if(self._moving) {
            setTimeout(check, 200); // keep checking until stop
        } else {
            console.log("GenieMini(" + self._id + "): updating position to", position);
            self._position = position;
            if (callback) callback();
        }
    }
    check();
}

module.exports = GenieMini;
<commit_msg>genie mini auto-detect orientation testing<commit_after>var util = require('util');
var EventEmitter = require('events').EventEmitter;

function GenieMini(id) {
    this.btServiceIds = ['f000474d04514000b000000000000000'];

    this._id = id;
    this._gmCh = null;
    this._dev = null;
    this._moving = false;
    this._angle = null;
    this._position = 0;
    this._currentMove = null;
    this._stepsPerDegree = 186.56716418;

    this.orientation = null;
}

util.inherits(GenieMini, EventEmitter);

GenieMini.prototype._connectBt = function(btPeripheral, callback) {
    var self = this;
    btPeripheral.connect(function(err1) {
        if (err1) {
            if (callback) callback(false);
            return;
        }
        console.log('GenieMini(' + self._id + '): connecting via BLE');
        setTimeout(function(){
            btPeripheral.discoverServices(self.btServiceIds, function(err2, services) {
                if (services && services[0]) {
                    services[0].discoverCharacteristics([], function(err, characteristics) {
                        //console.log("characteristics:", characteristics);
                        self._gmCh = null;
                        self._dev = null;
                        for (var i = 0; i < characteristics.length; i++) {
                            ch = characteristics[i];
                            console.log("ch.uuid", ch.uuid);
                            if (ch.uuid == "2") {
                                self._gmCh = ch;
                            }
                        }
                        if (self._gmCh) {
                            try {
                                self._gmCh.write(new Buffer("012600", 'hex'));
                                self._gmCh.subscribe(function(){
                                    self._dev = btPeripheral;
                                    self._dev.connected = true;
                                    self.connected = true;
                                    self._dev.type = "bt";
                                    self._gmCh.on('data', function(data, isNotification) {
                                        self._parseIncoming(data);
                                    });
                                    console.log("GenieMini(" + self._id + "): connected!");
                                    self._init();
                                    if (callback) callback(true);
                                });
                            } catch(err3) {
                                btPeripheral.disconnect();
                                console.log("GenieMini(" + self._id + "): exception while connecting: ", err3);
                                if (callback) callback(false);
                            }
                        } else {
                            console.log("GenieMini(" + self._id + "): couldn't locate characteristics, disconnecting... ", err);
                            btPeripheral.disconnect();
                            if (callback) callback(false);
                        }
                    });
                } else {
                    console.log("GenieMini(" + self._id + "): couldn't locate services, disconnecting... ", err2);
                    btPeripheral.disconnect();
                    if (callback) callback(false);
                }

            });
        }, 1000);

        btPeripheral.once('disconnect', function() {
            console.log("GenieMini(" + self._id + "): disconnected");
            self._dev = null;
            self.connected = false;
            //clearInterval(self.statusIntervalHandle);
            self.emit("status", self.getStatus());
        });

    });
}

GenieMini.prototype._init = function() {
    var self = this;
    var dataBuf = new Buffer(4);
    dataBuf.fill(0);
    dataBuf.writeInt32LE(0x000B, 0);
    //self.statusIntervalHandle = setInterval(function(){
    self._write(0x001E, dataBuf, function(err) { // checks orientation
        var tries = 0;
        var waitForOrientation = function() {
            tries++;
            if(self.orientation || tries > 5) {
                self.emit("status", self.getStatus());
            } else {
                setTimeout(waitForOrientation, 500);
            }
        }
        waitForOrientation();
    });
    //}, 5000);
}

GenieMini.prototype._parseIncoming = function(data) {
    var id = data.readUInt16LE(1);
    if(id == 0x0060 || id == 0x0061) { // moving steps
        var state = data.readUInt8(7);
        var steps = data.readInt16LE(3);
        this._moving = (state == 0x01);
        console.log("GenieMini(" + this._id + "): moving: ", this._moving, ", steps:", steps, "id:", id);
    } else if(id == 0x000B || id == 0x0052) { // current state
        var state = data.readUInt8(4);
        var angle = data.readUInt16LE(12);
        this._angle = angle;
        this._moving = (state == 0x01);
        console.log("GenieMini(" + this._id + "): moving: ", this._moving, ", angle:", angle, "id:", id);
        if(this._currentMove != null && this._lastAngle != null) {
            if(Math.abs(angle - this._lastAngle) < 180) {
                this._currentMove += angle - this._lastAngle;
            } else {
                if(this._lastAngle < angle) {
                    this._currentMove += 359 - (angle - this._lastAngle);
                } else {
                    this._currentMove -= 359 - (this._lastAngle - angle);
                }
            }
            console.log("GenieMini(" + this._id + "): _currentMove", this._currentMove);
        }
        this._lastAngle = angle;
    } else if(id == 0x0025) {
        var orientation = data.readUInt8(3);
        var origOrientation = this.orientation;
        if(orientation == 0x90) this.orientation = 'pan';
        if(orientation == 0x94) this.orientation = 'tilt';
        if(origOrientation != this.orientation) {
            console.log("GenieMini(" + this._id + "): orientation:", this.orientation);
            this.emit("status", this.getStatus());
        }
    } else {
        console.log("GenieMini(" + this._id + "): unknown id", id, "data", data);
    }
}

GenieMini.prototype.connect = function(device, callback) {
    if (device && device.connect) {
        this._moving = false;
        console.log("GenieMini(" + this._id + "): connecting...");
        this._connectBt(device, callback);
    } else {
        console.log("GenieMini(" + this._id + "): invalid device, cannot connect");
        if (callback) callback("invalid device");
    }
}

GenieMini.prototype.disconnect = function() {
    console.log("GenieMini(" + this._id + "): disconnecting...");
    if (this._dev && this._dev.connected) {
        this.connected = false;
        this._dev.connected = false;
        this._dev.disconnect();
    } else {
        this._dev = null;
    }
    this.emit("status", this.getStatus());
}

GenieMini.prototype.getStatus = function() {
    var type = (this._dev && this._dev.type) ? this._dev.type : null;
    return {
        connected: this._dev && this._dev.connected,
        connectionType: type,
        motor1: this._dev && this._dev.connected,
        motor1pos: this._position / this._stepsPerDegree,
        orientation: this.orientation
    }
}

GenieMini.prototype.disable = function(motor) {
    this._enabled = false;
}

GenieMini.prototype.enable = function(motor) {
    this._enabled = true;
}

GenieMini.prototype.move = function(motor, degrees, callback) {
    if (this._moving) {
        if (callback) callback("GenieMini(" + this._id + "): motor already running");
        return console.log("GenieMini(" + this._id + "): motor already running");
    }
    var steps = Math.round(degrees * this._stepsPerDegree);
    if(!steps) {
        console.log("GenieMini(" + this._id + "): NOT moving motor", steps, "steps, (", degrees, " degrees)");
        return callback && callback(null, this._position / this._stepsPerDegree);
    }
    console.log("GenieMini(" + this._id + "): moving motor", steps, "steps");
    if(!this._enabled) this.enable();

    var dataBuf = new Buffer(4);
    dataBuf.fill(0);
    dataBuf.writeInt32LE(steps, 0);
    this._moving = true;

    var self = this;
    self._write(0x005E, dataBuf, function(err) {
        if (!err) {
            var check = function() {
                setTimeout(function() {
                    if(self._moving === undefined) return check();;
                    if(self._moving) {
                        check(); // keep checking until stop
                    } else {
                        self._position += steps;
                        console.log("GenieMini(" + self._id + "): position:", self._position);
                        if (callback) callback(null, self._position / self._stepsPerDegree);
                    }
                }, 200);
            }
            check();
        } else {
            if (callback) callback(err);
            self._moving = false;
        }
    });
}
//<Buffer 01 5e 00 00 00 03 a4 00>
GenieMini.prototype._write = function(command, dataBuf, callback) {
    var template = new Buffer("010000", 'hex');
    var len = dataBuf ? dataBuf.length : 0;
    var COMMAND = 0;

    template.writeUInt16LE(parseInt(command), 1);

    var cmd;
    if (dataBuf) {
        cmd = Buffer.concat([template, dataBuf]);
    } else {
        cmd = template;
    }

    //console.log("GenieMini(" + this._id + "): sending data", cmd);

    try {
        this._gmCh.write(cmd);
        callback && callback();
    } catch(err) {
        callback && callback(err);
    }
}

GenieMini.prototype.constantMove = function(motor, speed, callback) {
    console.log("GenieMini(" + this._id + "): moving motor at speed ", speed, "%");
    if(!this._enabled) this.enable();

    var dataBuf = new Buffer(3);
    var self = this;
    dataBuf.fill(0);
    dataBuf.writeInt8(Math.round(speed), 0);
    dataBuf.writeUInt16LE(speed ? 0x01 : 0x00, 1);
    this._moving = true;

    if(this._watchdog) {
        clearTimeout(this._watchdog);
        this._watchdog = null;
    }
    if(speed) {
        this._watchdog = setTimeout(function(){
            console.log("GenieMini(" + self._id + "): stopping via watchdog");
            self.constantMove(motor, 0);
        }, 3000);
    }

    var sendMove = function() {
        self._write(0x003F, dataBuf, function(err) {
            if (!err) {
                var check = function() {
                    if(this._endTimer) clearTimeout(this._endTimer);
                    if(!speed) {
                        self._endTimer = setTimeout(function() {
                            if(self._moving === undefined) return;
                            if(self._moving) {
                                check(); // keep checking until stop
                            } else {
                                console.log("GenieMini(" + self._id + "): adding", self._currentMove, "° to position");
                                self._position += self._currentMove * self._stepsPerDegree;
                                self._currentMove = null;
                                if (callback) callback(null, self._position / self._stepsPerDegree);
                            }
                        }, 200);
                    } else {
                        if (callback) callback(null, null);
                    }
                }
                check();
            } else {
                if (callback) callback(err);
                self._moving = false;
            }
        });
    }

    var self = this;
    if(this._endTimer) clearTimeout(this._endTimer);
    if(self._currentMove == null) {
        self._lastAngle = null;
        self._currentMove = 0;
        console.log("GenieMini(" + self._id + "): starting move at speed, saving position");
        var d = new Buffer(2);
        d.fill(0);
        d.writeUInt16LE(0x000B, 0);
        this._moving = true;
        self._write(0x001E, d, function(err) {
            sendMove();
        });
    } else {
        sendMove();
    }
}

GenieMini.prototype.resetMotorPosition = function(motor, callback) {
    var self = this;
    var check = function() {
        if(self._moving) {
            setTimeout(check, 200); // keep checking until stop
        } else {
            self._position = 0;
            if (callback) callback();
        }
    }
    check();
}

GenieMini.prototype.setMotorPosition = function(motor, position, callback) {
    var self = this;
    var check = function() {
        if(self._moving) {
            setTimeout(check, 200); // keep checking until stop
        } else {
            console.log("GenieMini(" + self._id + "): updating position to", position);
            self._position = position;
            if (callback) callback();
        }
    }
    check();
}

module.exports = GenieMini;
<|endoftext|>


<commit_before>import React, { Component } from 'react';

class SaladDisplay extends Component{

  constructor(props) {
    super(props);
    this.state = {
      searchText: "",
      recipeError: "",
      recipeTitle: "",
      recipeUrl: "",
      recipeImg: "",
      recipePublisher: ""

    };
  }

  render() {
    return (

      {this.state.recipeTitle}

      Check it out on {this.state.recipePublisher}

      {this.state.recipeError}

    );
  }

  getRecipe(text) {
    if (text.length > 0) {
      let query = encodeURIComponent(text);
      let serverEndpoint = '/salad/' + query;
      fetch(serverEndpoint)
        .then(response => response.json())
        .then(data => {
          if (data.recipes != null) {
            this.setState({
              recipeTitle: data.recipes[0].title,
              recipeUrl: data.recipes[0].source_url,
              recipeImg: data.recipes[0].image_url,
              recipePublisher: data.recipes[0].publisher,
              recipeError: ""
            });
          } else {
            this.setState({recipeError: "No recipes found with those ingredients!"})
          }
        })
        .catch(error => {
          console.log("fetch error ", error);
        });
    }
  }
}

export default SaladDisplay;
<commit_msg>salad Display now returns random recipe<commit_after>import React, { Component } from 'react';

class SaladDisplay extends Component{

  constructor(props) {
    super(props);
    this.state = {
      searchText: "",
      recipeError: "",
      recipeTitle: "",
      recipeUrl: "",
      recipeImg: "",
      recipePublisher: ""

    };
  }

  render() {
    return (

      {this.state.recipeTitle}

      Check it out on {this.state.recipePublisher}

      {this.state.recipeError}

    );
  }

  getRecipe(text) {
    if (text.length > 0) {
      let query = encodeURIComponent(text);
      let serverEndpoint = '/salad/' + query;
      fetch(serverEndpoint)
        .then(response => response.json())
        .then(data => {
          if (data.recipes != null) {
            let numberOfRecipes = data.recipes.length;
            let randomNumber = Math.floor((Math.random() * numberOfRecipes) + 1);
            this.setState({
              recipeTitle: data.recipes[randomNumber].title,
              recipeUrl: data.recipes[randomNumber].source_url,
              recipeImg: data.recipes[randomNumber].image_url,
              recipePublisher: data.recipes[randomNumber].publisher,
              recipeError: ""
            });
          } else {
            this.setState({
              recipeTitle: null,
              recipeUrl: null,
              recipeImg: null,
              recipePublisher: null,
              recipeError: "No recipes found with those ingredients!",
            })
          }
        })
        .catch(error => {
          console.log("fetch error ", error);
        });
    }
  }
}

export default SaladDisplay;
<|endoftext|>