<commit_before>/*
---
name: Facebook Angularjs

description: Provides an easier way to make use of Facebook API with Angularjs

license: MIT-style license

authors:
  - Ciul

requires: [angular]
provides: [facebook]

...
*/
(function(window, angular, undefined) {
  'use strict';

  // Module global settings.
  var settings = {};

  // Module global flags.
  var flags = {
    sdk: false,
    ready: false
  };

  // Module global loadDeferred
  var loadDeferred;

  /**
   * Facebook module
   */
  angular.module('facebook', []).

    // Declare module settings value
    value('settings', settings).

    // Declare module flags value
    value('flags', flags).

    /**
     * Facebook provider
     */
    provider('Facebook', [
      function() {

        /**
         * Facebook appId
         * @type {Number}
         */
        settings.appId = null;

        this.setAppId = function(appId) {
          settings.appId = appId;
        };

        this.getAppId = function() {
          return settings.appId;
        };

        /**
         * Locale language, english by default
         * @type {String}
         */
        settings.locale = 'en_US';

        this.setLocale = function(locale) {
          settings.locale = locale;
        };

        this.getLocale = function() {
          return settings.locale;
        };

        /**
         * Set if you want to check the authentication status
         * at the start up of the app
         * @type {Boolean}
         */
        settings.status = true;

        this.setStatus = function(status) {
          settings.status = status;
        };

        this.getStatus = function() {
          return settings.status;
        };

        /**
         * Adding a Channel File improves the performance of the javascript SDK,
         * by addressing issues with cross-domain communication in certain browsers.
         * @type {String}
         */
        settings.channelUrl = null;

        this.setChannel = function(channel) {
          settings.channelUrl = channel;
        };

        this.getChannel = function() {
          return settings.channelUrl;
        };

        /**
         * Enable cookies to allow the server to access the session
         * @type {Boolean}
         */
        settings.cookie = true;

        this.setCookie = function(cookie) {
          settings.cookie = cookie;
        };

        this.getCookie = function() {
          return settings.cookie;
        };

        /**
         * Parse XFBML
         * @type {Boolean}
         */
        settings.xfbml = true;

        this.setXfbml = function(enable) {
          settings.xfbml = enable;
        };

        this.getXfbml = function() {
          return settings.xfbml;
        };

        /**
         * Auth Response
         * @type {Object}
         */
        settings.authResponse = true;

        this.setAuthResponse = function(obj) {
          settings.authResponse = obj || true;
        };

        this.getAuthResponse = function() {
          return settings.authResponse;
        };

        /**
         * Frictionless Requests
         * @type {Boolean}
         */
        settings.frictionlessRequests = false;

        this.setFrictionlessRequests = function(enable) {
          settings.frictionlessRequests = enable;
        };

        this.getFrictionlessRequests = function() {
          return settings.frictionlessRequests;
        };

        /**
         * HideFlashCallback
         * @type {Object}
         */
        settings.hideFlashCallback = null;

        this.setHideFlashCallback = function(obj) {
          settings.hideFlashCallback = obj || null;
        };

        this.getHideFlashCallback = function() {
          return settings.hideFlashCallback;
        };

        /**
         * Custom option setting
         * key @type {String}
         * value @type {*}
         * @return {*}
         */
        this.setInitCustomOption = function(key, value) {
          if (!angular.isString(key)) {
            return false;
          }

          settings[key] = value;
          return settings[key];
        };

        /**
         * get init option
         * @param  {String} key
         * @return {*}
         */
        this.getInitOption = function(key) {
          // If key is not String or If non existing key return null
          if (!angular.isString(key) || !settings.hasOwnProperty(key)) {
            return false;
          }

          return settings[key];
        };

        /**
         * load SDK
         */
        settings.loadSDK = true;

        this.setLoadSDK = function(a) {
          settings.loadSDK = !!a;
        };

        this.getLoadSDK = function() {
          return settings.loadSDK;
        };

        /**
         * Init Facebook API required stuff
         * This will prepare the app earlier (on settingsuration)
         * @arg {Object/String} initSettings
         * @arg {Boolean} _loadSDK (optional, true by default)
         */
        this.init = function(initSettings, _loadSDK) {
          // If string is passed, set it as appId
          if (angular.isString(initSettings)) {
            settings.appId = initSettings || settings.appId;
          }

          // If object is passed, merge it with app settings
          if (angular.isObject(initSettings)) {
            angular.extend(settings, initSettings);
          }

          // Set if Facebook SDK should be loaded automatically or not.
          if (angular.isDefined(_loadSDK)) {
            settings.loadSDK = !!_loadSDK;
          }
        };

        /**
         * This defined the Facebook service
         */
        this.$get = [
          '$q',
          '$rootScope',
          '$timeout',
          '$window',
          function($q, $rootScope, $timeout, $window) {
            /**
             * This is the NgFacebook class to be retrieved on Facebook Service request.
             */
            function NgFacebook() {
              this.appId = settings.appId;
            }

            /**
             * Ready state method
             * @return {Boolean}
             */
            NgFacebook.prototype.isReady = function() {
              return flags.ready;
            };

            /**
             * Map some asynchronous Facebook sdk methods to NgFacebook
             */
            angular.forEach([
              'login',
              'logout',
              'api',
              'ui',
              'getLoginStatus'
            ], function(name) {
              NgFacebook.prototype[name] = function() {

                var d = $q.defer(),
                    args = Array.prototype.slice.call(arguments), // Converts arguments passed into an array
                    userFn,
                    userFnIndex;

                // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
                angular.forEach(args, function(arg, index) {
                  if (angular.isFunction(arg)) {
                    userFn = arg;
                    userFnIndex = index;
                  }
                });

                // Replace user function intended to be passed to the Facebook API with a custom one
                // for being able to use promises.
                if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                  args.splice(userFnIndex, 1, function(response) {
                    $timeout(function() {
                      if (angular.isUndefined(response.error)) {
                        d.resolve(response);
                      } else {
                        d.reject(response);
                      }

                      if (angular.isFunction(userFn)) {
                        userFn(response);
                      }
                    });
                  });
                }

                $timeout(function() {
                  // Call when loadDeferred be resolved, meaning Service is ready to be used.
                  loadDeferred.promise.then(function() {
                    $window.FB[name].apply(FB, args);
                  }, function() {
                    throw 'Facebook API could not be initialized properly';
                  });
                });

                return d.promise;
              };
            });

            /**
             * Map Facebook sdk XFBML.parse() to NgFacebook.
             */
            NgFacebook.prototype.parseXFBML = function() {

              var d = $q.defer();

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(function() {
                  $window.FB.parse();
                  d.resolve();
                }, function() {
                  throw 'Facebook API could not be initialized properly';
                });
              });

              return d.promise;
            };

            /**
             * Map Facebook sdk subscribe method to NgFacebook. Renamed as subscribe
             * Thus, use it as Facebook.subscribe in the service.
             */
            NgFacebook.prototype.subscribe = function() {

              var d = $q.defer(),
                  args = Array.prototype.slice.call(arguments), // Get arguments passed into an array
                  userFn,
                  userFnIndex;

              // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
              angular.forEach(args, function(arg, index) {
                if (angular.isFunction(arg)) {
                  userFn = arg;
                  userFnIndex = index;
                }
              });

              // Replace user function intended to be passed to the Facebook API with a custom one
              // for being able to use promises.
              if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                args.splice(userFnIndex, 1, function(response) {
                  $timeout(function() {
                    if (angular.isUndefined(response.error)) {
                      d.resolve(response);
                    } else {
                      d.reject(response);
                    }

                    if (angular.isFunction(userFn)) {
                      userFn(response);
                    }
                  });
                });
              }

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(function() {
                  $window.FB.Event.subscribe.apply(FB, args);
                }, function() {
                  throw 'Facebook API could not be initialized properly';
                });
              });

              return d.promise;
            };

            /**
             * Map Facebook sdk unsubscribe method to NgFacebook. Renamed as unsubscribe
             * Thus, use it as Facebook.unsubscribe in the service.
             */
            NgFacebook.prototype.unsubscribe = function() {

              var d = $q.defer(),
                  args = Array.prototype.slice.call(arguments), // Get arguments passed into an array
                  userFn,
                  userFnIndex;

              // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
              angular.forEach(args, function(arg, index) {
                if (angular.isFunction(arg)) {
                  userFn = arg;
                  userFnIndex = index;
                }
              });

              // Replace user function intended to be passed to the Facebook API with a custom one
              // for being able to use promises.
              if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                args.splice(userFnIndex, 1, function(response) {
                  $timeout(function() {
                    if (angular.isUndefined(response.error)) {
                      d.resolve(response);
                    } else {
                      d.reject(response);
                    }

                    if (angular.isFunction(userFn)) {
                      userFn(response);
                    }
                  });
                });
              }

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(
                  function() {
                    $window.FB.Event.unsubscribe.apply(FB, args);
                  },
                  function() {
                    throw 'Facebook API could not be initialized properly';
                  }
                );
              });

              return d.promise;
            };

            return new NgFacebook(); // Singleton
          }
        ];

      }
    ]).

    /**
     * Module initialization
     */
    run([
      '$rootScope',
      '$q',
      '$window',
      '$timeout',
      function($rootScope, $q, $window, $timeout) {
        // Define global loadDeffered to notify when Service callbacks are safe to use
        loadDeferred = $q.defer();

        var loadSDK = settings.loadSDK;
        delete(settings['loadSDK']); // Remove loadSDK from settings since this isn't part from Facebook API.

        /**
         * Define fbAsyncInit required by Facebook API
         */
        $window.fbAsyncInit = function() {
          // Initialize our Facebook app
          $timeout(function() {
            if (!settings.appId) {
              throw 'Missing appId setting.';
            }

            FB.init(settings);

            // Set ready global flag
            flags.ready = true;

            /**
             * Subscribe to Facebook API events and broadcast through app.
             */
            angular.forEach({
              'auth.login': 'login',
              'auth.logout': 'logout',
              'auth.prompt': 'prompt',
              'auth.sessionChange': 'sessionChange',
              'auth.statusChange': 'statusChange',
              'auth.authResponseChange': 'authResponseChange',
              'xfbml.render': 'xfbmlRender',
              'edge.create': 'like',
              'edge.remove': 'unlike',
              'comment.create': 'comment',
              'comment.remove': 'uncomment'
            }, function(mapped, name) {
              FB.Event.subscribe(name, function(response) {
                $timeout(function() {
                  $rootScope.$broadcast('Facebook:' + mapped, response);
                });
              });
            });

            // Broadcast Facebook:load event
            $rootScope.$broadcast('Facebook:load');

            loadDeferred.resolve(FB);

          });
        };

        /**
         * Inject Facebook root element in DOM
         */
        (function addFBRoot() {
          var fbroot = document.getElementById('fb-root');

          if (!fbroot) {
            fbroot = document.createElement('div');
            fbroot.id = 'fb-root';
            document.body.insertBefore(fbroot, document.body.childNodes[0]);
          }

          return fbroot;
        })();

        /**
         * SDK script injecting
         */
         if(loadSDK) {
          (function injectScript() {
            var src           = '//connect.facebook.net/' + settings.locale + '/sdk.js',
                script        = document.createElement('script');
                script.id     = 'facebook-jssdk';
                script.async  = true;

            // Prefix protocol
            if ($window.location.protocol === 'file') {
              src = 'https:' + src;
            }

            script.src = src;
            script.onload = function() {
              flags.sdk = true; // Set sdk global flag
            };

            document.getElementsByTagName('head')[0].appendChild(script); // // Fix for IE < 9, and yet supported by lattest browsers
          })();
        }
      }
    ]);

})(window, angular);
<commit_msg>commenting out initial setting of authresponse<commit_after>/*
---
name: Facebook Angularjs

description: Provides an easier way to make use of Facebook API with Angularjs

license: MIT-style license

authors:
  - Ciul

requires: [angular]
provides: [facebook]

...
*/
(function(window, angular, undefined) {
  'use strict';

  // Module global settings.
  var settings = {};

  // Module global flags.
  var flags = {
    sdk: false,
    ready: false
  };

  // Module global loadDeferred
  var loadDeferred;

  /**
   * Facebook module
   */
  angular.module('facebook', []).

    // Declare module settings value
    value('settings', settings).

    // Declare module flags value
    value('flags', flags).

    /**
     * Facebook provider
     */
    provider('Facebook', [
      function() {

        /**
         * Facebook appId
         * @type {Number}
         */
        settings.appId = null;

        this.setAppId = function(appId) {
          settings.appId = appId;
        };

        this.getAppId = function() {
          return settings.appId;
        };

        /**
         * Locale language, english by default
         * @type {String}
         */
        settings.locale = 'en_US';

        this.setLocale = function(locale) {
          settings.locale = locale;
        };

        this.getLocale = function() {
          return settings.locale;
        };

        /**
         * Set if you want to check the authentication status
         * at the start up of the app
         * @type {Boolean}
         */
        settings.status = true;

        this.setStatus = function(status) {
          settings.status = status;
        };

        this.getStatus = function() {
          return settings.status;
        };

        /**
         * Adding a Channel File improves the performance of the javascript SDK,
         * by addressing issues with cross-domain communication in certain browsers.
         * @type {String}
         */
        settings.channelUrl = null;

        this.setChannel = function(channel) {
          settings.channelUrl = channel;
        };

        this.getChannel = function() {
          return settings.channelUrl;
        };

        /**
         * Enable cookies to allow the server to access the session
         * @type {Boolean}
         */
        settings.cookie = true;

        this.setCookie = function(cookie) {
          settings.cookie = cookie;
        };

        this.getCookie = function() {
          return settings.cookie;
        };

        /**
         * Parse XFBML
         * @type {Boolean}
         */
        settings.xfbml = true;

        this.setXfbml = function(enable) {
          settings.xfbml = enable;
        };

        this.getXfbml = function() {
          return settings.xfbml;
        };

        /**
         * Auth Response
         * @type {Object}
         */
        //settings.authResponse = true;

        this.setAuthResponse = function(obj) {
          settings.authResponse = obj || true;
        };

        this.getAuthResponse = function() {
          return settings.authResponse;
        };

        /**
         * Frictionless Requests
         * @type {Boolean}
         */
        settings.frictionlessRequests = false;

        this.setFrictionlessRequests = function(enable) {
          settings.frictionlessRequests = enable;
        };

        this.getFrictionlessRequests = function() {
          return settings.frictionlessRequests;
        };

        /**
         * HideFlashCallback
         * @type {Object}
         */
        settings.hideFlashCallback = null;

        this.setHideFlashCallback = function(obj) {
          settings.hideFlashCallback = obj || null;
        };

        this.getHideFlashCallback = function() {
          return settings.hideFlashCallback;
        };

        /**
         * Custom option setting
         * key @type {String}
         * value @type {*}
         * @return {*}
         */
        this.setInitCustomOption = function(key, value) {
          if (!angular.isString(key)) {
            return false;
          }

          settings[key] = value;
          return settings[key];
        };

        /**
         * get init option
         * @param  {String} key
         * @return {*}
         */
        this.getInitOption = function(key) {
          // If key is not String or If non existing key return null
          if (!angular.isString(key) || !settings.hasOwnProperty(key)) {
            return false;
          }

          return settings[key];
        };

        /**
         * load SDK
         */
        settings.loadSDK = true;

        this.setLoadSDK = function(a) {
          settings.loadSDK = !!a;
        };

        this.getLoadSDK = function() {
          return settings.loadSDK;
        };

        /**
         * Init Facebook API required stuff
         * This will prepare the app earlier (on settingsuration)
         * @arg {Object/String} initSettings
         * @arg {Boolean} _loadSDK (optional, true by default)
         */
        this.init = function(initSettings, _loadSDK) {
          // If string is passed, set it as appId
          if (angular.isString(initSettings)) {
            settings.appId = initSettings || settings.appId;
          }

          // If object is passed, merge it with app settings
          if (angular.isObject(initSettings)) {
            angular.extend(settings, initSettings);
          }

          // Set if Facebook SDK should be loaded automatically or not.
          if (angular.isDefined(_loadSDK)) {
            settings.loadSDK = !!_loadSDK;
          }
        };

        /**
         * This defined the Facebook service
         */
        this.$get = [
          '$q',
          '$rootScope',
          '$timeout',
          '$window',
          function($q, $rootScope, $timeout, $window) {
            /**
             * This is the NgFacebook class to be retrieved on Facebook Service request.
             */
            function NgFacebook() {
              this.appId = settings.appId;
            }

            /**
             * Ready state method
             * @return {Boolean}
             */
            NgFacebook.prototype.isReady = function() {
              return flags.ready;
            };

            /**
             * Map some asynchronous Facebook sdk methods to NgFacebook
             */
            angular.forEach([
              'login',
              'logout',
              'api',
              'ui',
              'getLoginStatus'
            ], function(name) {
              NgFacebook.prototype[name] = function() {

                var d = $q.defer(),
                    args = Array.prototype.slice.call(arguments), // Converts arguments passed into an array
                    userFn,
                    userFnIndex;

                // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
                angular.forEach(args, function(arg, index) {
                  if (angular.isFunction(arg)) {
                    userFn = arg;
                    userFnIndex = index;
                  }
                });

                // Replace user function intended to be passed to the Facebook API with a custom one
                // for being able to use promises.
                if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                  args.splice(userFnIndex, 1, function(response) {
                    $timeout(function() {
                      if (angular.isUndefined(response.error)) {
                        d.resolve(response);
                      } else {
                        d.reject(response);
                      }

                      if (angular.isFunction(userFn)) {
                        userFn(response);
                      }
                    });
                  });
                }

                $timeout(function() {
                  // Call when loadDeferred be resolved, meaning Service is ready to be used.
                  loadDeferred.promise.then(function() {
                    $window.FB[name].apply(FB, args);
                  }, function() {
                    throw 'Facebook API could not be initialized properly';
                  });
                });

                return d.promise;
              };
            });

            /**
             * Map Facebook sdk XFBML.parse() to NgFacebook.
             */
            NgFacebook.prototype.parseXFBML = function() {

              var d = $q.defer();

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(function() {
                  $window.FB.parse();
                  d.resolve();
                }, function() {
                  throw 'Facebook API could not be initialized properly';
                });
              });

              return d.promise;
            };

            /**
             * Map Facebook sdk subscribe method to NgFacebook. Renamed as subscribe
             * Thus, use it as Facebook.subscribe in the service.
             */
            NgFacebook.prototype.subscribe = function() {

              var d = $q.defer(),
                  args = Array.prototype.slice.call(arguments), // Get arguments passed into an array
                  userFn,
                  userFnIndex;

              // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
              angular.forEach(args, function(arg, index) {
                if (angular.isFunction(arg)) {
                  userFn = arg;
                  userFnIndex = index;
                }
              });

              // Replace user function intended to be passed to the Facebook API with a custom one
              // for being able to use promises.
              if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                args.splice(userFnIndex, 1, function(response) {
                  $timeout(function() {
                    if (angular.isUndefined(response.error)) {
                      d.resolve(response);
                    } else {
                      d.reject(response);
                    }

                    if (angular.isFunction(userFn)) {
                      userFn(response);
                    }
                  });
                });
              }

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(function() {
                  $window.FB.Event.subscribe.apply(FB, args);
                }, function() {
                  throw 'Facebook API could not be initialized properly';
                });
              });

              return d.promise;
            };

            /**
             * Map Facebook sdk unsubscribe method to NgFacebook. Renamed as unsubscribe
             * Thus, use it as Facebook.unsubscribe in the service.
             */
            NgFacebook.prototype.unsubscribe = function() {

              var d = $q.defer(),
                  args = Array.prototype.slice.call(arguments), // Get arguments passed into an array
                  userFn,
                  userFnIndex;

              // Get user function and it's index in the arguments array, to replace it with custom function, allowing the usage of promises
              angular.forEach(args, function(arg, index) {
                if (angular.isFunction(arg)) {
                  userFn = arg;
                  userFnIndex = index;
                }
              });

              // Replace user function intended to be passed to the Facebook API with a custom one
              // for being able to use promises.
              if (angular.isFunction(userFn) && angular.isNumber(userFnIndex)) {
                args.splice(userFnIndex, 1, function(response) {
                  $timeout(function() {
                    if (angular.isUndefined(response.error)) {
                      d.resolve(response);
                    } else {
                      d.reject(response);
                    }

                    if (angular.isFunction(userFn)) {
                      userFn(response);
                    }
                  });
                });
              }

              $timeout(function() {
                // Call when loadDeferred be resolved, meaning Service is ready to be used
                loadDeferred.promise.then(
                  function() {
                    $window.FB.Event.unsubscribe.apply(FB, args);
                  },
                  function() {
                    throw 'Facebook API could not be initialized properly';
                  }
                );
              });

              return d.promise;
            };

            return new NgFacebook(); // Singleton
          }
        ];

      }
    ]).

    /**
     * Module initialization
     */
    run([
      '$rootScope',
      '$q',
      '$window',
      '$timeout',
      function($rootScope, $q, $window, $timeout) {
        // Define global loadDeffered to notify when Service callbacks are safe to use
        loadDeferred = $q.defer();

        var loadSDK = settings.loadSDK;
        delete(settings['loadSDK']); // Remove loadSDK from settings since this isn't part from Facebook API.

        /**
         * Define fbAsyncInit required by Facebook API
         */
        $window.fbAsyncInit = function() {
          // Initialize our Facebook app
          $timeout(function() {
            if (!settings.appId) {
              throw 'Missing appId setting.';
            }

            FB.init(settings);

            // Set ready global flag
            flags.ready = true;

            /**
             * Subscribe to Facebook API events and broadcast through app.
             */
            angular.forEach({
              'auth.login': 'login',
              'auth.logout': 'logout',
              'auth.prompt': 'prompt',
              'auth.sessionChange': 'sessionChange',
              'auth.statusChange': 'statusChange',
              'auth.authResponseChange': 'authResponseChange',
              'xfbml.render': 'xfbmlRender',
              'edge.create': 'like',
              'edge.remove': 'unlike',
              'comment.create': 'comment',
              'comment.remove': 'uncomment'
            }, function(mapped, name) {
              FB.Event.subscribe(name, function(response) {
                $timeout(function() {
                  $rootScope.$broadcast('Facebook:' + mapped, response);
                });
              });
            });

            // Broadcast Facebook:load event
            $rootScope.$broadcast('Facebook:load');

            loadDeferred.resolve(FB);

          });
        };

        /**
         * Inject Facebook root element in DOM
         */
        (function addFBRoot() {
          var fbroot = document.getElementById('fb-root');

          if (!fbroot) {
            fbroot = document.createElement('div');
            fbroot.id = 'fb-root';
            document.body.insertBefore(fbroot, document.body.childNodes[0]);
          }

          return fbroot;
        })();

        /**
         * SDK script injecting
         */
         if(loadSDK) {
          (function injectScript() {
            var src           = '//connect.facebook.net/' + settings.locale + '/sdk.js',
                script        = document.createElement('script');
                script.id     = 'facebook-jssdk';
                script.async  = true;

            // Prefix protocol
            if ($window.location.protocol === 'file') {
              src = 'https:' + src;
            }

            script.src = src;
            script.onload = function() {
              flags.sdk = true; // Set sdk global flag
            };

            document.getElementsByTagName('head')[0].appendChild(script); // // Fix for IE < 9, and yet supported by lattest browsers
          })();
        }
      }
    ]);

})(window, angular);
<|endoftext|>


<commit_before>/**
 * Imports
 */

import createAction from '@f/create-action'
import palette from 'utils/palette'
import filter from '@f/filter-obj'
import range from '@f/range'
import map from '@f/map-obj'

/**
 * Images
 */

const animals = [
  'teacherBot',
  'crocodile',
  'chameleon',
  'penguin',
  'toucan',
  'panda'
]

const images = animals.reduce((acc, animal) => {
  acc[animal] = `/animalImages/${animal}.png`
  return acc
}, {})

const gameImages = animals.reduce((acc, animal) => {
  acc[animal] = `/animalImages/${animal}top.png`
  return acc
}, {})

/**
 * Actions
 */

const getCurrentColor = createAction('getCurrentColor', (...args) => args)
const animalPaint = createAction('animalPaint', (...args) => args)
const animalMove = createAction('animalMove', (...args) => args)
const animalTurn = createAction('animalTurn', (...args) => args)
const toggle = createAction('toggle', (...args) => args)
const paintT = createAction('paintT', (...args) => args)
const paintL = createAction('paintL', (...args) => args)
const paintJ = createAction('paintJ', (...args) => args)
const paintS = createAction('paintS', (...args) => args)
const paintZ = createAction('paintZ', (...args) => args)
const paintO = createAction('paintO', (...args) => args)
const paintI = createAction('paintI', (...args) => args)
const setLine = createAction('setActiveLine', (...args) => args)
const rand = createAction('createRand', (...args) => args)

function * checkColor (lineNum, color) {
  const a = yield getCurrentColor(lineNum)
  return a === color
}

function * ifColor (lineNum, color, fn) {
  if (yield checkColor(lineNum, color)) {
    yield * fn()
  }
}

function * repeatFn (lineNum, max, fn) {
  for (let i = 0; i < max; i++) {
    yield setLine(lineNum)
    yield * fn(i)
  }
}

function * callFn (lineNum, fn, ...args) {
  yield setLine(lineNum)
  yield * fn(...args)
}

/**
 * Capabilities
 *
 * up/right/down/left - parameterless
 * up/right/down/left - with `steps` argument
 * paint - parameterless (black/white)
 * paint - with `color` argument
 * for loops
 * while loops
 * color conditionals
 * ??? conditionals
 * chess movements
 * toggle bot
 */

/**
 * Arg types
 *
 * number
 * string
 * variable
 */

const capabilities = {
  faceNorth: {
    type: 'move',
    description: 'Turn the pixelbot to face north.'
  },
  up: {
    type: 'move',
    description: 'Move the pixelbot up <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  left: {
    type: 'move',
    description: 'Move the pixelbot left <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  right: {
    type: 'move',
    description: 'Move the pixelbot right <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  down: {
    type: 'move',
    description: 'Move the pixelbot down <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  paint: {
    type: 'paint',
    description:
      'Paint the square the pixelbot is currently on <%= args[0] %>.',
    defaultArgs: ['black'],
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint.'
      }
    ]
  },
  toggle: {
    type: 'paint',
    description: 'Toggle between blue and yellow.'
  },
  paintT: {
    type: 'paint',
    description: 'Paint a T tetris shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintL: {
    type: 'paint',
    description: 'Paint an L tetris shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintO: {
    type: 'paint',
    description: 'Paint an O shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintI: {
    type: 'paint',
    description: 'Paint an I shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintS: {
    type: 'paint',
    description: 'Paint an S shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintZ: {
    type: 'paint',
    description: 'Paint an Z shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintJ: {
    type: 'paint',
    description: 'Paint an J shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  lineBreak: {
    type: 'utility',
    usage: '\n',
    description: 'Add a line break.'
  },
  comment: {
    type: 'utility',
    usage: '// comment',
    description: 'Add a comment.'
  },
  repeat: {
    type: 'control',
    description: 'Repeat the actions inside of the loop `num` times.',
    block: true,
    args: [
      {
        name: 'num',
        type: 'number',
        default: 2,
        values: range(1, 10),
        description: 'The number of times to repeat the loop.'
      },
      {
        name: '() => {\n\t// code to repeat\n}',
        type: 'function',
        values: null,
        description: 'The function to be repeated'
      }
    ]
  },
  block_end: {
    type: null,
    description: 'end of a repeat block',
    hidden: true,
    unselectable: true
  },
  ifColor: {
    type: 'control',
    description: 'conditional block',
    block: true,
    args: [
      {
        name: 'color',
        type: 'string',
        default: 'white',
        values: palette,
        description: 'The color to match'
      },
      {
        name: '() => {\n\t// code to execute\n\t// if the color matches\n}',
        type: 'function',
        default: null,
        values: null,
        description: 'The function to conditionally execute'
      }
    ]
  },
  turnRight: {
    type: 'move',
    description: 'Turn the pixelbot 90 degrees to the right.',
    args: [
      {
        name: 'turns',
        type: 'number',
        default: 1,
        values: range(1, 4),
        description: 'The number of turns right for the pixelbot to make.'
      }
    ]
  },
  turnLeft: {
    type: 'move',
    description: 'Turn the pixelbot 90 degrees to the left.',
    args: [
      {
        name: 'turns',
        type: 'number',
        default: 1,
        values: range(1, 4),
        description: 'The number of turns left for the pixelbot to make.'
      }
    ]
  },
  forward: {
    type: 'move',
    description:
      'Move the pixelbot <%= args[0] %> space in the direction it is facing.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps forward to move the pixelbot.'
      }
    ]
  },
  moveTo: {
    type: 'move',
    description: 'Move the pixelbot to a specific coordinate (x, y).',
    defaultArgs: [0, 0],
    args: [
      {
        name: 'x',
        type: 'number',
        default: 0,
        values: range(0, 19),
        description: 'The x coordinate to move to.'
      },
      {
        name: 'y',
        type: 'number',
        default: 0,
        values: range(0, 19),
        description: 'The y coordinate to move to.'
      }
    ]
  },
  rand: {
    type: null,
    description: 'Generate a random number',
    args: [
      {
        name: 'min',
        type: 'number',
        default: 0,
        description: 'The minimum for the random number'
      },
      {
        name: 'max',
        type: 'number'
      }
    ]
  },
  createFunction: {
    type: 'functions',
    block: true,
    description: 'Create a new function'
  },
  userFn: {
    hidden: true,
    block: true,
    type: 'functions'
  },
  callFn: {
    hidden: true
  },
  setLine: {
    hidden: true
  }
}

const hasAction = {
  repeat: repeatFn,
  ifColor: ifColor,
  callFn: callFn
}

const teacherBot = Object.keys(capabilities).reduce((acc, key) => {
  acc[key] = capabilities[key].args ? [true] : true
  return acc
}, {})

const typeColors = {
  move: 'green',
  control: '#e056b6', // '#bd1317',
  paint: 'blue',
  functions: '#a563d8',
  userFn: '#a563d8'
}

const arrowColors = {
  up: 'yellow',
  left: '#5cd8f7',
  down: '#a725ad',
  right: '#ca2a2d'
}

const typeOrder = [
  'move',
  'paint',
  'control',
  'comment' /* XXX deprecated only in here for old challenges */,
  'utility',
  'functions'
]

const capabilityOrder = [
  'up',
  'left',
  'right',
  'down',
  'forward',
  'moveTo',
  'turnRight',
  'turnLeft',
  'faceNorth',
  'paint',
  'toggle',
  'paintO',
  'paintI',
  'paintS',
  'paintZ',
  'paintL',
  'paintJ',
  'paintT',
  'repeat',
  'block_end',
  'ifColor',
  'rand',
  'comment',
  'lineBreak'
]

/**
 * Exports
 */

export default function (spec, id, palette) {
  return map(createActionFromSpec(id), createDocs(spec, palette))
}

function createArgumentsFromSpec (id, spec, name) {
  return (lineNum, ...args) => [
    lineNum,
    id,
    ...spec.args.map((aspec, i) => {
      if (
        aspec.name === 'color' &&
        aspec.values &&
        aspec.values.filter(arg => arg === args[i] || arg.name === args[i])
          .length === 0
      ) {
        return aspec.default
      }

      return args[i]
    })
  ]
}

function createActionFromSpec (id) {
  return (spec, key) => {
    if (hasAction[key]) {
      return hasAction[key]
    } else {
      return createAction(key, createArgumentsFromSpec(id, spec, key))
    }
  }
}

export function createDocs (traits, palette) {
  return map(
    (val, key) =>
      traits[key] === true || key === 'comment' || key === 'lineBreak'
        ? { ...val, args: [] }
        : {
          ...val,
          args: (val.args || []).map((arg, i) => ({
            ...arg,
            values:
                (traits[key][i] === true
                  ? arg.name === 'color' ? palette : arg.values || true
                  : traits[key][i]) || null
          }))
        },
    {
      ...filter((val, key) => traits[key], capabilities),
      comment: capabilities.comment,
      lineBreak: capabilities.lineBreak,
      setLine: capabilities.setLine,
      callFn: capabilities.callFn
    }
  )
}

export {
  typeOrder,
  typeColors,
  arrowColors,
  capabilityOrder,
  capabilities,
  gameImages,
  teacherBot,
  images
}
<commit_msg>control type colors<commit_after>/**
 * Imports
 */

import createAction from '@f/create-action'
import palette from 'utils/palette'
import filter from '@f/filter-obj'
import range from '@f/range'
import map from '@f/map-obj'

/**
 * Images
 */

const animals = [
  'teacherBot',
  'crocodile',
  'chameleon',
  'penguin',
  'toucan',
  'panda'
]

const images = animals.reduce((acc, animal) => {
  acc[animal] = `/animalImages/${animal}.png`
  return acc
}, {})

const gameImages = animals.reduce((acc, animal) => {
  acc[animal] = `/animalImages/${animal}top.png`
  return acc
}, {})

/**
 * Actions
 */

const getCurrentColor = createAction('getCurrentColor', (...args) => args)
const animalPaint = createAction('animalPaint', (...args) => args)
const animalMove = createAction('animalMove', (...args) => args)
const animalTurn = createAction('animalTurn', (...args) => args)
const toggle = createAction('toggle', (...args) => args)
const paintT = createAction('paintT', (...args) => args)
const paintL = createAction('paintL', (...args) => args)
const paintJ = createAction('paintJ', (...args) => args)
const paintS = createAction('paintS', (...args) => args)
const paintZ = createAction('paintZ', (...args) => args)
const paintO = createAction('paintO', (...args) => args)
const paintI = createAction('paintI', (...args) => args)
const setLine = createAction('setActiveLine', (...args) => args)
const rand = createAction('createRand', (...args) => args)

function * checkColor (lineNum, color) {
  const a = yield getCurrentColor(lineNum)
  return a === color
}

function * ifColor (lineNum, color, fn) {
  if (yield checkColor(lineNum, color)) {
    yield * fn()
  }
}

function * repeatFn (lineNum, max, fn) {
  for (let i = 0; i < max; i++) {
    yield setLine(lineNum)
    yield * fn(i)
  }
}

function * callFn (lineNum, fn, ...args) {
  yield setLine(lineNum)
  yield * fn(...args)
}

/**
 * Capabilities
 *
 * up/right/down/left - parameterless
 * up/right/down/left - with `steps` argument
 * paint - parameterless (black/white)
 * paint - with `color` argument
 * for loops
 * while loops
 * color conditionals
 * ??? conditionals
 * chess movements
 * toggle bot
 */

/**
 * Arg types
 *
 * number
 * string
 * variable
 */

const capabilities = {
  faceNorth: {
    type: 'move',
    description: 'Turn the pixelbot to face north.'
  },
  up: {
    type: 'move',
    description: 'Move the pixelbot up <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  left: {
    type: 'move',
    description: 'Move the pixelbot left <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  right: {
    type: 'move',
    description: 'Move the pixelbot right <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  down: {
    type: 'move',
    description: 'Move the pixelbot down <%= args[0] %> space.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps right to move the pixelbot.'
      }
    ]
  },
  paint: {
    type: 'paint',
    description:
      'Paint the square the pixelbot is currently on <%= args[0] %>.',
    defaultArgs: ['black'],
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint.'
      }
    ]
  },
  toggle: {
    type: 'paint',
    description: 'Toggle between blue and yellow.'
  },
  paintT: {
    type: 'paint',
    description: 'Paint a T tetris shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintL: {
    type: 'paint',
    description: 'Paint an L tetris shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintO: {
    type: 'paint',
    description: 'Paint an O shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintI: {
    type: 'paint',
    description: 'Paint an I shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintS: {
    type: 'paint',
    description: 'Paint an S shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintZ: {
    type: 'paint',
    description: 'Paint an Z shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  paintJ: {
    type: 'paint',
    description: 'Paint an J shape.',
    args: [
      {
        name: 'color',
        type: 'string',
        default: {
          type: 'string',
          value: 'black'
        },
        values: palette,
        description: 'The color to paint'
      }
    ]
  },
  lineBreak: {
    type: 'utility',
    usage: '\n',
    description: 'Add a line break.'
  },
  comment: {
    type: 'utility',
    usage: '// comment',
    description: 'Add a comment.'
  },
  repeat: {
    type: 'control',
    description: 'Repeat the actions inside of the loop `num` times.',
    block: true,
    args: [
      {
        name: 'num',
        type: 'number',
        default: 2,
        values: range(1, 10),
        description: 'The number of times to repeat the loop.'
      },
      {
        name: '() => {\n\t// code to repeat\n}',
        type: 'function',
        values: null,
        description: 'The function to be repeated'
      }
    ]
  },
  block_end: {
    type: null,
    description: 'end of a repeat block',
    hidden: true,
    unselectable: true
  },
  ifColor: {
    type: 'control',
    description: 'conditional block',
    block: true,
    args: [
      {
        name: 'color',
        type: 'string',
        default: 'white',
        values: palette,
        description: 'The color to match'
      },
      {
        name: '() => {\n\t// code to execute\n\t// if the color matches\n}',
        type: 'function',
        default: null,
        values: null,
        description: 'The function to conditionally execute'
      }
    ]
  },
  turnRight: {
    type: 'move',
    description: 'Turn the pixelbot 90 degrees to the right.',
    args: [
      {
        name: 'turns',
        type: 'number',
        default: 1,
        values: range(1, 4),
        description: 'The number of turns right for the pixelbot to make.'
      }
    ]
  },
  turnLeft: {
    type: 'move',
    description: 'Turn the pixelbot 90 degrees to the left.',
    args: [
      {
        name: 'turns',
        type: 'number',
        default: 1,
        values: range(1, 4),
        description: 'The number of turns left for the pixelbot to make.'
      }
    ]
  },
  forward: {
    type: 'move',
    description:
      'Move the pixelbot <%= args[0] %> space in the direction it is facing.',
    defaultArgs: [1],
    args: [
      {
        name: 'steps',
        type: 'number',
        default: 1,
        values: range(1, 10),
        description: 'The number of steps forward to move the pixelbot.'
      }
    ]
  },
  moveTo: {
    type: 'move',
    description: 'Move the pixelbot to a specific coordinate (x, y).',
    defaultArgs: [0, 0],
    args: [
      {
        name: 'x',
        type: 'number',
        default: 0,
        values: range(0, 19),
        description: 'The x coordinate to move to.'
      },
      {
        name: 'y',
        type: 'number',
        default: 0,
        values: range(0, 19),
        description: 'The y coordinate to move to.'
      }
    ]
  },
  rand: {
    type: null,
    description: 'Generate a random number',
    args: [
      {
        name: 'min',
        type: 'number',
        default: 0,
        description: 'The minimum for the random number'
      },
      {
        name: 'max',
        type: 'number'
      }
    ]
  },
  createFunction: {
    type: 'functions',
    block: true,
    description: 'Create a new function'
  },
  userFn: {
    hidden: true,
    block: true,
    type: 'functions'
  },
  callFn: {
    hidden: true
  },
  setLine: {
    hidden: true
  }
}

const hasAction = {
  repeat: repeatFn,
  ifColor: ifColor,
  callFn: callFn
}

const teacherBot = Object.keys(capabilities).reduce((acc, key) => {
  acc[key] = capabilities[key].args ? [true] : true
  return acc
}, {})

const typeColors = {
  move: 'green',
  control: '#e056b6', // '#bd1317', '#f6756c'
  paint: 'blue',
  functions: '#a563d8',
  userFn: '#a563d8'
}

const arrowColors = {
  up: 'yellow',
  left: '#5cd8f7',
  down: '#a725ad',
  right: '#ca2a2d'
}

const typeOrder = [
  'move',
  'paint',
  'control',
  'comment' /* XXX deprecated only in here for old challenges */,
  'utility',
  'functions'
]

const capabilityOrder = [
  'up',
  'left',
  'right',
  'down',
  'forward',
  'moveTo',
  'turnRight',
  'turnLeft',
  'faceNorth',
  'paint',
  'toggle',
  'paintO',
  'paintI',
  'paintS',
  'paintZ',
  'paintL',
  'paintJ',
  'paintT',
  'repeat',
  'block_end',
  'ifColor',
  'rand',
  'comment',
  'lineBreak'
]

/**
 * Exports
 */

export default function (spec, id, palette) {
  return map(createActionFromSpec(id), createDocs(spec, palette))
}

function createArgumentsFromSpec (id, spec, name) {
  return (lineNum, ...args) => [
    lineNum,
    id,
    ...spec.args.map((aspec, i) => {
      if (
        aspec.name === 'color' &&
        aspec.values &&
        aspec.values.filter(arg => arg === args[i] || arg.name === args[i])
          .length === 0
      ) {
        return aspec.default
      }

      return args[i]
    })
  ]
}

function createActionFromSpec (id) {
  return (spec, key) => {
    if (hasAction[key]) {
      return hasAction[key]
    } else {
      return createAction(key, createArgumentsFromSpec(id, spec, key))
    }
  }
}

export function createDocs (traits, palette) {
  return map(
    (val, key) =>
      traits[key] === true || key === 'comment' || key === 'lineBreak'
        ? { ...val, args: [] }
        : {
          ...val,
          args: (val.args || []).map((arg, i) => ({
            ...arg,
            values:
                (traits[key][i] === true
                  ? arg.name === 'color' ? palette : arg.values || true
                  : traits[key][i]) || null
          }))
        },
    {
      ...filter((val, key) => traits[key], capabilities),
      comment: capabilities.comment,
      lineBreak: capabilities.lineBreak,
      setLine: capabilities.setLine,
      callFn: capabilities.callFn
    }
  )
}

export {
  typeOrder,
  typeColors,
  arrowColors,
  capabilityOrder,
  capabilities,
  gameImages,
  teacherBot,
  images
}
<|endoftext|>


<commit_before>!function(Foundation, $) {
  'use strict';

  /**
   * Creates a new instance of Equalizer.
   * @class
   * @fires Equalizer#init
   * @param {Object} element - jQuery object to add the trigger to.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  function Equalizer(element, options) {
    this.$element = element;
    this.options  = $.extend(Equalizer.defaults, options);
    this.$window  = $(window);
    this.name     = 'equalizer';
    this.attr     = 'data-equalizer';

    this._init();
    this._events();

    /**
     * Fires when the plugin has been successfuly initialized.
     * @event Equalizer#init
     */
    this.$element.trigger('init.zf.equalizer');
  }

  /**
   * Default settings for plugin
   */
  Equalizer.prototype.defaults = {
    equalizeOnStack: true
  };

  /**
   * Initializes the Equalizer plugin and calls functions to get equalizer functioning on load.
   * @private
   */
  Equalizer.prototype._init = function() {
    this._reflow();
  };

  /**
   * Initializes events for Equalizer.
   * @private
   */
  Equalizer.prototype._events = function() {
    var self = this;

    this.$window
      .off('.equalizer')
      .on('resize.fndtn.equalizer', Foundation.throttle(function () {
        self._reflow();
      }.bind(this), 50));
  };
  /**
   * Calls necessary functions to update Equalizer upon DOM change
   * @private
   */
  Equalizer.prototype._reflow = function() {
    var self = this;

    $('[' + this.attr + ']').each(function() {
      var $eqParent       = $(this),
          adjustedHeights = [];
      console.log($eqParent.data('equalizer'));
      if ($eqParent.find('img').length) {
        var imgLoad = imagesLoaded($eqParent.find('img'));
        imgLoad.on('always', function() {
          adjustedHeights = self.getHeights($eqParent);
          self.applyHeight($eqParent, adjustedHeights);
        });
      }
      else {
        adjustedHeights = self.getHeights($eqParent);
        self.applyHeight($eqParent, adjustedHeights);
      }
    });
  };
  /**
   * Finds the outer heights of children contained within an Equalizer parent and returns them in an array
   * @param {Object} $eqParent A jQuery instance of an Equalizer container
   * @returns {Array} heights An array of heights of children within Equalizer container
   */
  Equalizer.prototype.getHeights = function($eqParent) {

    var eqGroupName = $eqParent.data('equalizer'),
        eqGroup     = eqGroupName ? $eqParent.find('[' + this.attr + '-watch="' + eqGroupName + '"]:visible') : $eqParent.find('[' + this.attr + '-watch]:visible'),
        heights;

    eqGroup.height('inherit');
    heights = eqGroup.map(function () {
      // debugger;
      return $(this).outerHeight(false);
    }).get();
    return heights;
  };
  /**
   * Changes the CSS height property of each child in an Equalizer parent to match the tallest
   * @param {Object} $eqParent - A jQuery instance of an Equalizer container
   * @param {array} heights - An array of heights of children within Equalizer container
   * @fires Equalizer#preEqualized
   * @fires Equalizer#postEqualized
   */
  Equalizer.prototype.applyHeight = function($eqParent, heights) {
    var eqGroupName = $eqParent.data('equalizer'),
        eqGroup     = eqGroupName ? $eqParent.find('['+this.attr+'-watch="'+eqGroupName+'"]:visible') : $eqParent.find('['+this.attr+'-watch]:visible'),
        max         = Math.max.apply(null, heights);

    /**
     * Fires before the heights are applied
     * @event Equalizer#preEqualized
     */
    $eqParent.trigger('preEqualized.fndtn.equalizer');

    // for now, apply the max height found in the array
    for (var i = 0; i < eqGroup.length; i++) {
      $(eqGroup[i]).css('height', max);
    }

    /**
     * Fires when the heights have been applied
     * @event Equalizer#postEqualized
     */
    $eqParent.trigger('postEqualized.fndtn.equalizer');
  };

  Foundation.plugin('equalizer', Equalizer);

  // Exports for AMD/Browserify
  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
    module.exports = Equalizer;
  if (typeof define === 'function')
    define(['foundation'], function() {
      return Equalizer;
    });

}(Foundation, jQuery);<commit_msg>taking out extra console statement.<commit_after>!function(Foundation, $) {
  'use strict';

  /**
   * Creates a new instance of Equalizer.
   * @class
   * @fires Equalizer#init
   * @param {Object} element - jQuery object to add the trigger to.
   * @param {Object} options - Overrides to the default plugin settings.
   */
  function Equalizer(element, options) {
    this.$element = element;
    this.options  = $.extend(Equalizer.defaults, options);
    this.$window  = $(window);
    this.name     = 'equalizer';
    this.attr     = 'data-equalizer';

    this._init();
    this._events();

    /**
     * Fires when the plugin has been successfuly initialized.
     * @event Equalizer#init
     */
    this.$element.trigger('init.zf.equalizer');
  }

  /**
   * Default settings for plugin
   */
  Equalizer.prototype.defaults = {
    equalizeOnStack: true
  };

  /**
   * Initializes the Equalizer plugin and calls functions to get equalizer functioning on load.
   * @private
   */
  Equalizer.prototype._init = function() {
    this._reflow();
  };

  /**
   * Initializes events for Equalizer.
   * @private
   */
  Equalizer.prototype._events = function() {
    var self = this;

    this.$window
      .off('.equalizer')
      .on('resize.fndtn.equalizer', Foundation.throttle(function () {
        self._reflow();
      }.bind(this), 50));
  };

  /**
   * A noop version for the plugin
   * @private
   */
  Equalizer.prototype._killswitch = function() {
    return;
  };
  /**
   * Calls necessary functions to update Equalizer upon DOM change
   * @private
   */
  Equalizer.prototype._reflow = function() {
    var self = this;

    $('[' + this.attr + ']').each(function() {
      var $eqParent       = $(this),
          adjustedHeights = [];

      if ($eqParent.find('img').length) {
        var imgLoad = imagesLoaded($eqParent.find('img'));
        imgLoad.on('always', function() {
          adjustedHeights = self.getHeights($eqParent);
          self.applyHeight($eqParent, adjustedHeights);
        });
      }
      else {
        adjustedHeights = self.getHeights($eqParent);
        self.applyHeight($eqParent, adjustedHeights);
      }
    });
  };
  /**
   * Finds the outer heights of children contained within an Equalizer parent and returns them in an array
   * @param {Object} $eqParent A jQuery instance of an Equalizer container
   * @returns {Array} heights An array of heights of children within Equalizer container
   */
  Equalizer.prototype.getHeights = function($eqParent) {

    var eqGroupName = $eqParent.data('equalizer'),
        eqGroup     = eqGroupName ? $eqParent.find('[' + this.attr + '-watch="' + eqGroupName + '"]:visible') : $eqParent.find('[' + this.attr + '-watch]:visible'),
        heights;

    eqGroup.height('inherit');
    heights = eqGroup.map(function () { return $(this).outerHeight(false);}).get();
    return heights;
  };
  /**
   * Changes the CSS height property of each child in an Equalizer parent to match the tallest
   * @param {Object} $eqParent - A jQuery instance of an Equalizer container
   * @param {array} heights - An array of heights of children within Equalizer container
   * @fires Equalizer#preEqualized
   * @fires Equalizer#postEqualized
   */
  Equalizer.prototype.applyHeight = function($eqParent, heights) {
    var eqGroupName = $eqParent.data('equalizer'),
        eqGroup     = eqGroupName ? $eqParent.find('['+this.attr+'-watch="'+eqGroupName+'"]:visible') : $eqParent.find('['+this.attr+'-watch]:visible'),
        max         = Math.max.apply(null, heights);

    /**
     * Fires before the heights are applied
     * @event Equalizer#preEqualized
     */
    $eqParent.trigger('preEqualized.fndtn.equalizer');

    // for now, apply the max height found in the array
    for (var i = 0; i < eqGroup.length; i++) {
      $(eqGroup[i]).css('height', max);
    }

    /**
     * Fires when the heights have been applied
     * @event Equalizer#postEqualized
     */
    $eqParent.trigger('postEqualized.fndtn.equalizer');
  };

  Foundation.plugin('equalizer', Equalizer);

  // Exports for AMD/Browserify
  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
    module.exports = Equalizer;
  if (typeof define === 'function')
    define(['foundation'], function() {
      return Equalizer;
    });

}(Foundation, jQuery);<|endoftext|>


<commit_before>/*

  Gekko is a Bitcoin trading bot for popular Bitcoin exchanges written
  in node, it features multiple trading methods using technical analysis.

  Disclaimer:

  USE AT YOUR OWN RISK!

  The author of this project is NOT responsible for any damage or loss caused
  by this software. There can be bugs and the bot may not perform as expected
  or specified. Please consider testing it first with paper trading /
  backtesting on historical data. Also look at the code to see what how
  it's working.

*/

var gekkoDir = './';
var coreDir = gekkoDir + 'core/';
var pluginDir = gekkoDir + 'plugins/';

var _ = require('lodash');
var async = require('async');

var util = require(coreDir + 'util');
var log = require(coreDir + 'log');

// if the user just wants to see the current version
if(util.getArgument('v')) {
  util.logVersion();
  util.die();
}

// make sure the current node version is recent enough
if(!util.recentNode())
  util.die([
    'Your local version of nodejs is to old. ',
    'You have ',
    process.version,
    ' and you need atleast ',
    util.getRequiredNodeVersion()
  ].join(''));

var config = util.getConfig();

// Temporary checks to make sure everything we need is
// up to date and present on the system.

// temp at Fri Jan 17 16:00:19 CET 2014
if(config.normal)
  util.die('Please update your config! config.normal is now called config.watch');
if(config.EMA)
  util.die('Please update your config! EMA is now called DEMA');
// temp at Wed Jan 22 12:18:08 CET 2014
if(!config.profitSimulator.slippage)
  util.die('Please update your config! The profit simulator is missing slippage');
// temp at Sun Feb  9 17:13:45 CET 2014
if(!config.DEMA.thresholds)
  util.die('Please update your config!');
try {
  require('kraken-api');
} catch(err) {
  util.die('Kraken module cannot be found, if you are on Windows read this:\n\nhttps://github.com/askmike/gekko/issues/202');
}

if(
  config.trader.enabled &&
  !config['I understand that Gekko only automates MY OWN trading strategies']
)
  util.die('Do you understand what Gekko will do with your money? Read this first:\n\nhttps://github.com/askmike/gekko/issues/201');

// START

log.info('Gekko v' + util.getVersion(), 'started');
log.info('I\'m gonna make you rich, Bud Fox.', '\n\n');

var gekkoMode = 'realtime';

// currently we only support a single
// market and a single advisor.

// make sure the monitoring exchange is configured correctly for monitoring

var exchangeChecker = require(coreDir + 'exchangeChecker');
var invalid = exchangeChecker.cantMonitor(config.watch);
if(invalid)
  util.die(invalid);

var plugins = [];
var emitters = {};

var setupMarket = function(next) {
  var Market = require(coreDir + 'marketManager');
  emitters.market = new Market;
  next();
}

// load each plugin
var loadPlugins = function(next) {
  var pluginSettings = require(gekkoDir + 'plugins');

  var iterator = function(plugin, next) {

    // verify the actor settings in config
    if(!(plugin.slug in config)) {
      log.warn('unable to find', plugin.slug, 'in the config. Is your config up to date?')
      return next();
    }

    var pluginConfig = config[plugin.slug];

    // only load actors that are supported by
    // Gekko's current mode
    if(!_.contains(plugin.modes, gekkoMode))
      return next();

    // if the actor is disabled skip as well
    if(!pluginConfig.enabled)
      return next();

    // verify plugin dependencies are installed
    if('dependencies' in plugin)
      _.each(plugin.dependencies, function(dep) {
        try {
          require(dep.module);
        }
        catch(e) {

          var error = [
            'The plugin',
            plugin.slug,
            'expects the module',
            dep.module,
            'to be installed.',
            'However it is not, install',
            'it by running: \n\n',
            '\tnpm install',
            dep.module + '@' + dep.version
          ].join(' ');

          util.die(error);
        }

      });

    var Plugin = require(pluginDir + plugin.slug);

    if(!plugin.silent) {
      log.info('Setting up:');
      log.info('\t', plugin.name);
      log.info('\t', plugin.description);
    }

    if(plugin.async) {
      var instance = new Plugin(util.defer(next));

      instance.meta = plugin;
      plugins.push(instance);

    } else {
      var instance = new Plugin;

      instance.meta = plugin;
      plugins.push(instance);

      _.defer(next);
    }

    if(!plugin.silent)
      console.log();
  }

  async.eachSeries(
    pluginSettings,
    iterator,
    next
  );
};

// advisor is a special actor in that it spawns an
// advice feed which everyone can subscribe to.
var setupAdvisor = function(next) {

  var settings;

  var plugin = _.find(plugins, function(advisor) {
    if(!advisor.meta.originates)
      return false;

    settings = _.find(
      advisor.meta.originates,
      function(o) {
        return o.feed === 'advice feed'
      }
    );
    return settings;
  });

  emitters.advisor = settings ? plugin[settings.object] : false;

  next();
}

var attachPlugins = function(next) {

  var subscriptions = require(gekkoDir + 'subscriptions');

  _.each(plugins, function(plugin) {
    _.each(subscriptions, function(sub) {

      if(sub.handler in plugin) {

        // if the actor wants to listen
        // to something disabled
        if(!emitters[sub.emitter])
          return log.warn([
            plugin.meta.name,
            'wanted to listen to the',
            sub.emitter + ',',
            'however the',
            sub.emitter,
            'is disabled.'
          ].join(' '))

        // attach handler
        emitters[sub.emitter]
          .on(sub.event, plugin[sub.handler]);
      }

    });
  });

  next();
}

log.info('Setting up Gekko in', gekkoMode, 'mode');
console.log();

async.series(
  [
    loadPlugins,
    setupAdvisor,
    setupMarket,
    attachPlugins
  ],
  function() {
    // everything is setup!
    emitters.market.start();
  }
);<commit_msg>minimize this check to kraken users, see #203<commit_after>/*

  Gekko is a Bitcoin trading bot for popular Bitcoin exchanges written
  in node, it features multiple trading methods using technical analysis.

  Disclaimer:

  USE AT YOUR OWN RISK!

  The author of this project is NOT responsible for any damage or loss caused
  by this software. There can be bugs and the bot may not perform as expected
  or specified. Please consider testing it first with paper trading /
  backtesting on historical data. Also look at the code to see what how
  it's working.

*/

var gekkoDir = './';
var coreDir = gekkoDir + 'core/';
var pluginDir = gekkoDir + 'plugins/';

var _ = require('lodash');
var async = require('async');

var util = require(coreDir + 'util');
var log = require(coreDir + 'log');

// if the user just wants to see the current version
if(util.getArgument('v')) {
  util.logVersion();
  util.die();
}

// make sure the current node version is recent enough
if(!util.recentNode())
  util.die([
    'Your local version of nodejs is to old. ',
    'You have ',
    process.version,
    ' and you need atleast ',
    util.getRequiredNodeVersion()
  ].join(''));

var config = util.getConfig();

// Temporary checks to make sure everything we need is
// up to date and present on the system.

// temp at Fri Jan 17 16:00:19 CET 2014
if(config.normal)
  util.die('Please update your config! config.normal is now called config.watch');
if(config.EMA)
  util.die('Please update your config! EMA is now called DEMA');
// temp at Wed Jan 22 12:18:08 CET 2014
if(!config.profitSimulator.slippage)
  util.die('Please update your config! The profit simulator is missing slippage');
// temp at Sun Feb  9 17:13:45 CET 2014
if(!config.DEMA.thresholds)
  util.die('Please update your config!');

// see @link:
// https://github.com/askmike/gekko/issues/202
try {
  // only worry about this when the user wants to watch kraken
  if(config.watch.exchange.toLowerCase() === 'kraken')
    require('kraken-api');
} catch(err) {
  util.die('Kraken module cannot be found, if you are on Windows read this:\n\nhttps://github.com/askmike/gekko/issues/202');
}

if(
  config.trader.enabled &&
  !config['I understand that Gekko only automates MY OWN trading strategies']
)
  util.die('Do you understand what Gekko will do with your money? Read this first:\n\nhttps://github.com/askmike/gekko/issues/201');

// START

log.info('Gekko v' + util.getVersion(), 'started');
log.info('I\'m gonna make you rich, Bud Fox.', '\n\n');

var gekkoMode = 'realtime';

// currently we only support a single
// market and a single advisor.

// make sure the monitoring exchange is configured correctly for monitoring

var exchangeChecker = require(coreDir + 'exchangeChecker');
var invalid = exchangeChecker.cantMonitor(config.watch);
if(invalid)
  util.die(invalid);

var plugins = [];
var emitters = {};

var setupMarket = function(next) {
  var Market = require(coreDir + 'marketManager');
  emitters.market = new Market;
  next();
}

// load each plugin
var loadPlugins = function(next) {
  var pluginSettings = require(gekkoDir + 'plugins');

  var iterator = function(plugin, next) {

    // verify the actor settings in config
    if(!(plugin.slug in config)) {
      log.warn('unable to find', plugin.slug, 'in the config. Is your config up to date?')
      return next();
    }

    var pluginConfig = config[plugin.slug];

    // only load actors that are supported by
    // Gekko's current mode
    if(!_.contains(plugin.modes, gekkoMode))
      return next();

    // if the actor is disabled skip as well
    if(!pluginConfig.enabled)
      return next();

    // verify plugin dependencies are installed
    if('dependencies' in plugin)
      _.each(plugin.dependencies, function(dep) {
        try {
          require(dep.module);
        }
        catch(e) {

          var error = [
            'The plugin',
            plugin.slug,
            'expects the module',
            dep.module,
            'to be installed.',
            'However it is not, install',
            'it by running: \n\n',
            '\tnpm install',
            dep.module + '@' + dep.version
          ].join(' ');

          util.die(error);
        }

      });

    var Plugin = require(pluginDir + plugin.slug);

    if(!plugin.silent) {
      log.info('Setting up:');
      log.info('\t', plugin.name);
      log.info('\t', plugin.description);
    }

    if(plugin.async) {
      var instance = new Plugin(util.defer(next));

      instance.meta = plugin;
      plugins.push(instance);

    } else {
      var instance = new Plugin;

      instance.meta = plugin;
      plugins.push(instance);

      _.defer(next);
    }

    if(!plugin.silent)
      console.log();
  }

  async.eachSeries(
    pluginSettings,
    iterator,
    next
  );
};

// advisor is a special actor in that it spawns an
// advice feed which everyone can subscribe to.
var setupAdvisor = function(next) {

  var settings;

  var plugin = _.find(plugins, function(advisor) {
    if(!advisor.meta.originates)
      return false;

    settings = _.find(
      advisor.meta.originates,
      function(o) {
        return o.feed === 'advice feed'
      }
    );
    return settings;
  });

  emitters.advisor = settings ? plugin[settings.object] : false;

  next();
}

var attachPlugins = function(next) {

  var subscriptions = require(gekkoDir + 'subscriptions');

  _.each(plugins, function(plugin) {
    _.each(subscriptions, function(sub) {

      if(sub.handler in plugin) {

        // if the actor wants to listen
        // to something disabled
        if(!emitters[sub.emitter])
          return log.warn([
            plugin.meta.name,
            'wanted to listen to the',
            sub.emitter + ',',
            'however the',
            sub.emitter,
            'is disabled.'
          ].join(' '))

        // attach handler
        emitters[sub.emitter]
          .on(sub.event, plugin[sub.handler]);
      }

    });
  });

  next();
}

log.info('Setting up Gekko in', gekkoMode, 'mode');
console.log();

async.series(
  [
    loadPlugins,
    setupAdvisor,
    setupMarket,
    attachPlugins
  ],
  function() {
    // everything is setup!
    emitters.market.start();
  }
);<|endoftext|>


<commit_before>//     genie.js
//     (c) 2013 Kent C. Dodds
//     genie.js may be freely distributed under the MIT license.
/*
 * Vernacular:
 *  Genie: a spirit of Arabian folklore, as traditionally depicted
  *   imprisoned within a bottle or oil lamp, and capable of
   *  granting wishes when summoned.
 *  Magic Word: Keywords used to make a wish.
  *   The genie matches a given magic word to magic words for
   *  a wish to find the closeset match and executes the wishe's action
 *  Wish: An action with a set of magic words
 */

;(function(global){

  var _wishes = {},
    _previousId = 0,
    _enteredMagicWords = {};

  function _getNextId() {
    return 'q-' + _previousId++;
  }

  function registerWish(magicWords, action, id) {
    if (!Array.isArray(magicWords)) {
      magicWords = [magicWords];
    }
    if (id === undefined) {
      id = _getNextId();
    }

    var wish = {
      id: id,
      keywords: magicWords,
      action: action
    };
    _wishes[id] = wish;
    return _wishes[id];
  }

  function deregisterWish(id) {
    // Check if it may be a wish object
    if (typeof id === 'object' && id.id) {
      id = id.id;
    }
    delete _wishes[id];
    for (var word in _enteredMagicWords) {
      if (_enteredMagicWords[word].indexOf(id) != -1) {
        _enteredMagicWords[word].splice(_enteredMagicWords[word].indexOf(id), 1);
      }
    }
  }

  function clearWishes() {
    _wishes = {};
    _enteredMagicWords = {};
  }

  function getMatchingWishes(magicWord) {
    var matchingWishIds = _enteredMagicWords[magicWord] || [];
    var otherMatchingWishId = _addOtherMatchingKeywords(matchingWishIds, magicWord);
    var allWishIds = matchingWishIds.concat(otherMatchingWishId);
    var matchingWishes = [];
    for (var i = 0; i < allWishIds.length; i++) {
      matchingWishes.push(_wishes[allWishIds[i]]);
    }
    return matchingWishes;
  }

  function _addOtherMatchingKeywords(currentMatchingWishIds, givenMagicWord) {
    var otherMatchingWishIds = [];
    for (var wishId in _wishes) {
      if (currentMatchingWishIds.indexOf(wishId) == -1) {
        var wish =_wishes[wishId];
        if (_anyKeywordsMatch(wish.keywords, givenMagicWord)) {
          otherMatchingWishIds.push(wishId);
        }
      }
    }
    return otherMatchingWishIds;
  }

  function _anyKeywordsMatch(wishesMagicWords, givenMagicWord) {
    for (var i = 0; i < wishesMagicWords.length; i++) {
      if (_stringsMatch(wishesMagicWords[i], givenMagicWord)) {
        return true;
      }
    }
    return false;
  }

  function _stringsMatch(magicWord, givenMagicWord) {
    magicWord = magicWord.toLowerCase();
    givenMagicWord = givenMagicWord.toLowerCase();
    if (givenMagicWord.length > magicWord.length) {
      return false;
    }
    if (magicWord.indexOf(givenMagicWord) != -1) {
      return true;
    }
    var charNumber = 0;
    for (var i = 0; i < givenMagicWord.length; i++) {
      var matchChar = givenMagicWord[i];
      var found = false;
      for (var j = charNumber; j < magicWord.length; j++) {
        var stringChar = magicWord[j];
        if (stringChar == matchChar) {
          found = true;
          charNumber = j + 1;
          break;
        }
      }
      if (!found) {
        return false;
      }
    }
    return true;
  }

  function makeWish(id, magicWord) {
    // Check if it may be a wish object
    if (typeof id === 'object' && id.id) {
      id = id.id;
    }
    if (id === null || id === undefined) {
      var matchingWishes = getMatchingWishes(magicWord);
      if (matchingWishes.length > 0) {
        id = matchingWishes[0].id;
      }
    }
    if (_wishes[id] && _wishes[id].action) {
      _wishes[id].action();
    }

    if (magicWord) {
      // Reset entered keywords order.
      _enteredMagicWords[magicWord] = _enteredMagicWords[magicWord] || [];
      var existingIndex = _enteredMagicWords[magicWord].indexOf(id);
      if (existingIndex != -1) {
        _enteredMagicWords[magicWord].splice(existingIndex, 1);
      }
      _enteredMagicWords[magicWord].unshift(id);
    }
  }

  function setOptions(options) {
    _wishes = options.wishes || _wishes;
    _previousId = options.previousId || _previousId;
    _enteredMagicWords = options.enteredKeyWords || _enteredMagicWords;
  }

  global.genie = registerWish;
  global.genie.getMatchingWishes = getMatchingWishes;
  global.genie.makeWish = makeWish;
  global.genie.setOptions = setOptions;
  global.genie.deregisterWish = deregisterWish;
  global.genie.clearWishes = clearWishes;

})(this);
<commit_msg>Changed options api to return genie's options as well. Renamed it to make more sense.<commit_after>//     genie.js
//     (c) 2013 Kent C. Dodds
//     genie.js may be freely distributed under the MIT license.
/*
 * Vernacular:
 *  Genie: a spirit of Arabian folklore, as traditionally depicted
  *   imprisoned within a bottle or oil lamp, and capable of
   *  granting wishes when summoned.
 *  Magic Word: Keywords used to make a wish.
  *   The genie matches a given magic word to magic words for
   *  a wish to find the closeset match and executes the wishe's action
 *  Wish: An action with a set of magic words
 */

;(function(global){

  var _wishes = {},
    _previousId = 0,
    _enteredMagicWords = {};

  function _getNextId() {
    return 'q-' + _previousId++;
  }

  function registerWish(magicWords, action, id) {
    if (!Array.isArray(magicWords)) {
      magicWords = [magicWords];
    }
    if (id === undefined) {
      id = _getNextId();
    }

    var wish = {
      id: id,
      keywords: magicWords,
      action: action
    };
    _wishes[id] = wish;
    return _wishes[id];
  }

  function deregisterWish(id) {
    // Check if it may be a wish object
    if (typeof id === 'object' && id.id) {
      id = id.id;
    }
    delete _wishes[id];
    for (var word in _enteredMagicWords) {
      if (_enteredMagicWords[word].indexOf(id) != -1) {
        _enteredMagicWords[word].splice(_enteredMagicWords[word].indexOf(id), 1);
      }
    }
  }

  function clearWishes() {
    _wishes = {};
    _enteredMagicWords = {};
  }

  function getMatchingWishes(magicWord) {
    var matchingWishIds = _enteredMagicWords[magicWord] || [];
    var otherMatchingWishId = _addOtherMatchingKeywords(matchingWishIds, magicWord);
    var allWishIds = matchingWishIds.concat(otherMatchingWishId);
    var matchingWishes = [];
    for (var i = 0; i < allWishIds.length; i++) {
      matchingWishes.push(_wishes[allWishIds[i]]);
    }
    return matchingWishes;
  }

  function _addOtherMatchingKeywords(currentMatchingWishIds, givenMagicWord) {
    var otherMatchingWishIds = [];
    for (var wishId in _wishes) {
      if (currentMatchingWishIds.indexOf(wishId) == -1) {
        var wish =_wishes[wishId];
        if (_anyKeywordsMatch(wish.keywords, givenMagicWord)) {
          otherMatchingWishIds.push(wishId);
        }
      }
    }
    return otherMatchingWishIds;
  }

  function _anyKeywordsMatch(wishesMagicWords, givenMagicWord) {
    for (var i = 0; i < wishesMagicWords.length; i++) {
      if (_stringsMatch(wishesMagicWords[i], givenMagicWord)) {
        return true;
      }
    }
    return false;
  }

  function _stringsMatch(magicWord, givenMagicWord) {
    magicWord = magicWord.toLowerCase();
    givenMagicWord = givenMagicWord.toLowerCase();
    if (givenMagicWord.length > magicWord.length) {
      return false;
    }
    if (magicWord.indexOf(givenMagicWord) != -1) {
      return true;
    }
    var charNumber = 0;
    for (var i = 0; i < givenMagicWord.length; i++) {
      var matchChar = givenMagicWord[i];
      var found = false;
      for (var j = charNumber; j < magicWord.length; j++) {
        var stringChar = magicWord[j];
        if (stringChar == matchChar) {
          found = true;
          charNumber = j + 1;
          break;
        }
      }
      if (!found) {
        return false;
      }
    }
    return true;
  }

  function makeWish(id, magicWord) {
    // Check if it may be a wish object
    if (typeof id === 'object' && id.id) {
      id = id.id;
    }
    if (id === null || id === undefined) {
      var matchingWishes = getMatchingWishes(magicWord);
      if (matchingWishes.length > 0) {
        id = matchingWishes[0].id;
      }
    }
    if (_wishes[id] && _wishes[id].action) {
      _wishes[id].action();
    }

    if (magicWord) {
      // Reset entered keywords order.
      _enteredMagicWords[magicWord] = _enteredMagicWords[magicWord] || [];
      var existingIndex = _enteredMagicWords[magicWord].indexOf(id);
      if (existingIndex != -1) {
        _enteredMagicWords[magicWord].splice(existingIndex, 1);
      }
      _enteredMagicWords[magicWord].unshift(id);
    }
  }

  function options(options) {
    _wishes = options.wishes || _wishes;
    _previousId = options.previousId || _previousId;
    _enteredMagicWords = options.enteredKeyWords || _enteredMagicWords;
    return {
      wishes: _wishes,
      previousId: _previousId,
      enteredMagicWords: _enteredMagicWords
    };
  }

  global.genie = registerWish;
  global.genie.getMatchingWishes = getMatchingWishes;
  global.genie.makeWish = makeWish;
  global.genie.setOptions = options;
  global.genie.deregisterWish = deregisterWish;
  global.genie.clearWishes = clearWishes;

})(this);
<|endoftext|>