text: //! annyang
  //! version : 2.0.0
  //! author  : Tal Ater @TalAter
  //! license : MIT
  //! https://www.TalAter.com/annyang/
  
  (function (undefined) {
    "use strict";
  
    /**
     * # Quick Tutorial, Intro and Demos
     *
     * The quickest way to get started is to visit the [annyang homepage](https://www.talater.com/annyang/).
     *
     * For a more in-depth look at annyang, read on.
     *
     * # API Reference
     */
  
    // Save a reference to the global object (window in the browser)
    var root = this;
  
    // Get the SpeechRecognition object, while handling browser prefixes
    var SpeechRecognition = root.SpeechRecognition ||
                            root.webkitSpeechRecognition ||
                            root.mozSpeechRecognition ||
                            root.msSpeechRecognition ||
                            root.oSpeechRecognition;
  
    // Check browser support
    // This is done as early as possible, to make it as fast as possible for unsupported browsers
    if (!SpeechRecognition) {
      root.annyang = null;
      return undefined;
    }
  
    var commandsList = [];
    var recognition;
    var callbacks = { start: [], error: [], end: [], result: [], resultMatch: [], resultNoMatch: [], interimResult: [], errorNetwork: [], errorPermissionBlocked: [], errorPermissionDenied: [] };
    var autoRestart;
    var lastStartedAt = 0;
    var debugState = false;
    var debugStyle = 'font-weight: bold; color: #00f;';
    var pauseListening = false;
  
    // The command matching code is a modified version of Backbone.Router by Jeremy Ashkenas, under the MIT license.
    var optionalParam = /\s*\((.*?)\)\s*/g;
    var optionalRegex = /(\(\?:[^)]+\))\?/g;
    var namedParam    = /(\(\?)?:\w+/g;
    var splatParam    = /\*\w+/g;
    var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#]/g;
    var commandToRegExp = function(command) {
      command = command.replace(escapeRegExp, '\\$&')
                    .replace(optionalParam, '(?:$1)?')
                    .replace(namedParam, function(match, optional) {
                      return optional ? match : '([^\\s]+)';
                    })
                    .replace(splatParam, '(.*?)')
                    .replace(optionalRegex, '\\s*$1?\\s*');
      return new RegExp('^' + command + '$', 'i');
    };
  
    // This method receives an array of callbacks to iterate over, and invokes each of them
    var invokeCallbacks = function(callbacks) {
      var args = Array.prototype.slice.call(arguments, 1);
      callbacks.forEach(function(callback) {
        callback.callback.apply(callback.context, args);
      });
    };
  
    var initIfNeeded = function() {
      if (!isInitialized()) {
        root.annyang.init({}, false);
      }
    };
  
    var isInitialized = function() {
      return recognition !== undefined;
    };
  
    var registerCommand = function(command, cb, phrase) {
      commandsList.push({ command: command, callback: cb, originalPhrase: phrase });
      if (debugState) {
        root.console.log('Command successfully loaded: %c'+phrase, debugStyle);
      }
    };
  
    root.annyang = {
  
      /**
       * Initialize annyang with a list of commands to recognize.
       *
       * #### Examples:
       * ````javascript
       * var commands = {'hello :name': helloFunction};
       * var commands2 = {'hi': helloFunction};
       *
       * // initialize annyang, overwriting any previously added commands
       * annyang.init(commands, true);
       * // adds an additional command without removing the previous commands
       * annyang.init(commands2, false);
       * ````
       * As of v1.1.0 it is no longer required to call init(). Just start() listening whenever you want, and addCommands() whenever, and as often as you like.
       *
       * @param {Object} commands - Commands that annyang should listen to
       * @param {Boolean} [resetCommands=true] - Remove all commands before initializing?
       * @method init
       * @deprecated
       * @see [Commands Object](#commands-object)
       */
      init: function(commands, resetCommands) {
  
        // resetCommands defaults to true
        if (resetCommands === undefined) {
          resetCommands = true;
        } else {
          resetCommands = !!resetCommands;
        }
  
        // Abort previous instances of recognition already running
        if (recognition && recognition.abort) {
          recognition.abort();
        }
  
        // initiate SpeechRecognition
        recognition = new SpeechRecognition();
  
        // Set the max number of alternative transcripts to try and match with a command
        recognition.maxAlternatives = 5;
  
        // In HTTPS, turn off continuous mode for faster results.
        // In HTTP,  turn on  continuous mode for much slower results, but no repeating security notices
        recognition.continuous = root.location.protocol === 'http:';
  
        //Use interimResults to imporve performance
        recognition.interimResults = true;
  
        // Sets the language to the default 'en-US'. This can be changed with annyang.setLanguage()
        recognition.lang = 'en-US';
  
        recognition.onstart   = function()      { invokeCallbacks(callbacks.start); };
  
        recognition.onerror   = function(event) {
          invokeCallbacks(callbacks.error, event);
          switch (event.error) {
          case 'network':
            invokeCallbacks(callbacks.errorNetwork);
            break;
          case 'not-allowed':
          case 'service-not-allowed':
            // if permission to use the mic is denied, turn off auto-restart
            autoRestart = false;
            // determine if permission was denied by user or automatically.
            if (new Date().getTime()-lastStartedAt < 200) {
              invokeCallbacks(callbacks.errorPermissionBlocked);
            } else {
              invokeCallbacks(callbacks.errorPermissionDenied);
            }
            break;
          }
        };
  
        recognition.onend     = function() {
          invokeCallbacks(callbacks.end);
          // annyang will auto restart if it is closed automatically and not by user action.
          if (autoRestart) {
            // play nicely with the browser, and never restart annyang automatically more than once per second
            var timeSinceLastStart = new Date().getTime()-lastStartedAt;
            if (timeSinceLastStart < 1000) {
              setTimeout(root.annyang.start, 1000-timeSinceLastStart);
            } else {
              root.annyang.start();
            }
          }
        };
  
        recognition.onresult  = function(event) {
          if(pauseListening) {
            if (debugState) {
              root.console.log('Speech heard, but annyang is paused');
            }
            return false;
          }
  
          // Map the results to an array
          var SpeechRecognitionResult = event.results[event.resultIndex];
          var results = [];
          var finalResults = false
          for (var k = 0; k<SpeechRecognitionResult.length; k++) {
            if(SpeechRecognitionResult.isFinal){
              finalResults = true;
              results[k] = SpeechRecognitionResult[k].transcript;
            }
            else if(k == 0){
              //root.console.log('Interim: %c' + SpeechRecognitionResult[k].transcript, debugStyle);
              invokeCallbacks(callbacks.interimResult, SpeechRecognitionResult[k].transcript, null);
            }
          }
  
          // When there is a final result invoke the result callback
          if(finalResults){
            invokeCallbacks(callbacks.result, results);
          }
  
          var commandText;
          // go over each of the 5 results and alternative results received (we've set maxAlternatives to 5 above)
          for (var i = 0; i<results.length; i++) {
            // the text recognized
            commandText = results[i].trim();
            if (debugState) {
              root.console.log('Speech recognized: %c'+commandText, debugStyle);
            }
  
            // try and match recognized text to one of the commands on the list
            for (var j = 0, l = commandsList.length; j < l; j++) {
              var result = commandsList[j].command.exec(commandText);
              if (result) {
                var parameters = result.slice(1);
                if (debugState) {
                  root.console.log('command matched: %c'+commandsList[j].originalPhrase, debugStyle);
                  if (parameters.length) {
                    root.console.log('with parameters', parameters);
                  }
                }
                // execute the matched command
                commandsList[j].callback.apply(this, parameters);
                invokeCallbacks(callbacks.resultMatch, commandText, commandsList[j].originalPhrase, results);
                return true;
              }
            }
          }
          invokeCallbacks(callbacks.resultNoMatch, results);
          return false;
        };
  
        // build commands list
        if (resetCommands) {
          commandsList = [];
        }
        if (commands.length) {
          this.addCommands(commands);
        }
      },
  
      /**
       * Start listening.
       * It's a good idea to call this after adding some commands first, but not mandatory.
       *
       * Receives an optional options object which supports the following options:
       * - `autoRestart` (Boolean, default: true) Should annyang restart itself if it is closed indirectly, because of silence or window conflicts?
       * - `continuous`  (Boolean, default: undefined) Allow forcing continuous mode on or off. Annyang is pretty smart about this, so only set this if you know what you're doing.
       *
       * #### Examples:
       * ````javascript
       * // Start listening, don't restart automatically
       * annyang.start({ autoRestart: false });
       * // Start listening, don't restart automatically, stop recognition after first phrase recognized
       * annyang.start({ autoRestart: false, continuous: false });
       * ````
       * @param {Object} [options] - Optional options.
       * @method start
       */
      start: function(options) {
        pauseListening = false;
        initIfNeeded();
        options = options || {};
        if (options.autoRestart !== undefined) {
          autoRestart = !!options.autoRestart;
        } else {
          autoRestart = true;
        }
        if (options.continuous !== undefined) {
          recognition.continuous = !!options.continuous;
        }
  
        lastStartedAt = new Date().getTime();
        try {
          recognition.start();
        } catch(e) {
          if (debugState) {
            root.console.log(e.message);
          }
        }
      },
  
      /**
       * Stop listening, and turn off mic.
       *
       * Alternatively, to only temporarily pause annyang responding to commands without stopping the SpeechRecognition engine or closing the mic, use pause() instead.
       * @see [pause()](#pause)
       *
       * @method abort
       */
      abort: function() {
        autoRestart = false;
        if (isInitialized) {
          recognition.abort();
        }
      },
  
      /**
       * Pause listening. annyang will stop responding to commands (until the resume or start methods are called), without turning off the browser's SpeechRecognition engine or the mic.
       *
       * Alternatively, to stop the SpeechRecognition engine and close the mic, use abort() instead.
       * @see [abort()](#abort)
       *
       * @method pause
       */
      pause: function() {
        pauseListening = true;
      },
  
      /**
       * Resumes listening and restores command callback execution when a result matches.
       * If SpeechRecognition was aborted (stopped), start it.
       *
       * @method resume
       */
      resume: function() {
        root.annyang.start();
      },
  
      /**
       * Turn on output of debug messages to the console. Ugly, but super-handy!
       *
       * @param {Boolean} [newState=true] - Turn on/off debug messages
       * @method debug
       */
      debug: function(newState) {
        if (arguments.length > 0) {
          debugState = !!newState;
        } else {
          debugState = true;
        }
      },
  
      /**
       * Set the language the user will speak in. If this method is not called, defaults to 'en-US'.
       *
       * @param {String} language - The language (locale)
       * @method setLanguage
       * @see [Languages](#languages)
       */
      setLanguage: function(language) {
        initIfNeeded();
        recognition.lang = language;
      },
  
      /**
       * Add commands that annyang will respond to. Similar in syntax to init(), but doesn't remove existing commands.
       *
       * #### Examples:
       * ````javascript
       * var commands = {'hello :name': helloFunction, 'howdy': helloFunction};
       * var commands2 = {'hi': helloFunction};
       *
       * annyang.addCommands(commands);
       * annyang.addCommands(commands2);
       * // annyang will now listen to all three commands
       * ````
       *
       * @param {Object} commands - Commands that annyang should listen to
       * @method addCommands
       * @see [Commands Object](#commands-object)
       */
      addCommands: function(commands) {
        var cb;
  
        initIfNeeded();
  
        for (var phrase in commands) {
          if (commands.hasOwnProperty(phrase)) {
            cb = root[commands[phrase]] || commands[phrase];
            if (typeof cb === 'function') {
              // convert command to regex then register the command
              registerCommand(commandToRegExp(phrase), cb, phrase);
            } else if (typeof cb === 'object' && cb.regexp instanceof RegExp) {
              // register the command
              registerCommand(new RegExp(cb.regexp.source, 'i'), cb.callback, phrase);
            } else {
              if (debugState) {
                root.console.log('Can not register command: %c'+phrase, debugStyle);
              }
              continue;
            }
          }
        }
      },
  
      /**
       * Remove existing commands. Called with a single phrase, array of phrases, or methodically. Pass no params to remove all commands.
       *
       * #### Examples:
       * ````javascript
       * var commands = {'hello': helloFunction, 'howdy': helloFunction, 'hi': helloFunction};
       *
       * // Remove all existing commands
       * annyang.removeCommands();
       *
       * // Add some commands
       * annyang.addCommands(commands);
       *
       * // Don't respond to hello
       * annyang.removeCommands('hello');
       *
       * // Don't respond to howdy or hi
       * annyang.removeCommands(['howdy', 'hi']);
       * ````
       * @param {String|Array|Undefined} [commandsToRemove] - Commands to remove
       * @method removeCommands
       */
      removeCommands: function(commandsToRemove) {
        if (commandsToRemove === undefined) {
          commandsList = [];
          return;
        }
        commandsToRemove = Array.isArray(commandsToRemove) ? commandsToRemove : [commandsToRemove];
        commandsList = commandsList.filter(function(command) {
          for (var i = 0; i<commandsToRemove.length; i++) {
            if (commandsToRemove[i] === command.originalPhrase) {
              return false;
            }
          }
          return true;
        });
      },
  
      /**
       * Add a callback function to be called in case one of the following events happens:
       *
       * * `start` - Fired as soon as the browser's Speech Recognition engine starts listening
       * * `error` - Fired when the browser's Speech Recogntion engine returns an error, this generic error callback will be followed by more accurate error callbacks (both will fire if both are defined)
       * * `errorNetwork` - Fired when Speech Recognition fails because of a network error
       * * `errorPermissionBlocked` - Fired when the browser blocks the permission request to use Speech Recognition.
       * * `errorPermissionDenied` - Fired when the user blocks the permission request to use Speech Recognition.
       * * `end` - Fired when the browser's Speech Recognition engine stops
       * * `result` - Fired as soon as some speech was identified. This generic callback will be followed by either the `resultMatch` or `resultNoMatch` callbacks.
       *     Callback functions registered to this event will include an array of possible phrases the user said as the first argument
       * * `resultMatch` - Fired when annyang was able to match between what the user said and a registered command
       *     Callback functions registered to this event will include three arguments in the following order:
       *       * The phrase the user said that matched a command
       *       * The command that was matched
       *       * An array of possible alternative phrases the user might've said
       * * `resultNoMatch` - Fired when what the user said didn't match any of the registered commands
       *     Callback functions registered to this event will include an array of possible phrases the user might've said as the first argument
       *
       * #### Examples:
       * ````javascript
       * annyang.addCallback('error', function () {
       *   $('.myErrorText').text('There was an error!');
       * });
       *
       * annyang.addCallback('resultMatch', function (userSaid, commandText, phrases) {
       *   console.log(userSaid); // sample output: 'hello'
       *   console.log(commandText); // sample output: 'hello (there)'
       *   console.log(phrases); // sample output: ['hello', 'halo', 'yellow', 'polo', 'hello kitty']
       * });
       *
       * // pass local context to a global function called notConnected
       * annyang.addCallback('errorNetwork', notConnected, this);
       * ````
       * @param {String} type - Name of event that will trigger this callback
       * @param {Function} callback - The function to call when event is triggered
       * @param {Object} [context] - Optional context for the callback function
       * @method addCallback
       */
      addCallback: function(type, callback, context) {
        if (callbacks[type]  === undefined) {
          return;
        }
        var cb = root[callback] || callback;
        if (typeof cb !== 'function') {
          return;
        }
        callbacks[type].push({callback: cb, context: context || this});
      }
    };
  
  }).call(this);
  
  /**
   * # Good to Know
   *
   * ## Commands Object
   *
   * Both the [init()]() and addCommands() methods receive a `commands` object.
   *
   * annyang understands commands with `named variables`, `splats`, and `optional words`.
   *
   * * Use `named variables` for one word arguments in your command.
   * * Use `splats` to capture multi-word text at the end of your command (greedy).
   * * Use `optional words` or phrases to define a part of the command as optional.
   *
   * #### Examples:
   * ````html
   * <script>
   * var commands = {
   *   // annyang will capture anything after a splat (*) and pass it to the function.
   *   // e.g. saying "Show me Batman and Robin" will call showFlickr('Batman and Robin');
   *   'show me *term': showFlickr,
   *
   *   // A named variable is a one word variable, that can fit anywhere in your command.
   *   // e.g. saying "calculate October stats" will call calculateStats('October');
   *   'calculate :month stats': calculateStats,
   *
   *   // By defining a part of the following command as optional, annyang will respond
   *   // to both: "say hello to my little friend" as well as "say hello friend"
   *   'say hello (to my little) friend': greeting
   * };
   *
   * var showFlickr = function(term) {
   *   var url = 'http://api.flickr.com/services/rest/?tags='+tag;
   *   $.getJSON(url);
   * }
   *
   * var calculateStats = function(month) {
   *   $('#stats').text('Statistics for '+month);
   * }
   *
   * var greeting = function() {
   *   $('#greeting').text('Hello!');
   * }
   * </script>
   * ````
   *
   * ### Using Regular Expressions in commands
   * For advanced commands, you can pass a regular expression object, instead of
   * a simple string command.
   *
   * This is done by passing an object containing two properties: `regexp`, and
   * `callback` instead of the function.
   *
   * #### Examples:
   * ````javascript
   * var calculateFunction = function(month) { console.log(month); }
   * var commands = {
   *   // This example will accept any word as the "month"
   *   'calculate :month stats': calculateFunction,
   *   // This example will only accept months which are at the start of a quarter
   *   'calculate :quarter stats': {'regexp': /^calculate (January|April|July|October) stats$/, 'callback': calculateFunction}
   * }
   ````
   *
   * ## Languages
   *
   * While there isn't an official list of supported languages (cultures? locales?), here is a list based on [anecdotal evidence](http://stackoverflow.com/a/14302134/338039).
   *
   * * Afrikaans `af`
   * * Basque `eu`
   * * Bulgarian `bg`
   * * Catalan `ca`
   * * Arabic (Egypt) `ar-EG`
   * * Arabic (Jordan) `ar-JO`
   * * Arabic (Kuwait) `ar-KW`
   * * Arabic (Lebanon) `ar-LB`
   * * Arabic (Qatar) `ar-QA`
   * * Arabic (UAE) `ar-AE`
   * * Arabic (Morocco) `ar-MA`
   * * Arabic (Iraq) `ar-IQ`
   * * Arabic (Algeria) `ar-DZ`
   * * Arabic (Bahrain) `ar-BH`
   * * Arabic (Lybia) `ar-LY`
   * * Arabic (Oman) `ar-OM`
   * * Arabic (Saudi Arabia) `ar-SA`
   * * Arabic (Tunisia) `ar-TN`
   * * Arabic (Yemen) `ar-YE`
   * * Czech `cs`
   * * Dutch `nl-NL`
   * * English (Australia) `en-AU`
   * * English (Canada) `en-CA`
   * * English (India) `en-IN`
   * * English (New Zealand) `en-NZ`
   * * English (South Africa) `en-ZA`
   * * English(UK) `en-GB`
   * * English(US) `en-US`
   * * Finnish `fi`
   * * French `fr-FR`
   * * Galician `gl`
   * * German `de-DE`
   * * Hebrew `he`
   * * Hungarian `hu`
   * * Icelandic `is`
   * * Italian `it-IT`
   * * Indonesian `id`
   * * Japanese `ja`
   * * Korean `ko`
   * * Latin `la`
   * * Mandarin Chinese `zh-CN`
   * * Traditional Taiwan `zh-TW`
   * * Simplified China zh-CN `?`
   * * Simplified Hong Kong `zh-HK`
   * * Yue Chinese (Traditional Hong Kong) `zh-yue`
   * * Malaysian `ms-MY`
   * * Norwegian `no-NO`
   * * Polish `pl`
   * * Pig Latin `xx-piglatin`
   * * Portuguese `pt-PT`
   * * Portuguese (Brasil) `pt-BR`
   * * Romanian `ro-RO`
   * * Russian `ru`
   * * Serbian `sr-SP`
   * * Slovak `sk`
   * * Spanish (Argentina) `es-AR`
   * * Spanish (Bolivia) `es-BO`
   * * Spanish (Chile) `es-CL`
   * * Spanish (Colombia) `es-CO`
   * * Spanish (Costa Rica) `es-CR`
   * * Spanish (Dominican Republic) `es-DO`
   * * Spanish (Ecuador) `es-EC`
   * * Spanish (El Salvador) `es-SV`
   * * Spanish (Guatemala) `es-GT`
   * * Spanish (Honduras) `es-HN`
   * * Spanish (Mexico) `es-MX`
   * * Spanish (Nicaragua) `es-NI`
   * * Spanish (Panama) `es-PA`
   * * Spanish (Paraguay) `es-PY`
   * * Spanish (Peru) `es-PE`
   * * Spanish (Puerto Rico) `es-PR`
   * * Spanish (Spain) `es-ES`
   * * Spanish (US) `es-US`
   * * Spanish (Uruguay) `es-UY`
   * * Spanish (Venezuela) `es-VE`
   * * Swedish `sv-SE`
   * * Turkish `tr`
   * * Zulu `zu`
   *
   * ## Developing
   *
   * Prerequisities: node.js
   *
   * First, install dependencies in your local annyang copy:
   *
   *     npm install
   *
   * Make sure to run the default grunt task after each change to annyang.js. This can also be done automatically by running:
   *
   *     grunt watch
   *
   * You can also run a local server for testing your work with:
   *
   *     grunt dev
   *
   * Point your browser to `https://localhost:8443/demo/` to see the demo page.
   * Since it's using self-signed certificate, you might need to click *"Proceed Anyway"*.
   *
   */
  