<commit_before>angular.module('loginService', [])
.provider('loginService', function () {
  var userToken = localStorage.getItem('userToken'),
      errorState = 'app.error',
      logoutState = 'app.home';

  this.$get = function ($rootScope, $http, $q, $state) {
    /**
     * Low-level, private functions.
     */

    var setHeaders = function (token) {
      if (!token) {
        delete $http.defaults.headers.common['X-Token'];
        return;
      }
      $http.defaults.headers.common['X-Token'] = token.toString();
    };

    var setToken = function (token) {
      if (!token) {
        localStorage.removeItem('userToken');
      } else {
        localStorage.setItem('userToken', token);
      }
      setHeaders(token);
    };

    var getLoginData = function () {
      if (userToken) {
        setHeaders(userToken);
      } else {
        wrappedService.userRole = userRoles.public;
        wrappedService.isLogged = false;
      }
    };

    var managePermissions = function () {
      // Register routing function.
      $rootScope.$on('$stateChangeStart', function (event, to, toParams, from, fromParams) {

        /**
         * $stateChangeStart is a synchronous check to the accessLevels property
         * if it's not set, it will setup a pendingStateChange and will let
         * the grandfather resolve do his job.
         *
         * In short:
         * If accessLevels is still undefined, it let the user change the state.
         * Grandfather.resolve will either let the user in or reject the promise later!
         */
        if (wrappedService.userRole === null) {
          wrappedService.pendingStateChange = {
            to: to,
            toParams: toParams
          };
          return;
        }

        // if the state has undefined accessLevel, anyone can access it.
        // NOTE: if `wrappedService.userRole === undefined` means the service still doesn't know the user role,
        // we need to rely on grandfather resolve, so we let the stateChange success, for now.
        if (to.accessLevel === undefined || to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
          console.log('you are allowed on this page:', to.name);
        } else {
          event.preventDefault();
          // test this
          $state.go(errorState, { error: 'unauthorized' }, { location: false, inherit: false });
        }
      });

      // Gets triggered when a resolve isn't fulfilled
      // da aggiungere un caso in cui il resolve da informazioni solo ad un admin e non ad un user
      // quindi un url ad esempio /resource/admin
      // anche un url /resource/user
      // in questo modo si potrà vedere l'error redirect!
      // anche un caso in cui sono io che faccio fallire una $q cosi si vede l'errore stringa!
      $rootScope.$on('$stateChangeError', function (event, to, toParams, from, fromParams, error) {
        /**
         * This is a very clever way to implement failure redirection.
         * You can use the value of redirectMap, based on the value of the rejection
         * So you can setup DIFFERENT redirections based on different promise errors.
         */
        var errorObj;
        // in case the promise given to resolve function is an $http request
        // the error is a object containing the error and additional informations
        error = (typeof error === 'object') ? error.status.toString() : error;
        // in case of a 'tokenexpired', or a random 5xx status code from server, user gets loggedout
        if (error === 'tokenexpired' || /5\d{2}/.test(error)) {
          wrappedService.logoutUser();
          return $state.go(errorState, { error: error }, { location: false, inherit: false });
        }
        /**
         * Generic redirect handling.
         * If a state transition has been prevented and it's not one of the 2 above errors, means it's a
         * custom error in your application.
         *
         * redirectMap should be defined in the $state(s) that can generate transition errors.
         */
        if (angular.isDefined(to.redirectMap) && angular.isDefined(to.redirectMap[error])) {
          return $state.go(to.redirectMap[error], { error: error });
        }
        return $state.go(errorState, { error: error }, { location: false, inherit: false });
      });
    };

    /**
     * High level, public methods
     */

    var wrappedService = {
      loginHandler: function (user, status, headers, config) {
        /**
         * Custom logic to manually set userRole goes here
         *
         * Commented example shows an userObj coming with a 'completed'
         * property defining if the user has completed his registration process,
         * validating his/her email or not.
         *
         * EXAMPLE:
         * if (user.hasValidatedEmail) {
         *   wrappedService.userRole = userRoles.registered;
         * } else {
         *   wrappedService.userRole = userRoles.invalidEmail;
         *   $state.go('app.nagscreen');
         * }
         */
        // setup token
        setToken(user.token);
        // update user
        angular.extend(wrappedService.user, user);
        // flag true on isLogged
        wrappedService.isLogged = true;
        // update userRole
        wrappedService.userRole = user.userRole;
        return user;
      },
      loginUser: function (httpPromise) {
        httpPromise.success(wrappedService.loginHandler);
      },
      logoutUser: function (httpPromise) {
        /**
         * De-registers the userToken remotely
         * then clears the loginService as it was on startup
         */
        setToken(null);
        wrappedService.userRole = userRoles.public;
        wrappedService.user = {};
        wrappedService.isLogged = false;
        $state.go(logoutState);
      },
      resolvePendingState: function (httpPromise) {
        var checkUser = $q.defer(),
            finalPromise,
            pendingState = wrappedService.pendingStateChange;

        // When the $http is done, we register the http result into loginHandler, `data` parameter goes into loginService.loginHandler
        httpPromise.success(wrappedService.loginHandler);
        httpPromise.error(function () {
          checkUser.reject('tokenexpired');
        });

        /**
         * Define another check after the $http is done, this will *Actually* check if current user can access the requested $state
         */
        finalPromise = $q.all([httpPromise, checkUser.promise]);

        httpPromise.then(function (result) {
          wrappedService.isLogged = true;
          // duplicated logic from loginService $stateChangeStart, slightly different, now we *MUST* have the userRole informations.
          if (pendingState.to.accessLevel === undefined || pendingState.to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
            checkUser.resolve();
          } else {
            checkUser.reject('unauthorized');
          }
        });
        wrappedService.pendingStateChange = null;
        return finalPromise;
      },
      /**
       * Public properties
       */
      userRole: null,
      user: {},
      isLogged: null,
      pendingStateChange: null
    };

    getLoginData();
    managePermissions();

    return wrappedService;
  };
});
<commit_msg>managing error statuses on checkUser *MUCH* better<commit_after>angular.module('loginService', [])
.provider('loginService', function () {
  var userToken = localStorage.getItem('userToken'),
      errorState = 'app.error',
      logoutState = 'app.home';

  this.$get = function ($rootScope, $http, $q, $state) {
    /**
     * Low-level, private functions.
     */

    var setHeaders = function (token) {
      if (!token) {
        delete $http.defaults.headers.common['X-Token'];
        return;
      }
      $http.defaults.headers.common['X-Token'] = token.toString();
    };

    var setToken = function (token) {
      if (!token) {
        localStorage.removeItem('userToken');
      } else {
        localStorage.setItem('userToken', token);
      }
      setHeaders(token);
    };

    var getLoginData = function () {
      if (userToken) {
        setHeaders(userToken);
      } else {
        wrappedService.userRole = userRoles.public;
        wrappedService.isLogged = false;
      }
    };

    var managePermissions = function () {
      // Register routing function.
      $rootScope.$on('$stateChangeStart', function (event, to, toParams, from, fromParams) {

        /**
         * $stateChangeStart is a synchronous check to the accessLevels property
         * if it's not set, it will setup a pendingStateChange and will let
         * the grandfather resolve do his job.
         *
         * In short:
         * If accessLevels is still undefined, it let the user change the state.
         * Grandfather.resolve will either let the user in or reject the promise later!
         */
        if (wrappedService.userRole === null) {
          wrappedService.pendingStateChange = {
            to: to,
            toParams: toParams
          };
          return;
        }

        // if the state has undefined accessLevel, anyone can access it.
        // NOTE: if `wrappedService.userRole === undefined` means the service still doesn't know the user role,
        // we need to rely on grandfather resolve, so we let the stateChange success, for now.
        if (to.accessLevel === undefined || to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
          console.log('you are allowed on this page:', to.name);
        } else {
          event.preventDefault();
          // test this
          $state.go(errorState, { error: 'unauthorized' }, { location: false, inherit: false });
        }
      });

      // Gets triggered when a resolve isn't fulfilled
      // da aggiungere un caso in cui il resolve da informazioni solo ad un admin e non ad un user
      // quindi un url ad esempio /resource/admin
      // anche un url /resource/user
      // in questo modo si potrà vedere l'error redirect!
      // anche un caso in cui sono io che faccio fallire una $q cosi si vede l'errore stringa!
      $rootScope.$on('$stateChangeError', function (event, to, toParams, from, fromParams, error) {
        /**
         * This is a very clever way to implement failure redirection.
         * You can use the value of redirectMap, based on the value of the rejection
         * So you can setup DIFFERENT redirections based on different promise errors.
         */
        var errorObj;
        // in case the promise given to resolve function is an $http request
        // the error is a object containing the error and additional informations
        error = (typeof error === 'object') ? error.status.toString() : error;
        // in case of a 'tokenexpired', or a random 5xx status code from server, user gets loggedout
        if (error === 'tokenexpired') {
          wrappedService.logoutUser();
          return $state.go(errorState, { error: error }, { location: false, inherit: false });
        }
        /**
         * Generic redirect handling.
         * If a state transition has been prevented and it's not one of the 2 above errors, means it's a
         * custom error in your application.
         *
         * redirectMap should be defined in the $state(s) that can generate transition errors.
         */
        if (angular.isDefined(to.redirectMap) && angular.isDefined(to.redirectMap[error])) {
          return $state.go(to.redirectMap[error], { error: error });
        }
        return $state.go(errorState, { error: error }, { location: false, inherit: false });
      });
    };

    /**
     * High level, public methods
     */

    var wrappedService = {
      loginHandler: function (user, status, headers, config) {
        /**
         * Custom logic to manually set userRole goes here
         *
         * Commented example shows an userObj coming with a 'completed'
         * property defining if the user has completed his registration process,
         * validating his/her email or not.
         *
         * EXAMPLE:
         * if (user.hasValidatedEmail) {
         *   wrappedService.userRole = userRoles.registered;
         * } else {
         *   wrappedService.userRole = userRoles.invalidEmail;
         *   $state.go('app.nagscreen');
         * }
         */
        // setup token
        setToken(user.token);
        // update user
        angular.extend(wrappedService.user, user);
        // flag true on isLogged
        wrappedService.isLogged = true;
        // update userRole
        wrappedService.userRole = user.userRole;
        return user;
      },
      loginUser: function (httpPromise) {
        httpPromise.success(wrappedService.loginHandler);
      },
      logoutUser: function (httpPromise) {
        /**
         * De-registers the userToken remotely
         * then clears the loginService as it was on startup
         */
        setToken(null);
        wrappedService.userRole = userRoles.public;
        wrappedService.user = {};
        wrappedService.isLogged = false;
        $state.go(logoutState);
      },
      resolvePendingState: function (httpPromise) {
        var checkUser = $q.defer(),
            pendingState = wrappedService.pendingStateChange;

        // When the $http is done, we register the http result into loginHandler, `data` parameter goes into loginService.loginHandler
        httpPromise.success(wrappedService.loginHandler);
        httpPromise.success(function (data, status, headers, config) {
          checkUser.resolve();
        });
        httpPromise.error(function () {
          checkUser.reject('tokenexpired');
        });

        httpPromise.then(function (result) {
          wrappedService.isLogged = true;
          // duplicated logic from loginService $stateChangeStart, slightly different, now we *MUST* have the userRole informations.
          if (pendingState.to.accessLevel === undefined || pendingState.to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
            checkUser.resolve();
          } else {
            checkUser.reject('unauthorized');
          }
        });
        wrappedService.pendingStateChange = null;
        return checkUser.promise;
      },
      /**
       * Public properties
       */
      userRole: null,
      user: {},
      isLogged: null,
      pendingStateChange: null
    };

    getLoginData();
    managePermissions();

    return wrappedService;
  };
});
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*!
 * FlexCss.Dropdown
 * Licensed under the MIT License (MIT)
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 */

import Util from './util/Util';
import Settings from './util/Settings';
import Event from './util/Event';
import Widget from './Widget';
import FixedWindow from './lib/FixedWindow';

/**
 * @type {string}
 */
const ATTR_CC = 'data-collision-container';
/**
 * @type {string}
 */
const ATTR_DARKENER = 'data-darkener-container';
/**
 * @type {string}
 */
const DARKENER_INIT = 'init';
/**
 * @type {string}
 */
const ATTR_DATA_TARGET = 'data-target';
/**
 * @type {string}
 */
const ATTR_CLOSE_DROPDOWN = 'data-close-dropdown';
/**
 * @type {string}
 */
const ATTR_NAME = 'data-dropdown';
/**
 * @type {string}
 */
const STATE_LOADING = 'loading';

const CLS_DARKENER_DROPDOWN = 'darkener-dropdown';
/**
 * @type {string}
 */
const CLS_OPEN = 'open';

/**
 * @type {string}
 */
const CLS_DROPDOWN = 'dropdown';

/**
 * @type {string}
 */
export const EVENT_DROPDOWN_CLOSED = 'flexcss.dropdown.closed';

/**
 * A Dropdown
 */
class Dropdown {
    constructor(DelegateContainer, Darkener) {
        const doc = global.document;

        /**
         * Container Element
         * @type {HTMLElement}
         */
        this.container = DelegateContainer instanceof HTMLElement ?
            DelegateContainer : doc.getElementById(DelegateContainer);

        this.currentOpen = null;
        this.currentTarget = null;
        /**
         * @type {HTMLElement}
         */
        this.darkener = Darkener instanceof HTMLElement ? Darkener : document.getElementById(Darkener);

        this.destroyOnClose = false;

        if (!this.darkener || !this.container) {
            throw new Error('required elements not found (darkener and container element)');
        }
    }

    /**
     * Method that handles delegation events for dropdowns
     * @param e
     * @returns {boolean}
     * @private
     */
    _delegateFunction(e) {
        const currentOpen = this.currentOpen;
        const target = Util.closestCallback(e.target, n => n instanceof HTMLElement && n.hasAttribute(ATTR_NAME));
        const targetIsCurrent = target === this.currentTarget;

        if (currentOpen && !Util.isPartOfNode(e.target, currentOpen) || targetIsCurrent) {
            this.close();
            if (targetIsCurrent) {
                e.preventDefault();
            }
            return targetIsCurrent ? false : this._delegateFunction(e);
        }

        if (target && !currentOpen) {
            e.preventDefault();
            e.stopImmediatePropagation();

            if (target.isLoading) {
                return false;
            }
            this.createDropdown(target);
        } else {
            if (currentOpen) {
                if (e.target.hasAttribute(ATTR_CLOSE_DROPDOWN)) {
                    e.preventDefault();
                    this.close();
                }
                if (!Util.isPartOfNode(e.target, currentOpen)) {
                    this.close();
                }
            }
        }
    }

    /**
     * Register Events for this dropdown container
     * @returns {Dropdown}
     */
    registerEvents() {
        FixedWindow.getInstance().addScreenConstraint(Dropdown, (width) => width < Settings.get().smallBreakpoint);
        this.container.addEventListener(Settings.getTabEvent(), this._delegateFunction.bind(this), true);
        return this;
    }

    /**
     * Destroys this instance, unbinds events
     * @returns {Dropdown}
     */
    destroy() {
        this.container.removeEventListener(Settings.getTabEvent(), this._delegateFunction.bind(this), true);
        return this;
    }

    /**
     * Destroys instance on close of dropdown
     * @param v
     * @returns {Dropdown}
     */
    setDestroyOnClose(v) {
        this.destroyOnClose = v;
        return this;
    }

    /**
     * Will add the right class to container for specific darkener id
     * @param instance
     * @param show
     */
    toggleDarkenerToggler(instance, show) {
        const cls = `toggle-${(instance.id || CLS_DARKENER_DROPDOWN)}`;
        const classList = this.container.classList;
        if (show) {
            classList.add(cls);
        } else {
            classList.remove(cls);
        }
    }

    /**
     * Closes Dropdown on current instance
     * @return {Boolean|Promise}
     */
    close() {
        const currentOpen = this.currentOpen;
        if (!currentOpen) {
            return false;
        }
        const darkenerInstance = currentOpen.flexDarkenerInstance || this.darkener;
        const thisCurrentOpen = currentOpen;

        const future = new Promise((resolve) => {
            if (window.getComputedStyle(currentOpen).position === 'fixed') {
                Util.addEventOnce(Settings.getTransitionEvent(), currentOpen, function scheduler(e) {
                    if (e.target !== currentOpen) {
                        return Util.addEventOnce(Settings.getTransitionEvent(), currentOpen, scheduler.bind(this));
                    }
                    requestAnimationFrame(() => {
                        Event.dispatchAndFire(thisCurrentOpen, EVENT_DROPDOWN_CLOSED);
                        // if a new dropdown has been opened in the meantime, do not remove darkener
                        if (this.currentOpen !== null) {
                            return false;
                        }
                        this.toggleDarkenerToggler(darkenerInstance, false);
                        resolve(true);
                    });
                }.bind(this));
            } else {
                resolve(true);
                Event.dispatchAndFire(thisCurrentOpen, EVENT_DROPDOWN_CLOSED);
            }
        });

        FixedWindow.getInstance().close();
        currentOpen.classList.remove(CLS_OPEN);

        if (currentOpen.flexDarkenerInstance) {
            currentOpen.flexDarkenerInstance.classList.remove(DARKENER_INIT);
        } else {
            this.darkener.classList.remove(DARKENER_INIT);
        }

        this.currentOpen = null;
        this.currentTarget = null;

        if (this.destroyOnClose) {
            this.destroy();
        }

        return future;
    }

    /**
     * Creates a dropdown on given target and opens it
     * @param {HTMLElement} target target where this dropdown is placed
     * @param {FlexCss.Widget} [thisWidget] if given will use widget instead of widget instance
     * @return {FlexCss.Dropdown}
     */
    createDropdown(target, thisWidget) {
        const doc = global.document;

        if (!target) {
            throw new Error('Dropdown target not found');
        }

        const widget = thisWidget || Widget.findWidget(target);
        let future;
        const data = target.getAttribute(ATTR_NAME);
        const dropdownContainerElement = doc.getElementById(data);
        const async = !dropdownContainerElement && Widget.isWidget(widget) ? widget.getAsync() : false;

        if (async) {
            target.classList.add(STATE_LOADING);
            target.isLoading = true;
            future = async.then((r) => {
                if (r instanceof HTMLElement) {
                    if (r.id) {
                        target.setAttribute(ATTR_NAME, r.id);
                    }
                    return r;
                }
                // Create container Element:
                const element = doc.createElement('div');
                element.className = CLS_DROPDOWN;
                element.innerHTML = r;
                element.id = Util.guid();
                // Cache target for later use:
                target.setAttribute(ATTR_NAME, element.id);
                this.container.appendChild(element);
                return element;
            }).then((r) => {
                target.isLoading = false;
                target.classList.remove(STATE_LOADING);
                return r;
            });
        } else {
            if (!dropdownContainerElement) {
                throw new Error(`Could not found Dropdown container with ID "${data}"`);
            }
            future = new Promise((r) => {
                r(dropdownContainerElement);
            });
        }

        future.then((dropdownContent) => {
            if (this.currentOpen) {
                this.close();
            }
            // Skip one frame to show animation
            target.dropdownContent = dropdownContent;
            const isAbsolute = global.getComputedStyle(dropdownContent).position === 'absolute';
            dropdownContent.hfWidgetInstance = this;

            if (!target.flexCollisionContainer) {
                const collisionC = target.getAttribute(ATTR_CC);
                target.flexCollisionContainer = collisionC ?
                doc.getElementById(collisionC) || document.documentElement : document.documentElement;
            }

            dropdownContent.classList.toggle(CLS_OPEN);
            if (dropdownContent.classList.contains(CLS_OPEN)) {
                this.currentOpen = dropdownContent;
                this.currentTarget = target;
            }
            FixedWindow.getInstance().open(this, dropdownContent);
            if (isAbsolute) {
                // Check collision:
                let selfTarget = target.getAttribute(ATTR_DATA_TARGET);
                selfTarget = selfTarget ? doc.getElementById(selfTarget) : target;
                Util.setupPositionNearby(selfTarget, dropdownContent, target.flexCollisionContainer);
            } else {
                // optionally get custom darkener container for target
                const d = target.getAttribute(ATTR_DARKENER);
                if (d) {
                    dropdownContent.flexDarkenerInstance = doc.getElementById(d);
                    (dropdownContent.flexDarkenerInstance || this.darkener).classList.toggle(DARKENER_INIT);
                } else {
                    this.darkener.classList.toggle(DARKENER_INIT);
                }
                this.toggleDarkenerToggler(dropdownContent.flexDarkenerInstance || this.darkener, true);

                dropdownContent.style.left = '0';
                dropdownContent.style.top = 'auto';
            }
        });
    }

}

export default Dropdown;
<commit_msg>fix(Dropdown): added option to enable event bubbeling on click<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*!
 * FlexCss.Dropdown
 * Licensed under the MIT License (MIT)
 * Copyright (c) 2015 David Heidrich, BowlingX <me@bowlingx.com>
 */

import Util from './util/Util';
import Settings from './util/Settings';
import Event from './util/Event';
import Widget from './Widget';
import FixedWindow from './lib/FixedWindow';

/**
 * @type {string}
 */
const ATTR_CC = 'data-collision-container';
/**
 * @type {string}
 */
const ATTR_DARKENER = 'data-darkener-container';
/**
 * @type {string}
 */
const ATTR_ENABLE_EVENT_BUBBLE = 'data-enable-event-bubble';
/**
 * @type {string}
 */
const DARKENER_INIT = 'init';
/**
 * @type {string}
 */
const ATTR_DATA_TARGET = 'data-target';
/**
 * @type {string}
 */
const ATTR_CLOSE_DROPDOWN = 'data-close-dropdown';
/**
 * @type {string}
 */
const ATTR_NAME = 'data-dropdown';
/**
 * @type {string}
 */
const STATE_LOADING = 'loading';

const CLS_DARKENER_DROPDOWN = 'darkener-dropdown';
/**
 * @type {string}
 */
const CLS_OPEN = 'open';

/**
 * @type {string}
 */
const CLS_DROPDOWN = 'dropdown';

/**
 * @type {string}
 */
export const EVENT_DROPDOWN_CLOSED = 'flexcss.dropdown.closed';

/**
 * A Dropdown
 */
class Dropdown {
    constructor(DelegateContainer, Darkener) {
        const doc = global.document;

        /**
         * Container Element
         * @type {HTMLElement}
         */
        this.container = DelegateContainer instanceof HTMLElement ?
            DelegateContainer : doc.getElementById(DelegateContainer);

        this.currentOpen = null;
        this.currentTarget = null;
        /**
         * @type {HTMLElement}
         */
        this.darkener = Darkener instanceof HTMLElement ? Darkener : document.getElementById(Darkener);

        this.destroyOnClose = false;

        if (!this.darkener || !this.container) {
            throw new Error('required elements not found (darkener and container element)');
        }
    }

    /**
     * Method that handles delegation events for dropdowns
     * @param e
     * @returns {boolean}
     * @private
     */
    _delegateFunction(e) {
        const currentOpen = this.currentOpen;
        const target = Util.closestCallback(e.target, n => n instanceof HTMLElement && n.hasAttribute(ATTR_NAME));
        const targetIsCurrent = target === this.currentTarget;

        if (currentOpen && !Util.isPartOfNode(e.target, currentOpen) || targetIsCurrent) {
            this.close();
            if (targetIsCurrent) {
                e.preventDefault();
            }
            return targetIsCurrent ? false : this._delegateFunction(e);
        }

        if (target && !currentOpen) {

            if (!target.getAttribute(ATTR_ENABLE_EVENT_BUBBLE)) {
                e.preventDefault();
                e.stopImmediatePropagation();
            }

            if (target.isLoading) {
                return false;
            }
            this.createDropdown(target);
        } else {
            if (currentOpen) {
                if (e.target.hasAttribute(ATTR_CLOSE_DROPDOWN)) {
                    e.preventDefault();
                    this.close();
                }
                if (!Util.isPartOfNode(e.target, currentOpen)) {
                    this.close();
                }
            }
        }
    }

    /**
     * Register Events for this dropdown container
     * @returns {Dropdown}
     */
    registerEvents() {
        FixedWindow.getInstance().addScreenConstraint(Dropdown, (width) => width < Settings.get().smallBreakpoint);
        this.container.addEventListener(Settings.getTabEvent(), this._delegateFunction.bind(this), true);
        return this;
    }

    /**
     * Destroys this instance, unbinds events
     * @returns {Dropdown}
     */
    destroy() {
        this.container.removeEventListener(Settings.getTabEvent(), this._delegateFunction.bind(this), true);
        return this;
    }

    /**
     * Destroys instance on close of dropdown
     * @param v
     * @returns {Dropdown}
     */
    setDestroyOnClose(v) {
        this.destroyOnClose = v;
        return this;
    }

    /**
     * Will add the right class to container for specific darkener id
     * @param instance
     * @param show
     */
    toggleDarkenerToggler(instance, show) {
        const cls = `toggle-${(instance.id || CLS_DARKENER_DROPDOWN)}`;
        const classList = this.container.classList;
        if (show) {
            classList.add(cls);
        } else {
            classList.remove(cls);
        }
    }

    /**
     * Closes Dropdown on current instance
     * @return {Boolean|Promise}
     */
    close() {
        const currentOpen = this.currentOpen;
        if (!currentOpen) {
            return false;
        }
        const darkenerInstance = currentOpen.flexDarkenerInstance || this.darkener;
        const thisCurrentOpen = currentOpen;

        const future = new Promise((resolve) => {
            if (window.getComputedStyle(currentOpen).position === 'fixed') {
                Util.addEventOnce(Settings.getTransitionEvent(), currentOpen, function scheduler(e) {
                    if (e.target !== currentOpen) {
                        return Util.addEventOnce(Settings.getTransitionEvent(), currentOpen, scheduler.bind(this));
                    }
                    requestAnimationFrame(() => {
                        Event.dispatchAndFire(thisCurrentOpen, EVENT_DROPDOWN_CLOSED);
                        // if a new dropdown has been opened in the meantime, do not remove darkener
                        if (this.currentOpen !== null) {
                            return false;
                        }
                        this.toggleDarkenerToggler(darkenerInstance, false);
                        resolve(true);
                    });
                }.bind(this));
            } else {
                resolve(true);
                Event.dispatchAndFire(thisCurrentOpen, EVENT_DROPDOWN_CLOSED);
            }
        });

        FixedWindow.getInstance().close();
        currentOpen.classList.remove(CLS_OPEN);

        if (currentOpen.flexDarkenerInstance) {
            currentOpen.flexDarkenerInstance.classList.remove(DARKENER_INIT);
        } else {
            this.darkener.classList.remove(DARKENER_INIT);
        }

        this.currentOpen = null;
        this.currentTarget = null;

        if (this.destroyOnClose) {
            this.destroy();
        }

        return future;
    }

    /**
     * Creates a dropdown on given target and opens it
     * @param {HTMLElement} target target where this dropdown is placed
     * @param {FlexCss.Widget} [thisWidget] if given will use widget instead of widget instance
     * @return {FlexCss.Dropdown}
     */
    createDropdown(target, thisWidget) {
        const doc = global.document;

        if (!target) {
            throw new Error('Dropdown target not found');
        }

        const widget = thisWidget || Widget.findWidget(target);
        let future;
        const data = target.getAttribute(ATTR_NAME);
        const dropdownContainerElement = doc.getElementById(data);
        const async = !dropdownContainerElement && Widget.isWidget(widget) ? widget.getAsync() : false;

        if (async) {
            target.classList.add(STATE_LOADING);
            target.isLoading = true;
            future = async.then((r) => {
                if (r instanceof HTMLElement) {
                    if (r.id) {
                        target.setAttribute(ATTR_NAME, r.id);
                    }
                    return r;
                }
                // Create container Element:
                const element = doc.createElement('div');
                element.className = CLS_DROPDOWN;
                element.innerHTML = r;
                element.id = Util.guid();
                // Cache target for later use:
                target.setAttribute(ATTR_NAME, element.id);
                this.container.appendChild(element);
                return element;
            }).then((r) => {
                target.isLoading = false;
                target.classList.remove(STATE_LOADING);
                return r;
            });
        } else {
            if (!dropdownContainerElement) {
                throw new Error(`Could not found Dropdown container with ID "${data}"`);
            }
            future = new Promise((r) => {
                r(dropdownContainerElement);
            });
        }

        future.then((dropdownContent) => {
            if (this.currentOpen) {
                this.close();
            }
            // Skip one frame to show animation
            target.dropdownContent = dropdownContent;
            const isAbsolute = global.getComputedStyle(dropdownContent).position === 'absolute';
            dropdownContent.hfWidgetInstance = this;

            if (!target.flexCollisionContainer) {
                const collisionC = target.getAttribute(ATTR_CC);
                target.flexCollisionContainer = collisionC ?
                doc.getElementById(collisionC) || document.documentElement : document.documentElement;
            }

            dropdownContent.classList.toggle(CLS_OPEN);
            if (dropdownContent.classList.contains(CLS_OPEN)) {
                this.currentOpen = dropdownContent;
                this.currentTarget = target;
            }
            FixedWindow.getInstance().open(this, dropdownContent);
            if (isAbsolute) {
                // Check collision:
                let selfTarget = target.getAttribute(ATTR_DATA_TARGET);
                selfTarget = selfTarget ? doc.getElementById(selfTarget) : target;
                Util.setupPositionNearby(selfTarget, dropdownContent, target.flexCollisionContainer);
            } else {
                // optionally get custom darkener container for target
                const d = target.getAttribute(ATTR_DARKENER);
                if (d) {
                    dropdownContent.flexDarkenerInstance = doc.getElementById(d);
                    (dropdownContent.flexDarkenerInstance || this.darkener).classList.toggle(DARKENER_INIT);
                } else {
                    this.darkener.classList.toggle(DARKENER_INIT);
                }
                this.toggleDarkenerToggler(dropdownContent.flexDarkenerInstance || this.darkener, true);

                dropdownContent.style.left = '0';
                dropdownContent.style.top = 'auto';
            }
        });
    }

}

export default Dropdown;
<|endoftext|>


<commit_before>/* Soramame.ps.js から Processing.jsへの変換辞書
*/

function lib_dic() {

  this.num = 0;

  //変換候補データ - 命令
  //元データ ： 変換先データ
  this.commands = {
  	//Structure
	サイズ : 'size',
	初期設定する : 'setup',
	くり返し描画する : 'draw',
	もどる : 'return',

	//Environment
	コマ数 : 'frameRate',

	//2D Primitives
	テン : 'point',
	セン: 'line',
	マル : 'ellipse',
	サンカク : 'triangle',
	シカク : 'rect',
	円弧 : 'arc',
	四辺形 : 'quad',

	//attributes
	輪郭幅 : 'strokeWeight',

	//Input - Mouse
	クリック時 : 'mouseClicked',
	ドラッグ時 : 'mouseDragged',

	//Color
	//setting
	背景色 : 'background',
	輪郭色 : 'stroke',
	輪郭なし : 'noStroke',
	塗りつぶし色 : 'fill',
	塗りつぶしなし : 'noFill',

	//Typography
	//Loading & Displaying
	もじ表示 : 'text',

	//attributes
	もじサイズ : 'textSize',

	//test
	test : '//SoraMame Script!'};

  //変換候補データ - 環境変数
  //元データ ： 変換先データ
  this.vars = {

	//Environment
	幅 : 'width',
	高さ : 'height',

	//Input - Mouse
	マウスX : 'mouseX',
	マウスY : 'mouseY',
	マウスx : 'mouseX',
	マウスy : 'mouseY',

	//test
	test : '//SoraMame Script!'};

  //変換候補データ - データ型命
  //元データ ： 変換先データ
  this.types = {

	//Primitive
	整数 : 'int',
	実数 : 'float',
	文字型 : 'char',
	正否型 : 'boolean',
	カラー型 : 'color',

	//test
	test : '//SoraMame Script!'};
}

lib_dic.prototype.get_comm = function(str, arg){
	var msg;
    if ( this.commands[str] === undefined ) {
		this.commands[str] = "func_" + this.num;
		this.num += 1;
	}
	msg = this.commands[str] + '(' + arg +')'; //function
	return msg;
}

lib_dic.prototype.get_vars = function(str, arg){
	var msg;
    if ( this.vars[str] === undefined ) {
		msg = str;
	} else {
		msg = this.vars[str]; //vars of environment
    }
	return msg;
}

lib_dic.prototype.get_types= function(str, arg){
	var msg;
    if ( this.types[str] === undefined ) {
		msg = str;
	} else {
		msg = this.types[str]; //types of environment
    }
	return msg;
}<commit_msg>データ型の名前を統一。 全部、＊＊型としてみた。<commit_after>/* Soramame.ps.js から Processing.jsへの変換辞書
*/

function lib_dic() {

  this.num = 0;

  //変換候補データ - 命令
  //元データ ： 変換先データ
  this.commands = {
  	//Structure
	サイズ : 'size',
	初期設定する : 'setup',
	くり返し描画する : 'draw',
	もどる : 'return',

	//Environment
	コマ数 : 'frameRate',

	//2D Primitives
	テン : 'point',
	セン: 'line',
	マル : 'ellipse',
	サンカク : 'triangle',
	シカク : 'rect',
	円弧 : 'arc',
	四辺形 : 'quad',

	//attributes
	輪郭幅 : 'strokeWeight',

	//Input - Mouse
	クリック時 : 'mouseClicked',
	ドラッグ時 : 'mouseDragged',

	//Color
	//setting
	背景色 : 'background',
	輪郭色 : 'stroke',
	輪郭なし : 'noStroke',
	塗りつぶし色 : 'fill',
	塗りつぶしなし : 'noFill',

	//Typography
	//Loading & Displaying
	もじ表示 : 'text',

	//attributes
	もじサイズ : 'textSize',

	//test
	test : '//SoraMame Script!'};

  //変換候補データ - 環境変数
  //元データ ： 変換先データ
  this.vars = {

	//Environment
	幅 : 'width',
	高さ : 'height',

	//Input - Mouse
	マウスX : 'mouseX',
	マウスY : 'mouseY',
	マウスx : 'mouseX',
	マウスy : 'mouseY',

	//test
	test : '//SoraMame Script!'};

  //変換候補データ - データ型命
  //元データ ： 変換先データ
  this.types = {

	//Primitive
	整数型 : 'int',
	実数型 : 'float',
	文字型 : 'char',
	正否型 : 'boolean',
	カラー型 : 'color',

	//test
	test : '//SoraMame Script!'};
}

lib_dic.prototype.get_comm = function(str, arg){
	var msg;
    if ( this.commands[str] === undefined ) {
		this.commands[str] = "func_" + this.num;
		this.num += 1;
	}
	msg = this.commands[str] + '(' + arg +')'; //function
	return msg;
}

lib_dic.prototype.get_vars = function(str, arg){
	var msg;
    if ( this.vars[str] === undefined ) {
		msg = str;
	} else {
		msg = this.vars[str]; //vars of environment
    }
	return msg;
}

lib_dic.prototype.get_types= function(str, arg){
	var msg;
    if ( this.types[str] === undefined ) {
		msg = str;
	} else {
		msg = this.types[str]; //types of environment
    }
	return msg;
}<|endoftext|>


<commit_before>var Groups = null;

var init = config => {
	var path = require("path");
	Groups = require(path.join(config.model_dir, 'usergroups_model'));
};

var actionPut = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.body.group;
	if (!group) {
		console.error("Group required");
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id: user_id });
		if (!userGroup) {
			userGroup = new Groups();
			userGroup.user_id = user_id;
			userGroup.groups = [];
		}
		if (Array.isArray(group)) {
			group.forEach(function(g) {
				if (userGroup.groups.indexOf(g) == -1) {
					userGroup.groups.push(g);
				}
			});
		} else {
			if (userGroup.groups.indexOf(group) == -1) {
				userGroup.groups.push(group);
			}
		}
		res.send(await userGroup.save());
	} catch(err) {
			console.error(err);
			res.send(500, err);
			return;
	}
};

var actionPost = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.body.group;
	if (!group) {
		console.error("Group required");
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id: user_id });
		if (!userGroup) {
			userGroup = new Groups();
			userGroup.user_id = user_id;
		}
		userGroup.groups = [];
		if (Array.isArray(group)) {
			group.forEach(function (g) {
				if (userGroup.groups.indexOf(g) == -1) {
					userGroup.groups.push(g);
				}
			});
		} else {
			if (userGroup.groups.indexOf(group) == -1) {
				userGroup.groups.push(group);
			}
		}
		res.send(await userGroup.save());
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

var actionGet = async (req, res) => {
	var user_id = req.params.user_id;
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id });
		if (!userGroup) {
			res.send({ groups: [] });
			return;
		}
		res.send(userGroup);
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

var actionDelete = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.query.group;
	if (!group) {
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id });
		if (!userGroup) {
			res.send(400, "User not found");
			return;
		}
		var i = userGroup.groups.indexOf(group);
		if (i > -1) {
			userGroup.groups.splice(i, 1);
		}
		res.send(await userGroup.save());
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

module.exports = {
	init: init,
	actionPut: actionPut,
	actionPost: actionPost,
	actionGet: actionGet,
	actionDelete: actionDelete
};
<commit_msg>Minor refactor<commit_after>const Groups = null;

const init = config => {
	const path = require("path");
	Groups = require(path.join(config.model_dir, 'usergroups_model'));
};

const actionPut = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.body.group;
	if (!group) {
		console.error("Group required");
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id: user_id });
		if (!userGroup) {
			userGroup = new Groups();
			userGroup.user_id = user_id;
			userGroup.groups = [];
		}
		if (Array.isArray(group)) {
			group.forEach(function(g) {
				if (userGroup.groups.indexOf(g) == -1) {
					userGroup.groups.push(g);
				}
			});
		} else {
			if (userGroup.groups.indexOf(group) == -1) {
				userGroup.groups.push(group);
			}
		}
		res.send(await userGroup.save());
	} catch(err) {
			console.error(err);
			res.send(500, err);
			return;
	}
};

const actionPost = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.body.group;
	if (!group) {
		console.error("Group required");
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id: user_id });
		if (!userGroup) {
			userGroup = new Groups();
			userGroup.user_id = user_id;
		}
		userGroup.groups = [];
		if (Array.isArray(group)) {
			group.forEach(function (g) {
				if (userGroup.groups.indexOf(g) == -1) {
					userGroup.groups.push(g);
				}
			});
		} else {
			if (userGroup.groups.indexOf(group) == -1) {
				userGroup.groups.push(group);
			}
		}
		res.send(await userGroup.save());
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

const actionGet = async (req, res) => {
	var user_id = req.params.user_id;
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id });
		if (!userGroup) {
			res.send({ groups: [] });
			return;
		}
		res.send(userGroup);
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

const actionDelete = async (req, res) => {
	const user_id = req.params.user_id;
	const group = req.query.group;
	if (!group) {
		res.send(400, "Group required");
		return;
	}
	if (!user_id) {
		console.error("user_id required");
		res.send(400, "user_id required");
		return;
	}
	try {
		let userGroup = await Groups.findOne({ user_id });
		if (!userGroup) {
			res.send(400, "User not found");
			return;
		}
		let i = userGroup.groups.indexOf(group);
		if (i > -1) {
			userGroup.groups.splice(i, 1);
		}
		res.send(await userGroup.save());
	} catch (err) {
		console.error(err);
		res.send(500, err);
		return;
	}
};

module.exports = {
	init,
	actionPut,
	actionPost,
	actionGet,
	actionDelete
};
<|endoftext|>


<commit_before>"use strict";
var socket = {};

var Promise  = require('bluebird');
var debug    = require('debug')('socket')
var moment   = require('moment-timezone');
var marked   = require('marked');
var emojione = require('emojione');

var redis    = require('../libs/redis')();
var Smileys  = require('../libs/smileys');
var sentence = require('./sentences');
var Users    = require('../models/users');
var Messages = require('../models/messages');

var users    = new Users(redis.client);
var messages = new Messages(redis.client);
var smileys  = new Smileys();

var dateFormat = 'DD/MM à HH:mm:ss'

emojione.ascii = true;

marked.setOptions({
  breaks: true,
  tables: false,
  sanitize: true,
  highlight: function(code) {
    return require('highlight.js').highlightAuto(code).value;
  }
});

var renderer = new marked.Renderer();

renderer.link = function(href, title, text) {
  return '' + text + ''
}

renderer.paragraph = function(text) {
  return smileys.replace(emojione.shortnameToImage(text));
}

// Ping du client toutes les 50 secondes pour éviter
// un drop de la connexion par Heroku au bout de 55
// secondes (erreur H15)
var heartbeatInterval = 50000;

socket.init = function(io) {

  function sendHeartbeat() {
    setTimeout(sendHeartbeat, heartbeatInterval);
    io.emit('ping', { beat : 1 });
  }

  io.on('connection', function(socket) {

    var session = socket.handshake.session;
    var time = moment().tz('Europe/Paris').format(dateFormat);

    if( ! session.user )
      return;

    session.user.socket = socket.id;

    return users.exist(session.user.name)
    .then(function(exist) {
      if(exist) throw new AlreadyConnectedError();
      else return Promise.resolve()
    })
    .then(function() {
      return users.banned(session.user.name).then(function(isBanned) {
        if(isBanned) throw new UserBannedError();
        else return Promise.resolve();
      });
    })
    .then(function() {
      users.add(session.user);
      io.emit('user_new', time, session.user.name);
      users.list().map(function(user) {
        io.emit('user_connected', user);
      });
    })
    .then(function() {
      // Réception la réponse (pong) du client
      socket.on('pong', function(data) {
        debug('Pong received from client');
      });
      // Réception d'un message
      socket.on('message', function(message) {
        if(message.trim() && message.length <= 1000) {
          addMessage(io, session.user, marked(message, { renderer:renderer }));
          var prob  = message.search(/chatbot/i) !== -1 ? 5 : 500;
          if(getRandomInt({ emax:prob }) === 0) {
            var delay = getRandomInt({ min:5, max:15 }) * 1000;
            Promise.delay(delay).then(function() {
              chatbotSpeech(io, session.user.name);
            });
          }
        }
      });
      // Frappe au clavier
      socket.on('typing', function(isTyping) {
        socket.broadcast.emit('isTyping', {
          isTyping:isTyping,
          user:session.user.name
        });
      });
      // Suppression d'un message
      socket.on('remove_message', function(id) {
        io.emit('remove_message', id);
      });
      // Déconnexion de l'utilisateur
      socket.on('disconnect', function() {
        users.remove(session.user.name);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_disconnected', time, session.user);
      });
      // Ban d'un utilisateur par un admin
      socket.on('ban', function(username) {
        if(!session.user.isAdmin || username.toLowerCase() == session.user.name.toLowerCase())
          return;
        banUser(io, socket.id, username);
      });
      // Deban d'un utilisateur
      socket.on('unban', function(username) {
        if(!session.user.isAdmin)
          return;
        users.unban(username);
        addBotMessage(io, " Une seconde chance a été offerte à " + username, { storage:true });
      });
      // Liste des utilisateurs bannis
      socket.on('banlist', function() {
        if(!session.user.isAdmin)
          return;
        users.banlist().map(function(user) {
          return user.name;
        })
        .then(function(userslist) {
          var message = 'Liste (' + userslist.length + ') : ' + userslist.toString();
          addBotMessage(io, userslist.length > 0 ? message : "Personne n'a été banni :)", { socket:socket.id });
        });
      });
      // Débloquer un utilisateur
      socket.on('unlock', function(username) {
        if(!session.user.isAdmin)
          return;
        users.remove(username);
        addBotMessage(io, username + ' a été débloqué', { socket:socket.id });
      });
      // Utilisateur est AFK
      socket.on('afk', function() {
        session.user.status = 'afk';
        users.add(session.user);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_afk', time, session.user.name);
      });
      // Utilisateur n'est plus AFK
      socket.on('unafk', function() {
        session.user.status = 'online';
        users.add(session.user);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_unafk', time, session.user.name);
      });
      // Messages privés
      socket.on('private_message', function(username, message) {
        if(username.toLowerCase() == session.user.name.toLowerCase()) {
          addBotMessage(io, 'WTF, ' + session.user.name + ' se parle à lui même oO ...', { storage:true });
          return;
        }
        users.getUserSocket(username)
        .then(function(userSocket) {
          time = moment().tz('Europe/Paris').format(dateFormat);
          if(message.trim() && message.length <= 1000) {
            var marksrc  = marked('*(chuchotte à **' + username + '**)* ' + message, { renderer:renderer });
            var markdest = marked('*(murmure)* ' + message, { renderer:renderer });
            io.to(socket.id).emit('message', { time:time, user:session.user, message:marksrc });
            io.to(userSocket).emit('message', { time:time, user:session.user, message:markdest });
            io.to(userSocket).emit('private_notification', session.user.name);
          }
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable, transmission du message impossible...', { socket:socket.id });
        });
      });
      // Highlight d'un utilisateur
      socket.on('highlight', function(username) {
        if(username.toLowerCase() == session.user.name.toLowerCase()) {
          addBotMessage(io, 'WTF, ' + session.user.name + ' se poke lui même oO ...', { storage:true });
          return;
        }
        users.getUserSocket(username)
        .then(function(userSocket) {
          time = moment().tz('Europe/Paris').format(dateFormat);
          addBotMessage(io, 'Vous avez poke @' + username, { socket:socket.id });
          io.to(userSocket).emit('user_highlight', time, session.user.name);
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socket.id });
        });
      });
      // Lancer un dé
      socket.on('roll', function(pattern) {
        if(!pattern) {
          addBotMessage(io, session.user.name + " lance 1d6 et obtient " + getRandomInt({ min:1, max:6 }), { storage:true });
          return;
        }

        var dice   = pattern.split('d');
        var number = parseInt(dice[0], 10);
        var sides  = parseInt(dice[1], 10);
        var result = [];

        number = (number > 0 && number <= 10)  ? number : 1 ;
        sides  = (sides  > 0 && sides  <= 100) ? sides  : 6 ;

        for(var i = 0; i < number; i++) {
          result.push(getRandomInt({ min:1, max:sides }));
        }

        var message = session.user.name + " lance " + number + "d" + sides + " et obtient " + result.toString();
        addBotMessage(io, message, { storage:true });
      });
      socket.on('rolluser', function(username) {
        users.getUserSocket(username)
        .then(function() {
          var message, storage;
          var lucky = getRandomInt({ emax:200 });
          switch(lucky) {
            case 0:
              message = 'Mouhahaha :evil:';
              storage = { storage:true };
              banUser(io, socket.id, username);
              break;
            case 199:
              message = 'Dommage, le trolleur trollé :D';
              storage = { storage:true };
              banUser(io, socket.id, session.user.name);
              break;
            default:
              message = '... la tentative a échoué, peut-être une autre fois :3';
              break;
          }
          addBotMessage(io, "Quelqu'un a tenté un roll " + username + "...", storage);
          return Promise.delay({ message:message, storage:storage }, 50);
        })
        .then(function(result) {
          addBotMessage(io, result.message, result.storage);
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socket.id });
        });
      });
    })
    .catch(AlreadyConnectedError, function() {
      Promise.delay(500).then(function() {
        io.to(socket.id).emit('already_connected');
      });
    })
    .catch(UserBannedError, function() {
      Promise.delay(500).then(function() {
        io.to(socket.id).emit('user_banned');
      });
    });
  });
  // Envoi du premier Heartbeat
  setTimeout(sendHeartbeat, heartbeatInterval);
};

var addMessage = function(io, user, message) {
  var time = moment().tz('Europe/Paris').format(dateFormat);
  var data = { time:time, user:user, message:message };
  messages.add(time, user.name, message)
  .then(function(id) {
    data.id = id;
    io.emit('message', data);
  });
};

var addBotMessage = function(io, message, options) {
  var time = moment().tz('Europe/Paris').format(dateFormat);
  var data = { type:'message-bot', time:time, message:message };
  if(!options) {
    io.emit('message', data);
    return;
  }
  if(options.storage)
    messages.add(time, null, message);
  if(options.socket)
    io.to(options.socket).emit('message', data);
  else
    io.emit('message', data);
};

var banUser = function(io, socketid, username) {
  users.getUserSocket(username)
  .then(function(userSocket) {
    users.ban(username);
    io.to(userSocket).emit('ban');
    addBotMessage(io, username + " a été kick du chat", { storage:true });
  })
  .catch(function() {
    addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socketid });
  });
}

function chatbotSpeech(io, username) {
  var messages = sentence.get();
  var index = getRandomInt({ emax:messages.length });
  addBotMessage(io, messages[index].replace('%user', username), { storage:true });
}

/*
 * Génére un nombre aléatoire selon le nombre de faces
 * Les valeurs min et max sont incluses [1-sides]
 */
function rollDice(sides) {
  return Math.floor(Math.random() * (sides - 1 + 1)) + 1;
}

/*
 * Génère un entier aléatoire
 *
 * Ranges possibles :
 * ----------------------------
 * { max:x }         // [0-x]
 * { min:x, max:y }  // [x-y]
 * { min:x, emax:y } // [x-y[
 * { emax:x }        // [0-x[
 * ----------------------------
 */
function getRandomInt(range) {
  if(!range.min) range.min = 0;
  if(range.max) // Inclusive max
    return Math.floor(Math.random() * (range.max - range.min + 1)) + range.min;
  else if(range.emax) // Exclusive max
    return Math.floor(Math.random() * (range.emax - range.min)) + range.min;
}

// Déclaration des erreurs
function AlreadyConnectedError() {}
function UserBannedError() {}

// Déclaration des prototypes
AlreadyConnectedError.prototype = Object.create(Error.prototype);
UserBannedError.prototype = Object.create(Error.prototype);

module.exports = socket;<commit_msg>fix(): correction mineur<commit_after>"use strict";
var socket = {};

var Promise  = require('bluebird');
var debug    = require('debug')('socket')
var moment   = require('moment-timezone');
var marked   = require('marked');
var emojione = require('emojione');

var redis    = require('../libs/redis')();
var Smileys  = require('../libs/smileys');
var sentence = require('./sentences');
var Users    = require('../models/users');
var Messages = require('../models/messages');

var users    = new Users(redis.client);
var messages = new Messages(redis.client);
var smileys  = new Smileys();

var dateFormat = 'DD/MM à HH:mm:ss'

emojione.ascii = true;

marked.setOptions({
  breaks: true,
  tables: false,
  sanitize: true,
  highlight: function(code) {
    return require('highlight.js').highlightAuto(code).value;
  }
});

var renderer = new marked.Renderer();

renderer.link = function(href, title, text) {
  return '' + text + ''
}

renderer.paragraph = function(text) {
  return smileys.replace(emojione.shortnameToImage(text));
}

// Ping du client toutes les 50 secondes pour éviter
// un drop de la connexion par Heroku au bout de 55
// secondes (erreur H15)
var heartbeatInterval = 50000;

socket.init = function(io) {

  function sendHeartbeat() {
    setTimeout(sendHeartbeat, heartbeatInterval);
    io.emit('ping', { beat : 1 });
  }

  io.on('connection', function(socket) {

    var session = socket.handshake.session;
    var time = moment().tz('Europe/Paris').format(dateFormat);

    if( ! session.user )
      return;

    session.user.socket = socket.id;

    return users.exist(session.user.name)
    .then(function(exist) {
      if(exist) throw new AlreadyConnectedError();
      else return Promise.resolve()
    })
    .then(function() {
      return users.banned(session.user.name).then(function(isBanned) {
        if(isBanned) throw new UserBannedError();
        else return Promise.resolve();
      });
    })
    .then(function() {
      users.add(session.user);
      io.emit('user_new', time, session.user.name);
      users.list().map(function(user) {
        io.emit('user_connected', user);
      });
    })
    .then(function() {
      // Réception la réponse (pong) du client
      socket.on('pong', function(data) {
        debug('Pong received from client');
      });
      // Réception d'un message
      socket.on('message', function(message) {
        if(message.trim() && message.length <= 1000) {
          addMessage(io, session.user, marked(message, { renderer:renderer }));
          var prob  = message.search(/chatbot/gi) !== -1 ? 5 : 400;
          if(getRandomInt({ emax:prob }) === 0) {
            var delay = getRandomInt({ min:5, max:15 }) * 1000;
            Promise.delay(delay).then(function() {
              chatbotSpeech(io, session.user.name);
            });
          }
        }
      });
      // Frappe au clavier
      socket.on('typing', function(isTyping) {
        socket.broadcast.emit('isTyping', {
          isTyping:isTyping,
          user:session.user.name
        });
      });
      // Suppression d'un message
      socket.on('remove_message', function(id) {
        io.emit('remove_message', id);
      });
      // Déconnexion de l'utilisateur
      socket.on('disconnect', function() {
        users.remove(session.user.name);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_disconnected', time, session.user);
      });
      // Ban d'un utilisateur par un admin
      socket.on('ban', function(username) {
        if(!session.user.isAdmin || username.toLowerCase() == session.user.name.toLowerCase())
          return;
        banUser(io, socket.id, username);
      });
      // Deban d'un utilisateur
      socket.on('unban', function(username) {
        if(!session.user.isAdmin)
          return;
        users.unban(username);
        addBotMessage(io, " Une seconde chance a été offerte à " + username, { storage:true });
      });
      // Liste des utilisateurs bannis
      socket.on('banlist', function() {
        if(!session.user.isAdmin)
          return;
        users.banlist().map(function(user) {
          return user.name;
        })
        .then(function(userslist) {
          var message = 'Liste (' + userslist.length + ') : ' + userslist.toString();
          addBotMessage(io, userslist.length > 0 ? message : "Personne n'a été banni :)", { socket:socket.id });
        });
      });
      // Débloquer un utilisateur
      socket.on('unlock', function(username) {
        if(!session.user.isAdmin)
          return;
        users.remove(username);
        addBotMessage(io, username + ' a été débloqué', { socket:socket.id });
      });
      // Utilisateur est AFK
      socket.on('afk', function() {
        session.user.status = 'afk';
        users.add(session.user);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_afk', time, session.user.name);
      });
      // Utilisateur n'est plus AFK
      socket.on('unafk', function() {
        session.user.status = 'online';
        users.add(session.user);
        time = moment().tz('Europe/Paris').format(dateFormat);
        io.emit('user_unafk', time, session.user.name);
      });
      // Messages privés
      socket.on('private_message', function(username, message) {
        if(username.toLowerCase() == session.user.name.toLowerCase()) {
          addBotMessage(io, 'WTF, ' + session.user.name + ' se parle à lui même oO ...', { storage:true });
          return;
        }
        users.getUserSocket(username)
        .then(function(userSocket) {
          time = moment().tz('Europe/Paris').format(dateFormat);
          if(message.trim() && message.length <= 1000) {
            var marksrc  = marked('*(chuchotte à **' + username + '**)* ' + message, { renderer:renderer });
            var markdest = marked('*(murmure)* ' + message, { renderer:renderer });
            io.to(socket.id).emit('message', { time:time, user:session.user, message:marksrc });
            io.to(userSocket).emit('message', { time:time, user:session.user, message:markdest });
            io.to(userSocket).emit('private_notification', session.user.name);
          }
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable, transmission du message impossible...', { socket:socket.id });
        });
      });
      // Highlight d'un utilisateur
      socket.on('highlight', function(username) {
        if(username.toLowerCase() == session.user.name.toLowerCase()) {
          addBotMessage(io, 'WTF, ' + session.user.name + ' se poke lui même oO ...', { storage:true });
          return;
        }
        users.getUserSocket(username)
        .then(function(userSocket) {
          time = moment().tz('Europe/Paris').format(dateFormat);
          addBotMessage(io, 'Vous avez poke @' + username, { socket:socket.id });
          io.to(userSocket).emit('user_highlight', time, session.user.name);
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socket.id });
        });
      });
      // Lancer un dé
      socket.on('roll', function(pattern) {
        if(!pattern) {
          addBotMessage(io, session.user.name + " lance 1d6 et obtient " + getRandomInt({ min:1, max:6 }), { storage:true });
          return;
        }

        var dice   = pattern.split('d');
        var number = parseInt(dice[0], 10);
        var sides  = parseInt(dice[1], 10);
        var result = [];

        number = (number > 0 && number <= 10)  ? number : 1 ;
        sides  = (sides  > 0 && sides  <= 100) ? sides  : 6 ;

        for(var i = 0; i < number; i++) {
          result.push(getRandomInt({ min:1, max:sides }));
        }

        var message = session.user.name + " lance " + number + "d" + sides + " et obtient " + result.toString();
        addBotMessage(io, message, { storage:true });
      });
      socket.on('rolluser', function(username) {
        users.getUserSocket(username)
        .then(function() {
          var message, storage;
          var lucky = getRandomInt({ emax:200 });
          switch(lucky) {
            case 0:
              message = 'Mouhahaha :evil:';
              storage = { storage:true };
              banUser(io, socket.id, username);
              break;
            case 199:
              message = 'Dommage, le trolleur trollé :D';
              storage = { storage:true };
              banUser(io, socket.id, session.user.name);
              break;
            default:
              message = '... la tentative a échoué, peut-être une autre fois :3';
              break;
          }
          addBotMessage(io, "Quelqu'un a tenté un roll " + username + "...", storage);
          return Promise.delay({ message:message, storage:storage }, 50);
        })
        .then(function(result) {
          addBotMessage(io, result.message, result.storage);
        })
        .catch(function() {
          addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socket.id });
        });
      });
    })
    .catch(AlreadyConnectedError, function() {
      Promise.delay(500).then(function() {
        io.to(socket.id).emit('already_connected');
      });
    })
    .catch(UserBannedError, function() {
      Promise.delay(500).then(function() {
        io.to(socket.id).emit('user_banned');
      });
    });
  });
  // Envoi du premier Heartbeat
  setTimeout(sendHeartbeat, heartbeatInterval);
};

var addMessage = function(io, user, message) {
  var time = moment().tz('Europe/Paris').format(dateFormat);
  var data = { time:time, user:user, message:message };
  messages.add(time, user.name, message)
  .then(function(id) {
    data.id = id;
    io.emit('message', data);
  });
};

var addBotMessage = function(io, message, options) {
  var time = moment().tz('Europe/Paris').format(dateFormat);
  var data = { type:'message-bot', time:time, message:message };
  if(!options) {
    io.emit('message', data);
    return;
  }
  if(options.storage)
    messages.add(time, null, message);
  if(options.socket)
    io.to(options.socket).emit('message', data);
  else
    io.emit('message', data);
};

var banUser = function(io, socketid, username) {
  users.getUserSocket(username)
  .then(function(userSocket) {
    users.ban(username);
    io.to(userSocket).emit('ban');
    addBotMessage(io, username + " a été kick du chat", { storage:true });
  })
  .catch(function() {
    addBotMessage(io, '(' + username + ') utilisateur introuvable...', { socket:socketid });
  });
}

function chatbotSpeech(io, username) {
  var messages = sentence.get();
  var index = getRandomInt({ emax:messages.length });
  addBotMessage(io, messages[index].replace('%user', username), { storage:true });
}

/*
 * Génére un nombre aléatoire selon le nombre de faces
 * Les valeurs min et max sont incluses [1-sides]
 */
function rollDice(sides) {
  return Math.floor(Math.random() * (sides - 1 + 1)) + 1;
}

/*
 * Génère un entier aléatoire
 *
 * Ranges possibles :
 * ----------------------------
 * { max:x }         // [0-x]
 * { min:x, max:y }  // [x-y]
 * { min:x, emax:y } // [x-y[
 * { emax:x }        // [0-x[
 * ----------------------------
 */
function getRandomInt(range) {
  if(!range.min) range.min = 0;
  if(range.max) // Inclusive max
    return Math.floor(Math.random() * (range.max - range.min + 1)) + range.min;
  else if(range.emax) // Exclusive max
    return Math.floor(Math.random() * (range.emax - range.min)) + range.min;
}

// Déclaration des erreurs
function AlreadyConnectedError() {}
function UserBannedError() {}

// Déclaration des prototypes
AlreadyConnectedError.prototype = Object.create(Error.prototype);
UserBannedError.prototype = Object.create(Error.prototype);

module.exports = socket;<|endoftext|>


<commit_before>/**
 * @author       Greg McLean <GregDevProjects>
 * @copyright    2021 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * Interpolates two given Vectors and returns a new Vector between them.
 *
 * Does not modify either of the passed Vectors.
 *
 * @function Phaser.Math.LinearXY
 * @since 3.6.0
 *
 * @param {Phaser.Math.Vector2} vector1 - Starting vector
 * @param {Phaser.Math.Vector2} vector2 - Ending vector
 * @param {number} [t=0] - The percentage between vector1 and vector2 to return, represented as a number between 0 and 1.
 *
 * @return {Phaser.Math.Vector2} The step t% of the way between vector1 and vector2.
 */
var LinearXY = function (vector1, vector2, t)
{
    if (t === undefined)
    {
        t = 0;
    }
    return vector1.clone().lerp(vector2, t);
};

module.exports = LinearXY;
<commit_msg>Correct version number<commit_after>/**
 * @author       Greg McLean <GregDevProjects>
 * @copyright    2021 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

/**
 * Interpolates two given Vectors and returns a new Vector between them.
 *
 * Does not modify either of the passed Vectors.
 *
 * @function Phaser.Math.LinearXY
 * @since 3.60.0
 *
 * @param {Phaser.Math.Vector2} vector1 - Starting vector
 * @param {Phaser.Math.Vector2} vector2 - Ending vector
 * @param {number} [t=0] - The percentage between vector1 and vector2 to return, represented as a number between 0 and 1.
 *
 * @return {Phaser.Math.Vector2} The step t% of the way between vector1 and vector2.
 */
var LinearXY = function (vector1, vector2, t)
{
    if (t === undefined) { t = 0; }

    return vector1.clone().lerp(vector2, t);
};

module.exports = LinearXY;
<|endoftext|>


<commit_before>var liveaddress = angular.module('liveaddress', []);

liveaddress.directive('liveaddress', ['$http', '$q', function($http, $q){
  return {
    restrict: 'A',
    templateUrl: 'partial.html',
    replace: true,
    scope: {
      token: '=',
      geocoded: '=?ngModel',
      inputClass: '=?'
    },
    link: function(scope, element, attrs){
      var canceler;

      scope.suggestions = [];
      scope.current = 0;
      scope.geocoded = false;

      scope.$watch('address', function(newAddress, oldAddress){
        if (canceler) {
          canceler.resolve();
        }

        if (scope.suggestions && scope.suggestions[scope.current] && scope.suggestions[scope.current].text == newAddress) {
          return;
        }

        if (!newAddress) {
          scope.suggestions = [];
          scope.current = 0;
          return;
        }

        canceler = $q.defer();

        $http({
          method: 'GET',
          url: 'https://autocomplete-api.smartystreets.com/suggest',
          params: {
            'auth-id': scope.token,
            'prefix': newAddress
          },
          timeout: canceler
        }).success(function(data, status, headers, config){
          console.log(data.suggestions);
          scope.suggestions = data.suggestions;
          scope.current = 0;
        }).error(function(data, status, headers, config){
          scope.suggestions = [];
          scope.current = 0;
        });
      });

      scope.$watch('current', function(current, lastCurrent){
        if (scope.suggestions && scope.suggestions[current] && (current || current != lastCurrent)) {
          scope.address = scope.suggestions[scope.current].text;
        }
      });

      scope.handleKeydown = function(e){
        if (e.which == 38) {
          //up
          if (scope.current == 0) {
            scope.current = scope.suggestions.length-1;
          } else {
            scope.current--;
          }
        } else if (e.which == 40) {
          //down
          if (scope.current == scope.suggestions.length-1) {
            scope.current = 0;
          } else {
            scope.current++;
          }
        } else {
          return;
        }
        e.preventDefault();
        return false;
      };

      scope.select = function(item){
        scope.current = item;
        scope.geocoded = scope.suggestions[scope.current] || false;
      };

      var ignoreBlur = false;

      scope.handleBlur = function(){
        if (ignoreBlur) {
          ignoreBlur = false;
        } else {
          if (scope.suggestions[scope.current]) {
            scope.geocoded = scope.suggestions[scope.current];
            scope.address = scope.suggestions[scope.current].text;
          } else {
            scope.geocoded = false;
          }
        }
      };

      scope.ignoreNextBlur = function(){
        ignoreBlur = true;
      };
    }
  };
}]);<commit_msg>remove console log<commit_after>var liveaddress = angular.module('liveaddress', []);

liveaddress.directive('liveaddress', ['$http', '$q', function($http, $q){
  return {
    restrict: 'A',
    templateUrl: 'partial.html',
    replace: true,
    scope: {
      token: '=',
      geocoded: '=?ngModel',
      inputClass: '=?'
    },
    link: function(scope, element, attrs){
      var canceler;

      scope.suggestions = [];
      scope.current = 0;
      scope.geocoded = false;

      scope.$watch('address', function(newAddress, oldAddress){
        if (canceler) {
          canceler.resolve();
        }

        if (scope.suggestions && scope.suggestions[scope.current] && scope.suggestions[scope.current].text == newAddress) {
          return;
        }

        if (!newAddress) {
          scope.suggestions = [];
          scope.current = 0;
          return;
        }

        canceler = $q.defer();

        $http({
          method: 'GET',
          url: 'https://autocomplete-api.smartystreets.com/suggest',
          params: {
            'auth-id': scope.token,
            'prefix': newAddress
          },
          timeout: canceler
        }).success(function(data, status, headers, config){
          scope.suggestions = data.suggestions;
          scope.current = 0;
        }).error(function(data, status, headers, config){
          scope.suggestions = [];
          scope.current = 0;
        });
      });

      scope.$watch('current', function(current, lastCurrent){
        if (scope.suggestions && scope.suggestions[current] && (current || current != lastCurrent)) {
          scope.address = scope.suggestions[scope.current].text;
        }
      });

      scope.handleKeydown = function(e){
        if (e.which == 38) {
          //up
          if (scope.current == 0) {
            scope.current = scope.suggestions.length-1;
          } else {
            scope.current--;
          }
        } else if (e.which == 40) {
          //down
          if (scope.current == scope.suggestions.length-1) {
            scope.current = 0;
          } else {
            scope.current++;
          }
        } else {
          return;
        }
        e.preventDefault();
        return false;
      };

      scope.select = function(item){
        scope.current = item;
        scope.geocoded = scope.suggestions[scope.current] || false;
      };

      var ignoreBlur = false;

      scope.handleBlur = function(){
        if (ignoreBlur) {
          ignoreBlur = false;
        } else {
          if (scope.suggestions[scope.current]) {
            scope.geocoded = scope.suggestions[scope.current];
            scope.address = scope.suggestions[scope.current].text;
          } else {
            scope.geocoded = false;
          }
        }
      };

      scope.ignoreNextBlur = function(){
        ignoreBlur = true;
      };
    }
  };
}]);<|endoftext|>


<commit_before>'use strict';

var Bind = require('github/jillix/bind');
var Events = require('github/jillix/events');

// a recursive function until a module
function tryNextModule (miids, index, container, dataContext, self) {
    // stop when no more modules
    if (!miids[index]) {
        return self.emit("ready");
    }
    // otherwise go and try to load the module
    M(container, miids[index], dataContext, function(err, module) {
        if (err) {
            tryNextModule(miids, ++index, container, dataContext);
        }
    });
}

module.exports = function (config, dataContext) {

    var self = this;
    var target = self && self.dom ? self.dom : document;

    Events.call(self, config);

    function loadModules (dataCtx) {
        // load modules
        for (var selector in config.modules) {
            if (!config.modules.hasOwnProperty(selector)) return;

            var modules = config.modules[selector];
            var container = target.querySelector('#' + selector);

            if (typeof modules === 'string') {
                M(container, modules, dataCtx, function () {
                    self.emit("ready");
                });
            }
            // assuming an array
            else if (modules.length) {
                // start finding the first accessible miid in the module array
                tryNextModule(modules, 0, container, dataCtx, self);
            }
        }

        if (!config.modules || !config.modules.length) {
            self.emit("ready");
        }
    }

    function removeLoadedModules () {

        for (var selector in config.modules) {
            if (!config.modules.hasOwnProperty(selector)) return;

            var container = target.querySelector("#" + config.modules[selector]);
            $(container).remove();
        }
    }

    loadModules(dataContext);

    // set document title
    if (config.title) {
        document.title = config.title;
    }

/***********************************************/
/* Module functions TODO find another solution */
/***********************************************/

    // TODO Only two arguments (arrays):
    //      1. Dynamic arguments
    //      2. Configured arguments (from descriptor)
    this.reload = function (dataCtx) {
        removeLoadedModules();
        loadModules(dataCtx);
    };

    this.show = function () {
        $(self.dom).parent().show();
    }

    this.hide = function () {
        $(self.dom).parent().hide();
    }
/***********************************************/

    // run the binds
    for (var i in config.binds) {
        if (!config.hasOwnProperty(i)) return;
        Bind.call(self, config.binds[i]);
    }

    if (typeof window[config.onInitEnd] === 'function') {
        window[config.onInitEnd].apply(self);
    }
};
<commit_msg>Fixed wrong for loop skipping of when non-own properties<commit_after>'use strict';

var Bind = require('github/jillix/bind');
var Events = require('github/jillix/events');

// a recursive function until a module
function tryNextModule (miids, index, container, dataContext, self) {
    // stop when no more modules
    if (!miids[index]) {
        return self.emit("ready");
    }
    // otherwise go and try to load the module
    M(container, miids[index], dataContext, function(err, module) {
        if (err) {
            tryNextModule(miids, ++index, container, dataContext);
        }
    });
}

module.exports = function (config, dataContext) {

    var self = this;
    var target = self && self.dom ? self.dom : document;

    Events.call(self, config);

    function loadModules (dataCtx) {
        // load modules
        for (var selector in config.modules) {
            if (!config.modules.hasOwnProperty(selector)) continue;

            var modules = config.modules[selector];
            var container = target.querySelector('#' + selector);

            if (typeof modules === 'string') {
                M(container, modules, dataCtx, function () {
                    self.emit("ready");
                });
            }
            // assuming an array
            else if (modules.length) {
                // start finding the first accessible miid in the module array
                tryNextModule(modules, 0, container, dataCtx, self);
            }
        }

        if (!config.modules || !config.modules.length) {
            self.emit("ready");
        }
    }

    function removeLoadedModules () {

        for (var selector in config.modules) {
            if (!config.modules.hasOwnProperty(selector)) continue;

            var container = target.querySelector("#" + config.modules[selector]);
            $(container).remove();
        }
    }

    loadModules(dataContext);

    // set document title
    if (config.title) {
        document.title = config.title;
    }

/***********************************************/
/* Module functions TODO find another solution */
/***********************************************/

    // TODO Only two arguments (arrays):
    //      1. Dynamic arguments
    //      2. Configured arguments (from descriptor)
    this.reload = function (dataCtx) {
        removeLoadedModules();
        loadModules(dataCtx);
    };

    this.show = function () {
        $(self.dom).parent().show();
    }

    this.hide = function () {
        $(self.dom).parent().hide();
    }
/***********************************************/

    // run the binds
    if (config.binds) {
        for (var i = 0; i < config.binds.length; ++i) {
            Bind.call(self, config.binds[i]);
        }
    }

    if (typeof window[config.onInitEnd] === 'function') {
        window[config.onInitEnd].apply(self);
    }
};

<|endoftext|>


<commit_before>"use strict";
var Gpio = require('pigpio').Gpio;
function LedRun(pin) {
    this._pin = pin;
    this._led = new Gpio(pin, {mode: Gpio.OUTPUT});
    this._interval = null;
}
LedRun.prototype.start = function(blinkInterval) {
    var that = this;
    this._interval = setInterval(function () {
        that._led
        that._led.digitalWrite(that._led.digitalRead() ^ 1); // 1 = on, 0 = off :)
    }, blinkInterval);
};

LedRun.prototype.stop = function() {
    this._led.digitalWrite(0);
    clearInterval(this._interval);
};

module.exports = LedRun;
<commit_msg>Fix ledrun.js<commit_after>"use strict";
var Gpio = require('pigpio').Gpio;
function LedRun(pin) {
    this._pin = pin;
    this._led = new Gpio(pin, {mode: Gpio.OUTPUT});
    this._interval = null;
}
LedRun.prototype.start = function(blinkInterval) {
    var that = this;
    this._interval = setInterval(function () {
        that._led.digitalWrite(that._led.digitalRead() ^ 1); // 1 = on, 0 = off :)
    }, blinkInterval);
};

LedRun.prototype.stop = function() {
    this._led.digitalWrite(0);
    clearInterval(this._interval);
};

module.exports = LedRun;
<|endoftext|>


<commit_before>function level4(){
	this.dataHandler = new DataHandler();
	this.data = new Database();
	this.data.addSet("t");
	this.data.addSet("p");
	this.data.addSet("v");
	walls = new WallHandler([[P(10,75), P(540,75), P(540,440), P(10,440)]])
	this.wallV = 1.5;
	this.introText = "I AM LEVEL 4";
	this.outroText = "YOUR TRAINING IS NOW COMPLETE.  \nYOU CAN LEARN NOTHING MORE FROM ME.";
	this.updateListeners = {};//{run:this.updateRun, compress:this.updateCompress, expand:this.updateExpand, pause:this.updatePause};
	this.dataListeners = {};//{run:this.dataRun, pause:this.dataPause};
	this.buttons = {};
	this.sliders = {};
	this.savedVals = {};
	this.g = .002;
	var heaterX = 200;
	var heaterY = 400;
	var heaterWidth = 50;
	var heaterHeight = 30;
	//this.heater = new Heater(heaterX, heaterY, heaterWidth, heaterHeight, 50, 300)
	this.weight = new Weight(250,75,.5,10,100);
	//walls = new WallHandler([[P(100,100), P(300,100),P(300,300),P(100,300)]])
	//walls = new WallHandler([[P(10,10), P(540,10), P(540,440), P(10,440)]])
	walls.setup();
	this.minY = 25;
	this.maxY = walls.pts[0][2].y-75;
	this.counter = 0;
	collide.setup();
}

level4.prototype = {
	init: function(){
		this.addDots();
		this.drawHeader();
		this.startIntro();
	},
	startIntro: function(){
		saveVals(this);
		cleanDash(this);
		emptyListener(this, "update");
		emptyListener(this, "data");
		this.drawDashStart();
		this.textBox = new MainTextBox(this.introText);
	},
	startSim: function(){
		this.textBox.remove();
		cleanDash(this);
		this.drawDashRun();
		addListener(this, "update", "run", this.updateRun);
		addListener(this, "data", "run", this.dataRun);
		this.pVSv = new Graph(575,8+header.height,300,300, "Volume", "Pressure", "#5a8a92");
		this.tVSv = new Graph(575,8+header.height+30+this.pVSv.height, 300, 300,"Volume", "Temperature", "#ca1a14");
		this.fTurn=0;
		this.movedWallsLast = new Boolean();
		this.movedWallsLast = false;
	},
	startOutro: function(){
		saveVals(this);
		cleanDash(this);
		emptyListener(this, "update");
		emptyListener(this, "data");
		this.drawDashOut();
		this.textBox = new MainTextBox(this.outroText);
	},
	update: function(){
		for (var updateListener in this.updateListeners){
			this.updateListeners[updateListener].apply(curLevel);
		}
	},
	addData: function(){
		for (var dataListener in this.dataListeners){
			this.dataListeners[dataListener].apply(curLevel);
		}
	},
	updateRun: function(){
		move();
		this.addWeightForce();
		this.moveWalls();
		this.checkDotHits(); //okay, I think move walls should go up here so if we're at max, wallV gets set to zero.  I dunno though.  Figger it out.
		this.checkWallHits();
		this.drawRun();
	},
	addWeightForce: function(){
		this.counter++;
		this.wallV += this.g;
	},
	drawRun: function(){
		draw.clear();
		draw.dots();
		draw.walls(walls);
		//draw.fillPts(walls.pts[1], this.heater.col);
		draw.fillPts(this.weight.pts, this.weight.col);
	},
	checkDotHits: function(){
		collide.check();
	},
	onDotImpact: function(a, b){
		collide.impactStd(a, b);
	},
	checkWallHits: function(){
		walls.check();
	},
	onWallImpact: function(dot, line, wallUV, perpV){
		if(line[0]==0 && line[1]==0){
			var pt = walls.pts[line[0]][line[1]];
			var dotVo = dot.v.dy;
			var wallVo = this.wallV;
			if (pt.y==this.yMin){
				dot.v.dy = -dotVo;
			}else if (pt.y==this.yMax){
				this.wallV = (wallVo*(this.weight.weight-dot.m)+2*dot.m*dotVo)/(this.weight.weight+dot.m);
			} else{
				dot.v.dy = (dotVo*(dot.m-this.weight.weight)+2*this.weight.weight*wallVo)/(dot.m+this.weight.weight);
				this.wallV = (wallVo*(this.weight.weight-dot.m)+2*dot.m*dotVo)/(this.weight.weight+dot.m);
				dot.y = pt.y+dot.r;
			}
		}else{
			walls.impactStd(dot, wallUV, perpV)
		}
		this.fTurn += dot.m*perpV;

		//if(line[0]==1){
		//	var foo = hitHeater(dot, perpV, this.heater.t)
		//}

	},
	addDots: function(){
		addSpecies(["spc1", "spc2", "spc3"]);
		//populate("spc1", 15, 15, myCanvas.width-400, myCanvas.height-150, 200, 4);
		//populate("spc2", 75, 75, myCanvas.width-400, myCanvas.height-150, 20, 4);
		//populate("spc3", 15, 15, myCanvas.width-400, myCanvas.height-150, 400, 4);
		//populate("spc1", 20, 100, 500, 300, 500, 300);
		//populate("spc3", 20, 100, 500, 300, 500, 300);
		//populate("spc2", 20, 100, 500, 300, 1, 300);
	},
	dataRun: function(){
		this.data.p.push(this.dataHandler.pressure(this.fTurn));
		this.data.t.push(this.dataHandler.temp());
		this.data.v.push(this.dataHandler.volPolyWall());
		this.fTurn = 0;
		this.pVSv.plotData(this.data.v, this.data.p);
		this.tVSv.plotData(this.data.v, this.data.t);
	},
	drawHeader: function(){
		this.header = makeHeader("THE PATH TO THE BEYOND");
	},
	vol: function(){
		return walls.area(0);// - walls.area(1);
	},
	drawDashStart: function(){
		var buttonWidth = 150;
		var name = "toSim"
		this.buttons[name] = (new Button(myCanvas.width/2 - buttonWidth/2, 40, buttonWidth, 50,"To the simulation!","#ceae6a", "#b3975c"));
		this.buttons[name].addReleaseListener(this.startSim, this);
	},
	drawDashOut: function(){
		var buttonWidth = 150;
		var name = "toSim"
		this.buttons[name] = new Button(myCanvas.width/2 - buttonWidth/2, 40, buttonWidth, 50,"To the simulation!","#ceae6a", "#b3975c");
		this.buttons[name].addReleaseListener(this.startSim, this);
	},
	drawDashRun: function(){
		var toIntroName = "toIntro";
		this.buttons[toIntroName] = new Button(425,15,90,30,"To intro","#ceae6a", "#b3975c");
		this.buttons[toIntroName].addReleaseListener(this.startIntro, this);
		var toOutroName = "toOutro";
		this.buttons[toOutroName] = new Button(425,55,90,30,"To outro","#ceae6a", "#b3975c");
		this.buttons[toOutroName].addReleaseListener(this.startOutro, this);
		var tempSliderName = "temp";
		this.sliders[tempSliderName] = new Slider(this, tempSliderName, 150,20,"Temperature");
		this.sliders[tempSliderName].addDragListener(this.changeTemp,this);
		var weightSliderName = "weight";
		this.sliders[weightSliderName] = new Slider(this, weightSliderName, 240,20,"Weight");
		this.sliders[weightSliderName].addDragListener(this.changeWeight, this);
	},
	moveWalls: function(){
		var wall = walls.pts[0];
		var lastY = wall[0].y
		var unboundedY = lastY + this.wallV;
		var dyWeight = this.wallV;
		if(unboundedY>this.maxY || unboundedY<this.minY){
			var boundedY = Math.max(this.minY, Math.min(this.maxY, unboundedY));
			wall[0].y = boundedY;
			wall[1].y = boundedY;
			wall[wall.length-1].y = boundedY;
			var deltaPEOverM = this.g*(unboundedY-boundedY);
			if(this.wallV>0){
				this.wallV = -Math.sqrt(this.wallV*this.wallV - 2*deltaPEOverM);
			} else {
				this.wallV = Math.sqrt(this.wallV*this.wallV - 2*deltaPEOverM);
			}
			dyWeight = boundedY - lastY;
		}else{
			wall[0].y+=this.wallV;
			wall[1].y+=this.wallV;
			wall[wall.length-1].y+=this.wallV;
		}
		this.weight.move(V(0,dyWeight));
		walls.setupWall(0);
	},
	changeTemp: function(sliderVal){
		this.heater.changeTemp(sliderVal);
	},
	changeWeight: function(sliderVal){
		this.weight.changeWeight(sliderVal);
	},

}
<commit_msg>wall takes full size step when hits bumper<commit_after>function level4(){
	this.dataHandler = new DataHandler();
	this.data = new Database();
	this.data.addSet("t");
	this.data.addSet("p");
	this.data.addSet("v");
	walls = new WallHandler([[P(10,75), P(540,75), P(540,440), P(10,440)]])
	this.wallV = 1.5;
	this.introText = "I AM LEVEL 4";
	this.outroText = "YOUR TRAINING IS NOW COMPLETE.  \nYOU CAN LEARN NOTHING MORE FROM ME.";
	this.updateListeners = {};//{run:this.updateRun, compress:this.updateCompress, expand:this.updateExpand, pause:this.updatePause};
	this.dataListeners = {};//{run:this.dataRun, pause:this.dataPause};
	this.buttons = {};
	this.sliders = {};
	this.savedVals = {};
	this.g = .002;
	var heaterX = 200;
	var heaterY = 400;
	var heaterWidth = 50;
	var heaterHeight = 30;
	//this.heater = new Heater(heaterX, heaterY, heaterWidth, heaterHeight, 50, 300)
	this.weight = new Weight(250,75,.5,10,100);
	//walls = new WallHandler([[P(100,100), P(300,100),P(300,300),P(100,300)]])
	//walls = new WallHandler([[P(10,10), P(540,10), P(540,440), P(10,440)]])
	walls.setup();
	this.minY = 25;
	this.maxY = walls.pts[0][2].y-75;
	collide.setup();
}

level4.prototype = {
	init: function(){
		this.addDots();
		this.drawHeader();
		this.startIntro();
	},
	startIntro: function(){
		saveVals(this);
		cleanDash(this);
		emptyListener(this, "update");
		emptyListener(this, "data");
		this.drawDashStart();
		this.textBox = new MainTextBox(this.introText);
	},
	startSim: function(){
		this.textBox.remove();
		cleanDash(this);
		this.drawDashRun();
		addListener(this, "update", "run", this.updateRun);
		addListener(this, "data", "run", this.dataRun);
		this.pVSv = new Graph(575,8+header.height,300,300, "Volume", "Pressure", "#5a8a92");
		this.tVSv = new Graph(575,8+header.height+30+this.pVSv.height, 300, 300,"Volume", "Temperature", "#ca1a14");
		this.fTurn=0;
		this.movedWallsLast = new Boolean();
		this.movedWallsLast = false;
	},
	startOutro: function(){
		saveVals(this);
		cleanDash(this);
		emptyListener(this, "update");
		emptyListener(this, "data");
		this.drawDashOut();
		this.textBox = new MainTextBox(this.outroText);
	},
	update: function(){
		for (var updateListener in this.updateListeners){
			this.updateListeners[updateListener].apply(curLevel);
		}
	},
	addData: function(){
		for (var dataListener in this.dataListeners){
			this.dataListeners[dataListener].apply(curLevel);
		}
	},
	updateRun: function(){
		move();
		this.addWeightForce();
		this.moveWalls();
		this.checkDotHits(); //okay, I think move walls should go up here so if we're at max, wallV gets set to zero.  I dunno though.  Figger it out.
		this.checkWallHits();
		this.drawRun();
	},
	addWeightForce: function(){
		this.counter++;
		this.wallV += this.g;
	},
	drawRun: function(){
		draw.clear();
		draw.dots();
		draw.walls(walls);
		//draw.fillPts(walls.pts[1], this.heater.col);
		draw.fillPts(this.weight.pts, this.weight.col);
	},
	checkDotHits: function(){
		collide.check();
	},
	onDotImpact: function(a, b){
		collide.impactStd(a, b);
	},
	checkWallHits: function(){
		walls.check();
	},
	onWallImpact: function(dot, line, wallUV, perpV){
		if(line[0]==0 && line[1]==0){
			var pt = walls.pts[line[0]][line[1]];
			var dotVo = dot.v.dy;
			var wallVo = this.wallV;
			if (pt.y==this.yMin){
				dot.v.dy = -dotVo;
			}else if (pt.y==this.yMax){
				this.wallV = (wallVo*(this.weight.weight-dot.m)+2*dot.m*dotVo)/(this.weight.weight+dot.m);
			} else{
				dot.v.dy = (dotVo*(dot.m-this.weight.weight)+2*this.weight.weight*wallVo)/(dot.m+this.weight.weight);
				this.wallV = (wallVo*(this.weight.weight-dot.m)+2*dot.m*dotVo)/(this.weight.weight+dot.m);
				dot.y = pt.y+dot.r;
			}
		}else{
			walls.impactStd(dot, wallUV, perpV)
		}
		this.fTurn += dot.m*perpV;
	},
	addDots: function(){
		addSpecies(["spc1", "spc2", "spc3"]);
		//populate("spc1", 15, 15, myCanvas.width-400, myCanvas.height-150, 200, 4);
		//populate("spc2", 75, 75, myCanvas.width-400, myCanvas.height-150, 20, 4);
		//populate("spc3", 15, 15, myCanvas.width-400, myCanvas.height-150, 400, 4);
		//populate("spc1", 20, 100, 500, 300, 500, 300);
		//populate("spc3", 20, 100, 500, 300, 500, 300);
		//populate("spc2", 20, 100, 500, 300, 1, 300);
	},
	dataRun: function(){
		this.data.p.push(this.dataHandler.pressure(this.fTurn));
		this.data.t.push(this.dataHandler.temp());
		this.data.v.push(this.dataHandler.volPolyWall());
		this.fTurn = 0;
		this.pVSv.plotData(this.data.v, this.data.p);
		this.tVSv.plotData(this.data.v, this.data.t);
	},
	drawHeader: function(){
		this.header = makeHeader("THE PATH TO THE BEYOND");
	},
	vol: function(){
		return walls.area(0);// - walls.area(1);
	},
	drawDashStart: function(){
		var buttonWidth = 150;
		var name = "toSim"
		this.buttons[name] = (new Button(myCanvas.width/2 - buttonWidth/2, 40, buttonWidth, 50,"To the simulation!","#ceae6a", "#b3975c"));
		this.buttons[name].addReleaseListener(this.startSim, this);
	},
	drawDashOut: function(){
		var buttonWidth = 150;
		var name = "toSim"
		this.buttons[name] = new Button(myCanvas.width/2 - buttonWidth/2, 40, buttonWidth, 50,"To the simulation!","#ceae6a", "#b3975c");
		this.buttons[name].addReleaseListener(this.startSim, this);
	},
	drawDashRun: function(){
		var toIntroName = "toIntro";
		this.buttons[toIntroName] = new Button(425,15,90,30,"To intro","#ceae6a", "#b3975c");
		this.buttons[toIntroName].addReleaseListener(this.startIntro, this);
		var toOutroName = "toOutro";
		this.buttons[toOutroName] = new Button(425,55,90,30,"To outro","#ceae6a", "#b3975c");
		this.buttons[toOutroName].addReleaseListener(this.startOutro, this);
		var tempSliderName = "temp";
		this.sliders[tempSliderName] = new Slider(this, tempSliderName, 150,20,"Temperature");
		this.sliders[tempSliderName].addDragListener(this.changeTemp,this);
		var weightSliderName = "weight";
		this.sliders[weightSliderName] = new Slider(this, weightSliderName, 240,20,"Weight");
		this.sliders[weightSliderName].addDragListener(this.changeWeight, this);
	},
	moveWalls: function(){
		var wall = walls.pts[0];
		var lastY = wall[0].y
		var unboundedY = lastY + this.wallV;
		var dyWeight = this.wallV;
		if(unboundedY>this.maxY || unboundedY<this.minY){
			var boundedY = Math.max(this.minY, Math.min(this.maxY, unboundedY));
			var nextY = 2*boundedY-unboundedY
			wall[0].y = nextY;
			wall[1].y = nextY;
			wall[wall.length-1].y = nextY;
			var deltaPEOverM = this.g*(unboundedY-nextY);
			if(this.wallV>0){
				this.wallV = -Math.sqrt(this.wallV*this.wallV - 2*deltaPEOverM);
			} else {
				this.wallV = Math.sqrt(this.wallV*this.wallV - 2*deltaPEOverM);
			}
			dyWeight = boundedY - lastY;
		}else{
			wall[0].y+=this.wallV;
			wall[1].y+=this.wallV;
			wall[wall.length-1].y+=this.wallV;
		}
		this.weight.move(V(0,dyWeight));
		walls.setupWall(0);
	},
	changeTemp: function(sliderVal){
		this.heater.changeTemp(sliderVal);
	},
	changeWeight: function(sliderVal){
		this.weight.changeWeight(sliderVal);
	},

}
<|endoftext|>