<commit_before>// @flow

import md5 from 'js-md5';

const logger = require('jitsi-meet-logger').getLogger(__filename);

/**
 * The name of the {@code localStorage} store where the app persists its values.
 */
const PERSISTED_STATE_NAME = 'jitsi-state';

/**
 * Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
 * {@code true}), we persist the entire subtree. If it's an {@code Object}, we
 * perist a filtered subtree based on the properties of the config object.
 */
declare type ElementConfig = boolean | Object;

/**
 * The type of the name-config pairs stored in {@code PersistenceRegistry}.
 */
declare type PersistencyConfigMap = { [name: string]: ElementConfig };

/**
 * A registry to allow features to register their redux store subtree to be
 * persisted and also handles the persistency calls too.
 */
class PersistenceRegistry {
    _checksum: string;

    _elements: PersistencyConfigMap = {};

    /**
     * Returns the persisted redux state. Takes the {@link #_elements} into
     * account as we may have persisted something in the past that we don't want
     * to retreive anymore. The next {@link #persistState} will remove such
     * values.
     *
     * @returns {Object}
     */
    getPersistedState() {
        let filteredPersistedState = {};

        // localStorage key per feature
        for (const subtreeName of Object.keys(this._elements)) {
            // Assumes that the persisted value is stored under the same key as
            // the feature's redux state name.
            // TODO We'll need to introduce functions later that can control the
            // persist key's name. Similar to control serialization and
            // deserialization. But that should be a straightforward change.
            const persistedSubtree
                = this._getPersistedSubtree(
                    subtreeName,
                    this._elements[subtreeName]);

            if (persistedSubtree !== undefined) {
                filteredPersistedState[subtreeName] = persistedSubtree;
            }
        }

        // legacy
        if (Object.keys(filteredPersistedState).length === 0) {
            const { localStorage } = window;
            let persistedState = localStorage.getItem(PERSISTED_STATE_NAME);

            if (persistedState) {
                try {
                    persistedState = JSON.parse(persistedState);
                } catch (error) {
                    logger.error(
                        'Error parsing persisted state',
                        persistedState,
                        error);
                    persistedState = {};
                }

                filteredPersistedState = this._getFilteredState(persistedState);

                // Store into the new format and delete the old format so that
                // it's not used again.
                this.persistState(filteredPersistedState);
                localStorage.removeItem(PERSISTED_STATE_NAME);
            }
        }

        // Initialize the checksum.
        this._checksum = this._calculateChecksum(filteredPersistedState);

        logger.info('redux state rehydrated as', filteredPersistedState);

        return filteredPersistedState;
    }

    /**
     * Initiates a persist operation, but its execution will depend on the
     * current checksums (checks changes).
     *
     * @param {Object} state - The redux state.
     * @returns {void}
     */
    persistState(state: Object) {
        const filteredState = this._getFilteredState(state);
        const checksum = this._calculateChecksum(filteredState);

        if (checksum !== this._checksum) {
            for (const subtreeName of Object.keys(filteredState)) {
                try {
                    window.localStorage.setItem(
                        subtreeName,
                        JSON.stringify(filteredState[subtreeName]));
                } catch (error) {
                    logger.error(
                        'Error persisting redux subtree',
                        subtreeName,
                        filteredState[subtreeName],
                        error);
                }
            }
            logger.info(
                `redux state persisted. ${this._checksum} -> ${checksum}`);
            this._checksum = checksum;
        }
    }

    /**
     * Registers a new subtree config to be used for the persistency.
     *
     * @param {string} name - The name of the subtree the config belongs to.
     * @param {ElementConfig} config - The config {@code Object}, or
     * {@code boolean} if the entire subtree needs to be persisted.
     * @returns {void}
     */
    register(name: string, config?: ElementConfig = true) {
        this._elements[name] = config;
    }

    /**
     * Calculates the checksum of a specific state.
     *
     * @param {Object} state - The redux state to calculate the checksum of.
     * @private
     * @returns {string} The checksum of the specified {@code state}.
     */
    _calculateChecksum(state: Object) {
        try {
            return md5.hex(JSON.stringify(state) || '');
        } catch (error) {
            logger.error('Error calculating checksum for state', state, error);

            return '';
        }
    }

    /**
     * Prepares a filtered state from the actual or the persisted redux state,
     * based on this registry.
     *
     * @param {Object} state - The actual or persisted redux state.
     * @private
     * @returns {Object}
     */
    _getFilteredState(state: Object) {
        const filteredState = {};

        for (const name of Object.keys(this._elements)) {
            if (state[name]) {
                filteredState[name]
                    = this._getFilteredSubtree(
                        state[name],
                        this._elements[name]);
            }
        }

        return filteredState;
    }

    /**
     * Prepares a filtered subtree based on the config for persisting or for
     * retrieval.
     *
     * @param {Object} subtree - The redux state subtree.
     * @param {ElementConfig} subtreeConfig - The related config.
     * @private
     * @returns {Object}
     */
    _getFilteredSubtree(subtree, subtreeConfig) {
        let filteredSubtree;

        if (typeof subtreeConfig === 'object') {
            // Only a filtered subtree gets persisted as specified by
            // subtreeConfig.
            filteredSubtree = {};
            for (const persistedKey of Object.keys(subtree)) {
                if (subtreeConfig[persistedKey]) {
                    filteredSubtree[persistedKey] = subtree[persistedKey];
                }
            }
        } else if (subtreeConfig) {
            // Persist the entire subtree.
            filteredSubtree = subtree;
        }

        return filteredSubtree;
    }

    /**
     * Retreives a persisted subtree from the storage.
     *
     * @param {string} subtreeName - The name of the subtree.
     * @param {Object} subtreeConfig - The config of the subtree from
     * {@link #_elements}.
     * @private
     * @returns {Object}
     */
    _getPersistedSubtree(subtreeName, subtreeConfig) {
        let persistedSubtree = window.localStorage.getItem(subtreeName);

        if (persistedSubtree) {
            try {
                persistedSubtree = JSON.parse(persistedSubtree);

                const filteredSubtree
                    = this._getFilteredSubtree(persistedSubtree, subtreeConfig);

                if (filteredSubtree !== undefined) {
                    return filteredSubtree;
                }
            } catch (error) {
                logger.error(
                    'Error parsing persisted subtree',
                    subtreeName,
                    persistedSubtree,
                    error);
            }
        }

        return undefined;
    }
}

export default new PersistenceRegistry();
<commit_msg>Fix persistency to handle default values too (#3228)<commit_after>// @flow

import md5 from 'js-md5';

const logger = require('jitsi-meet-logger').getLogger(__filename);

/**
 * The name of the {@code localStorage} store where the app persists its values.
 */
const PERSISTED_STATE_NAME = 'jitsi-state';

/**
 * Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
 * {@code true}), we persist the entire subtree. If it's an {@code Object}, we
 * perist a filtered subtree based on the properties of the config object.
 */
declare type ElementConfig = boolean | Object;

/**
 * The type of the name-config pairs stored in {@code PersistenceRegistry}.
 */
declare type PersistencyConfigMap = { [name: string]: ElementConfig };

/**
 * A registry to allow features to register their redux store subtree to be
 * persisted and also handles the persistency calls too.
 */
class PersistenceRegistry {
    _checksum: string;
    _defaultStates: { [name: string ]: ?Object} = {};
    _elements: PersistencyConfigMap = {};

    /**
     * Returns the persisted redux state. Takes the {@link #_elements} into
     * account as we may have persisted something in the past that we don't want
     * to retreive anymore. The next {@link #persistState} will remove such
     * values.
     *
     * @returns {Object}
     */
    getPersistedState() {
        let filteredPersistedState = {};

        // localStorage key per feature
        for (const subtreeName of Object.keys(this._elements)) {
            // Assumes that the persisted value is stored under the same key as
            // the feature's redux state name.
            // TODO We'll need to introduce functions later that can control the
            // persist key's name. Similar to control serialization and
            // deserialization. But that should be a straightforward change.
            const persistedSubtree
                = this._getPersistedSubtree(
                    subtreeName,
                    this._elements[subtreeName],
                    this._defaultStates[subtreeName]);

            if (persistedSubtree !== undefined) {
                filteredPersistedState[subtreeName] = persistedSubtree;
            }
        }

        // legacy
        if (Object.keys(filteredPersistedState).length === 0) {
            const { localStorage } = window;
            let persistedState = localStorage.getItem(PERSISTED_STATE_NAME);

            if (persistedState) {
                try {
                    persistedState = JSON.parse(persistedState);
                } catch (error) {
                    logger.error(
                        'Error parsing persisted state',
                        persistedState,
                        error);
                    persistedState = {};
                }

                filteredPersistedState = this._getFilteredState(persistedState);

                // Store into the new format and delete the old format so that
                // it's not used again.
                this.persistState(filteredPersistedState);
                localStorage.removeItem(PERSISTED_STATE_NAME);
            }
        }

        // Initialize the checksum.
        this._checksum = this._calculateChecksum(filteredPersistedState);

        logger.info('redux state rehydrated as', filteredPersistedState);

        return filteredPersistedState;
    }

    /**
     * Initiates a persist operation, but its execution will depend on the
     * current checksums (checks changes).
     *
     * @param {Object} state - The redux state.
     * @returns {void}
     */
    persistState(state: Object) {
        const filteredState = this._getFilteredState(state);
        const checksum = this._calculateChecksum(filteredState);

        if (checksum !== this._checksum) {
            for (const subtreeName of Object.keys(filteredState)) {
                try {
                    window.localStorage.setItem(
                        subtreeName,
                        JSON.stringify(filteredState[subtreeName]));
                } catch (error) {
                    logger.error(
                        'Error persisting redux subtree',
                        subtreeName,
                        filteredState[subtreeName],
                        error);
                }
            }
            logger.info(
                `redux state persisted. ${this._checksum} -> ${checksum}`);
            this._checksum = checksum;
        }
    }

    /**
     * Registers a new subtree config to be used for the persistency.
     *
     * @param {string} name - The name of the subtree the config belongs to.
     * @param {ElementConfig} config - The config {@code Object}, or
     * {@code boolean} if the entire subtree needs to be persisted.
     * @param {Object} defaultState - The default state of the component. If
     * it's provided, the rehydrated state will be merged with it before it gets
     * pushed into Redux.
     * @returns {void}
     */
    register(
            name: string,
            config?: ElementConfig = true,
            defaultState?: Object) {
        this._elements[name] = config;
        this._defaultStates[name] = defaultState;
    }

    /**
     * Calculates the checksum of a specific state.
     *
     * @param {Object} state - The redux state to calculate the checksum of.
     * @private
     * @returns {string} The checksum of the specified {@code state}.
     */
    _calculateChecksum(state: Object) {
        try {
            return md5.hex(JSON.stringify(state) || '');
        } catch (error) {
            logger.error('Error calculating checksum for state', state, error);

            return '';
        }
    }

    /**
     * Prepares a filtered state from the actual or the persisted redux state,
     * based on this registry.
     *
     * @param {Object} state - The actual or persisted redux state.
     * @private
     * @returns {Object}
     */
    _getFilteredState(state: Object) {
        const filteredState = {};

        for (const name of Object.keys(this._elements)) {
            if (state[name]) {
                filteredState[name]
                    = this._getFilteredSubtree(
                        state[name],
                        this._elements[name]);
            }
        }

        return filteredState;
    }

    /**
     * Prepares a filtered subtree based on the config for persisting or for
     * retrieval.
     *
     * @param {Object} subtree - The redux state subtree.
     * @param {ElementConfig} subtreeConfig - The related config.
     * @private
     * @returns {Object}
     */
    _getFilteredSubtree(subtree, subtreeConfig) {
        let filteredSubtree;

        if (typeof subtreeConfig === 'object') {
            // Only a filtered subtree gets persisted as specified by
            // subtreeConfig.
            filteredSubtree = {};
            for (const persistedKey of Object.keys(subtree)) {
                if (subtreeConfig[persistedKey]) {
                    filteredSubtree[persistedKey] = subtree[persistedKey];
                }
            }
        } else if (subtreeConfig) {
            // Persist the entire subtree.
            filteredSubtree = subtree;
        }

        return filteredSubtree;
    }

    /**
     * Retreives a persisted subtree from the storage.
     *
     * @param {string} subtreeName - The name of the subtree.
     * @param {Object} subtreeConfig - The config of the subtree from
     * {@link #_elements}.
     * @param {Object} subtreeDefaults - The defaults of the persisted subtree.
     * @private
     * @returns {Object}
     */
    _getPersistedSubtree(subtreeName, subtreeConfig, subtreeDefaults) {
        let persistedSubtree = window.localStorage.getItem(subtreeName);

        if (persistedSubtree) {
            try {
                persistedSubtree = JSON.parse(persistedSubtree);

                const filteredSubtree
                    = this._getFilteredSubtree(persistedSubtree, subtreeConfig);

                if (filteredSubtree !== undefined) {
                    return this._mergeDefaults(
                        filteredSubtree, subtreeDefaults);
                }
            } catch (error) {
                logger.error(
                    'Error parsing persisted subtree',
                    subtreeName,
                    persistedSubtree,
                    error);
            }
        }

        return undefined;
    }

    /**
     * Merges the persisted subtree with its defaults before rehydrating the
     * values.
     *
     * @private
     * @param {Object} subtree - The Redux subtree.
     * @param {?Object} defaults - The defaults, if any.
     * @returns {Object}
     */
    _mergeDefaults(subtree: Object, defaults: ?Object) {
        if (!defaults) {
            return subtree;
        }

        // If the subtree is an array, we don't need to merge it with the
        // defaults, because if it has a value, it will overwrite it, and if
        // it's undefined, it won't be even returned, and Redux will natively
        // use the default values instead.
        if (!Array.isArray(subtree)) {
            return {
                ...defaults,
                ...subtree
            };
        }
    }
}

export default new PersistenceRegistry();
<|endoftext|>


<commit_before>goog.provide('hutmap.map.Sidebar');

goog.require('goog.dom');
goog.require('goog.object');
goog.require('goog.string');

goog.require('hutmap.consts');
goog.require('Wkt.Wkt');
goog.require('Wkt.gmap3');

/**
 * Allows manipulation of data displayed in the sidebar.
 *
 * @param {String} sidebarContentDivId The id of the sidebar content div.
 */
hutmap.map.Sidebar = function(sidebarContentDivId) {
  this.sidebarDiv = goog.dom.getElement(sidebarContentDivId);
};

/**
 * Updates the sidebar to display information on the given hut.
 *
 * @param {Object} hut the hut to display information about.
 */
hutmap.map.Sidebar.prototype.setHut = function(hut) {
  var elt = null;
  var textContent = '';
  // Set the title
  var name = goog.object.get(hut, 'name', 'Unknown hut');
  var hutUrl = goog.object.get(hut, 'hut_url', '');
  textContent = '' + name + '';
  elt = goog.dom.getElement('sidebar_title');
  elt.innerHTML = textContent;
  // Set the image
  var photoUrl = goog.object.get(hut, 'photo_url');
  if (photoUrl === '' || photoUrl == null)
    photoUrl = '/static/img/no-image-found.jpeg';
  textContent = '';
  elt = goog.dom.getElement('sidebar_img');
  elt.innerHTML = textContent;
  // Set the fee per person
  textContent = this.getFeeRange(hut, 'fee_person_min', 'fee_person_max');
  elt = goog.dom.getElement('sidebar_fee_person');
  goog.dom.setTextContent(elt, textContent);
  // Set the fee per hut
  textContent = this.getFeeRange(hut, 'fee_hut_min', 'fee_hut_max');
  elt = goog.dom.getElement('sidebar_fee_hut');
  goog.dom.setTextContent(elt, textContent);
  // Set the hut capacity
  textContent = this.getCapacityRange(hut);
  elt = goog.dom.getElement('sidebar_capacity_hut');
  goog.dom.setTextContent(elt, textContent);
  // Set the total capacity
  textContent = goog.object.get(hut, 'capacity_max', 'Unknown');
  elt = goog.dom.getElement('sidebar_total_capacity');
  goog.dom.setTextContent(elt, textContent);
  // Set the number of structures
  textContent = goog.object.get(hut, 'num_structures', 'Unknown');
  elt = goog.dom.getElement('sidebar_structures');
  goog.dom.setTextContent(elt, textContent);
  // Set whether reservations are accepted
  textContent = this.getBooleanAsYesNo(hut, 'reservations');
  elt = goog.dom.getElement('sidebar_reservations');
  goog.dom.setTextContent(elt, textContent);
  // Set the region
  var region = goog.object.get(hut, 'region', null);
  if (region == null) {
    textContent = 'Unknown';
  } else {
    textContent = region['region'];
  }
  elt = goog.dom.getElement('sidebar_region');
  goog.dom.setTextContent(elt, textContent);
  // Set the location
  var location = goog.object.get(hut, 'location', null);
  if (location == null) {
    textContent = 'Unknown';
  } else {
    var point = this.getWktPointAsLatLon(location);
    textContent = point.lat().toFixed(2) + ', ' + point.lng().toFixed(2);
  }
  elt = goog.dom.getElement('sidebar_location');
  goog.dom.setTextContent(elt, textContent);
  // Set the accuracy
  var locationAccuracy = goog.object.get(hut, 'location_accuracy', null);
  if (locationAccuracy == null) {
    textContent = 'Unknown';
  } else {
    // TODO: convert to text or visual
    textContent = locationAccuracy + '/' + hutmap.consts.g.maxAccuracy;
  }
  elt = goog.dom.getElement('sidebar_accuracy');
  goog.dom.setTextContent(elt, textContent);
  // Set the altitude
  var altitude = goog.object.get(hut, 'altitude', null);
  if (altitude == null) {
    textContent = 'Unknown';
  } else {
    textContent = altitude + 'm';
  }
  elt = goog.dom.getElement('sidebar_altitude');
  goog.dom.setTextContent(elt, textContent);
  // Set the access
  var access = goog.object.get(hut, 'access', null);
  if (access == null) {
    textContent = 'Unknown';
  } else {
    var accessString = hutmap.consts.g.access[parseInt(access)];
    textContent = goog.string.toTitleCase(accessString);
  }
  elt = goog.dom.getElement('sidebar_access');
  goog.dom.setTextContent(elt, textContent);
  var type = goog.object.get(hut, 'type', null);
  if (type == null) {
    textContent = 'Unknown';
  } else {
    textContent = type['name'];
  }
  elt = goog.dom.getElement('sidebar_type');
  goog.dom.setTextContent(elt, textContent);
  // Set the agency
  var agency = goog.object.get(hut, 'agency', null);
  if (agency == null) {
    textContent = 'Unknown';
  } else {
    textContent = '' +
      goog.string.truncate(agency['name'], 25) + '';
  }
  elt = goog.dom.getElement('sidebar_agency');
  elt.innerHTML = textContent;

  // Unhide the html
  var span = goog.dom.getElementsByTagNameAndClass('span', 'hidden', this.sidebarDiv)[0];
  span.style.display = 'block';
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getFeeRange = function(hut, feeMinProp, feeMaxProp) {
  var feeMin = hut[feeMinProp];
  var feeMax = hut[feeMaxProp];

  var textContent = '$' + feeMin + '-' + '$' + feeMax;
  if (feeMin == null || feeMax == null) {
    if (feeMin == null && feeMax == null)
      textContent = 'N/A';
    else if (feeMin == null)
      textContent = '$' + feeMax;
    else
      textContent = '$' + feeMin;
  } else if (feeMin === feeMax) {
    textContent = '$' + feeMin;
  }
  return textContent;
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getCapacityRange = function(hut) {
  var capMin = hut['capacity_hut_min'];
  var capMax = hut['capacity_hut_max'];

  var textContent = capMin + '-' + capMax;
  if (capMin == null || capMax == null) {
    if (capMin == null && capMax == null)
      textContent = 'Unknown';
    else if (capMin == null)
      textContent = capMax;
    else
      textContent = capMin;
  } else if (capMin == capMax) {
    textContent = capMin;
  }
  return textContent;
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getBooleanAsYesNo = function(bool) {
  if (bool)
    return 'yes';
  else
    return 'no';
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getWktPointAsLatLon = function(wkt_point) {
  var point = new Wkt.Wkt(wkt_point);
  return point.toObject().getPosition();
};

<commit_msg>Added hut url to sidebar display.<commit_after>goog.provide('hutmap.map.Sidebar');

goog.require('goog.dom');
goog.require('goog.object');
goog.require('goog.string');

goog.require('hutmap.consts');
goog.require('Wkt.Wkt');
goog.require('Wkt.gmap3');

/**
 * Allows manipulation of data displayed in the sidebar.
 *
 * @param {String} sidebarContentDivId The id of the sidebar content div.
 */
hutmap.map.Sidebar = function(sidebarContentDivId) {
  this.sidebarDiv = goog.dom.getElement(sidebarContentDivId);
};

/**
 * Updates the sidebar to display information on the given hut.
 *
 * @param {Object} hut the hut to display information about.
 */
hutmap.map.Sidebar.prototype.setHut = function(hut) {
  var elt = null;
  var textContent = '';
  // Set the title
  var name = goog.object.get(hut, 'name', 'Unknown hut');
  var hutUrl = goog.object.get(hut, 'hut_url', '');
  textContent = '' + name + '';
  elt = goog.dom.getElement('sidebar_title');
  elt.innerHTML = textContent;
  // Set the image
  var photoUrl = goog.object.get(hut, 'photo_url');
  if (photoUrl === '' || photoUrl == null)
    photoUrl = '/static/img/no-image-found.jpeg';
  textContent = '';
  elt = goog.dom.getElement('sidebar_img');
  elt.innerHTML = textContent;
  // Set the fee per person
  textContent = this.getFeeRange(hut, 'fee_person_min', 'fee_person_max');
  elt = goog.dom.getElement('sidebar_fee_person');
  goog.dom.setTextContent(elt, textContent);
  // Set the fee per hut
  textContent = this.getFeeRange(hut, 'fee_hut_min', 'fee_hut_max');
  elt = goog.dom.getElement('sidebar_fee_hut');
  goog.dom.setTextContent(elt, textContent);
  // Set the hut capacity
  textContent = this.getCapacityRange(hut);
  elt = goog.dom.getElement('sidebar_capacity_hut');
  goog.dom.setTextContent(elt, textContent);
  // Set the total capacity
  textContent = goog.object.get(hut, 'capacity_max', 'Unknown');
  elt = goog.dom.getElement('sidebar_total_capacity');
  goog.dom.setTextContent(elt, textContent);
  // Set the number of structures
  textContent = goog.object.get(hut, 'num_structures', 'Unknown');
  elt = goog.dom.getElement('sidebar_structures');
  goog.dom.setTextContent(elt, textContent);
  // Set whether reservations are accepted
  textContent = this.getBooleanAsYesNo(hut, 'reservations');
  elt = goog.dom.getElement('sidebar_reservations');
  goog.dom.setTextContent(elt, textContent);
  // Set the region
  var region = goog.object.get(hut, 'region', null);
  if (region == null) {
    textContent = 'Unknown';
  } else {
    textContent = region['region'];
  }
  elt = goog.dom.getElement('sidebar_region');
  goog.dom.setTextContent(elt, textContent);
  // Set the location
  var location = goog.object.get(hut, 'location', null);
  if (location == null) {
    textContent = 'Unknown';
  } else {
    var point = this.getWktPointAsLatLon(location);
    textContent = point.lat().toFixed(2) + ', ' + point.lng().toFixed(2);
  }
  elt = goog.dom.getElement('sidebar_location');
  goog.dom.setTextContent(elt, textContent);
  // Set the accuracy
  var locationAccuracy = goog.object.get(hut, 'location_accuracy', null);
  if (locationAccuracy == null) {
    textContent = 'Unknown';
  } else {
    // TODO: convert to text or visual
    textContent = locationAccuracy + '/' + hutmap.consts.g.maxAccuracy;
  }
  elt = goog.dom.getElement('sidebar_accuracy');
  goog.dom.setTextContent(elt, textContent);
  // Set the altitude
  var altitude = goog.object.get(hut, 'altitude', null);
  if (altitude == null) {
    textContent = 'Unknown';
  } else {
    textContent = altitude + 'm';
  }
  elt = goog.dom.getElement('sidebar_altitude');
  goog.dom.setTextContent(elt, textContent);
  // Set the access
  var access = goog.object.get(hut, 'access', null);
  if (access == null) {
    textContent = 'Unknown';
  } else {
    var accessString = hutmap.consts.g.access[parseInt(access)];
    textContent = goog.string.toTitleCase(accessString);
  }
  elt = goog.dom.getElement('sidebar_access');
  goog.dom.setTextContent(elt, textContent);
  var type = goog.object.get(hut, 'type', null);
  if (type == null) {
    textContent = 'Unknown';
  } else {
    textContent = type['name'];
  }
  elt = goog.dom.getElement('sidebar_type');
  goog.dom.setTextContent(elt, textContent);
  // Set the agency
  var agency = goog.object.get(hut, 'agency', null);
  if (agency == null) {
    textContent = 'Unknown';
  } else {
    textContent = '' +
      goog.string.truncate(agency['name'], 20) + '';
  }
  elt = goog.dom.getElement('sidebar_agency');
  elt.innerHTML = textContent;
  // Set the url
  var url = goog.object.get(hut, 'hut_url', null);
  if (url == null) {
    textContent = 'Unknown';
  } else {
    textContent = '' +
      goog.string.truncate(url, 20) + '';
  }
  elt = goog.dom.getElement('sidebar_hut_url');
  elt.innerHTML = textContent;

  // Unhide the html
  var span = goog.dom.getElementsByTagNameAndClass('span', 'hidden', this.sidebarDiv)[0];
  span.style.display = 'block';
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getFeeRange = function(hut, feeMinProp, feeMaxProp) {
  var feeMin = hut[feeMinProp];
  var feeMax = hut[feeMaxProp];

  var textContent = '$' + feeMin + '-' + '$' + feeMax;
  if (feeMin == null || feeMax == null) {
    if (feeMin == null && feeMax == null)
      textContent = 'N/A';
    else if (feeMin == null)
      textContent = '$' + feeMax;
    else
      textContent = '$' + feeMin;
  } else if (feeMin === feeMax) {
    textContent = '$' + feeMin;
  }
  return textContent;
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getCapacityRange = function(hut) {
  var capMin = hut['capacity_hut_min'];
  var capMax = hut['capacity_hut_max'];

  var textContent = capMin + '-' + capMax;
  if (capMin == null || capMax == null) {
    if (capMin == null && capMax == null)
      textContent = 'Unknown';
    else if (capMin == null)
      textContent = capMax;
    else
      textContent = capMin;
  } else if (capMin == capMax) {
    textContent = capMin;
  }
  return textContent;
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getBooleanAsYesNo = function(bool) {
  if (bool)
    return 'yes';
  else
    return 'no';
};

/**
 * @private
 */
hutmap.map.Sidebar.prototype.getWktPointAsLatLon = function(wkt_point) {
  var point = new Wkt.Wkt(wkt_point);
  return point.toObject().getPosition();
};

<|endoftext|>


<commit_before>/* eslint-disable */

import inherited from './inherited';
import nonInherited from './nonInherited';

const debounce = (fn) => {
	let timeoutId;
	return (...args) => {
		clearTimeout(timeoutId);
		timeoutId = setTimeout(() => fn(...args));
	};
};

const setSelector = debounce((rule, selectors) => {
	rule.selector = selectors.join(',\n');
});

const getReset = (option = 'inherited') => {
	switch (option) {
		case 'inherited':
			return inherited;
		case 'nonInherited':
			return nonInherited;
		case 'all':
			return { ...inherited, ...nonInherited };
		default:
			// If option is an object, merge it with the `inherited` props.
			return { ...inherited, ...option };
	}
};

export default function jssIsolate(options = {}) {
	const globalIsolate = options.isolate == null ? true : options.isolate;
	const selectors = [];
	let resetSheet = null;
	let resetRule;

	return (rule, sheet) => {
		if (
			rule.type !== 'regular' ||
			!sheet ||
			sheet === resetSheet ||
			!rule.style
		) {
			return;
		}

		const { parent } = rule.options;
		if (parent && (parent.type === 'keyframe' || parent.type === 'conditional')) {
			return;
		}

		let isolate = globalIsolate;
		if (sheet.options.isolate != null) {
			isolate = sheet.options.isolate;
		}
		if (rule.style.isolate != null) {
			isolate = rule.style.isolate;
			delete rule.style.isolate;
		}

		if (isolate === false) {
			return;
		}

		// Option `isolate` may be for e.g. `{isolate: 'root'}`.
		// In this case it must match the rule name in order to isolate it.
		if (isolate !== rule.name && typeof isolate === 'string') {
			return;
		}

		// Create a separate style sheet once and use it for all rules.
		if (!resetSheet && rule.options.jss) {
			resetSheet = rule.options.jss.createStyleSheet({}, {
				link: true,
				meta: 'jss-isolate',
				// Lets make it always the first one in sheets for testing
				// and specificity.
				index: -Infinity,
			});
			resetRule = resetSheet.addRule('reset', getReset(options.reset));
			resetSheet.attach();
		}
		if (selectors.indexOf(rule.selector) === -1) {
			selectors.push(rule.selector);
		}
		setSelector(resetRule, selectors);
	};
}
<commit_msg>Fix: Another wrong file name case<commit_after>/* eslint-disable */

import inherited from './inherited';
import nonInherited from './noninherited';

const debounce = (fn) => {
	let timeoutId;
	return (...args) => {
		clearTimeout(timeoutId);
		timeoutId = setTimeout(() => fn(...args));
	};
};

const setSelector = debounce((rule, selectors) => {
	rule.selector = selectors.join(',\n');
});

const getReset = (option = 'inherited') => {
	switch (option) {
		case 'inherited':
			return inherited;
		case 'nonInherited':
			return nonInherited;
		case 'all':
			return { ...inherited, ...nonInherited };
		default:
			// If option is an object, merge it with the `inherited` props.
			return { ...inherited, ...option };
	}
};

export default function jssIsolate(options = {}) {
	const globalIsolate = options.isolate == null ? true : options.isolate;
	const selectors = [];
	let resetSheet = null;
	let resetRule;

	return (rule, sheet) => {
		if (
			rule.type !== 'regular' ||
			!sheet ||
			sheet === resetSheet ||
			!rule.style
		) {
			return;
		}

		const { parent } = rule.options;
		if (parent && (parent.type === 'keyframe' || parent.type === 'conditional')) {
			return;
		}

		let isolate = globalIsolate;
		if (sheet.options.isolate != null) {
			isolate = sheet.options.isolate;
		}
		if (rule.style.isolate != null) {
			isolate = rule.style.isolate;
			delete rule.style.isolate;
		}

		if (isolate === false) {
			return;
		}

		// Option `isolate` may be for e.g. `{isolate: 'root'}`.
		// In this case it must match the rule name in order to isolate it.
		if (isolate !== rule.name && typeof isolate === 'string') {
			return;
		}

		// Create a separate style sheet once and use it for all rules.
		if (!resetSheet && rule.options.jss) {
			resetSheet = rule.options.jss.createStyleSheet({}, {
				link: true,
				meta: 'jss-isolate',
				// Lets make it always the first one in sheets for testing
				// and specificity.
				index: -Infinity,
			});
			resetRule = resetSheet.addRule('reset', getReset(options.reset));
			resetSheet.attach();
		}
		if (selectors.indexOf(rule.selector) === -1) {
			selectors.push(rule.selector);
		}
		setSelector(resetRule, selectors);
	};
}
<|endoftext|>


<commit_before>(function(){
  'use strict';
  var RgstrCtrl = function($scope, db, RgstrSrvc){
    $scope.isDefault = !db.anyAccounts;
    $scope.usernameValid = null;

    $scope.checkUser = function(username){
      RgstrSrvc.check(username, $scope);
    }

    $scope.Create = function(){
      if ($scope.username === null){
        alert("fucking retard, you didn't give a username (and I put one in for you)");
      }
      RgstrSrvc.register($scope.username);
    };

  };

  module.exports = ["$scope", 'db', 'RgstrSrvc', RgstrCtrl];
}());
<commit_msg>fixed args for registerservice<commit_after>(function(){
  'use strict';
  var RgstrCtrl = function($scope, db, RgstrSrvc){
    $scope.isDefault = !db.anyAccounts;
    $scope.usernameValid = null;

    $scope.checkUser = function(username){
      RgstrSrvc.check(username, $scope);
    }

    $scope.Create = function(){
      if ($scope.username === null){
        alert("fucking retard, you didn't give a username (and I put one in for you)");
      }
      RgstrSrvc.register($scope.username, $scope.password);
    };

  };

  module.exports = ["$scope", 'db', 'RgstrSrvc', RgstrCtrl];
}());
<|endoftext|>


<commit_before>_r(function (app) {

  _.extend(Backbone.View.prototype, Backbone.Events);

  app.base.pageView = Backbone.View.extend({

    auto_render: false,
    model_generated: false,
    max_age: 10000,
    $el: window.app.config.$content,
    wait_for_user_loaded: true,
    reload_on_login: true,

    initialize: function (module, action, $el, params) {
      var self = this;

      _.extend(this, Backbone.Events);

      this.$el = $el || this.$el;
      this.el = this.$el[0];
      this.module = module || this.module;
      this.action = action || this.action;
      this.i18n = [module, action];
      this.rendered = false;
      this.params = Array.isArray(params) ? params : [];

      if ( ! this.checkAllowed.call(this)) {
        this.closeAndBack();
        return false;
      }

      if (this.model) {
        if ( ! this.model.get || ! typeof(this.model.get) === 'function') {
          this.model = new this.model();
          this.model_generated = true;
        }
        this.addLocals({_model: this.model});
        //this.model.view = this;
      }

      _.bindAll(this);

      this.addLocals({
        _t: this._t,
        _view: this
      });
      if (this.init && typeof(this.init) === 'function') {
        this.init();
      }

      if (this.auto_render) {
        if (this.wait_for_user_loaded && ! app.user_self.loaded) {
          app.once('user_loaded', function () {
            self.render();
          });
        } else {
          this.render();
        }
      }

      if (this.reload_on_login) {
        app.once('login', function () {
          if (self.reload_on_login && self.$el.parent().length !== 0 && (app.current.view === self || ! self.$el.hasClass('main_content'))) {
            self.render();
          }
        });
      }

      this._gc_interval = setInterval(function () {
        self._check_gc();
      }, 250);

      this._expiration = +new Date() + this.max_age;
      return true;
    },

    _check_gc: function () {
      if (this.rendered && this.$el.parent().length === 0) {
        clearInterval(this._gc_interval);
        this._unload();
      }
    },

    _unload: function () {
      if (this.model_generated) {
        this.model.unbind();
        delete this.model;
      }
      this.unbind();
    },

    addLocals: function (locals) {
      if (!this.locals) {
        this.locals = {};
      }
      this.locals = _.extend(this.locals, locals);
    },

    successRender: function (locals) {
      var self = this;
      app.template(this.module, this.action, locals, function (html) {
        if (html === false) {
          self.errorRender(locals);
        } else {
          if (self.afterRender(html) !== false) {
            self.delegateEvents();
            self.rendered = true;
            self.trigger('rendered');
          }
        }
      });
    },

    errorRender: function (locals) {
      var self = this;
      var module = 'page';
      var action = 'error';
      if (this.own_error_template) {
        module = this.module;
        if (_.isString(this.own_error_template)) {
          action = this.own_error_template;
        }
      }
      app.template(module, action, locals, function (html) {
        self.afterRender.call(self, html)
        self.trigger('error');
        self.rendered = true;
      });
    },

    render: function () {
      var self = this;
      this.rendered = false;
      this.load(function (err, data) {
        var locals = _.extend({
          success: !err,
          data: data,
        }, self.locals);
        if (err) {
          self.errorRender(locals);
        } else {
          self.successRender(locals);
        }
      });
    },

    load: function (callback) {
      callback();
    },

    /**
     * Called after the rendering of this view has produced some html.
     * Used for putting the html in an element or handling errors.
     */
    afterRender: function (html, error) {
      this.$el.html(html);
    },

    /**
     * If the page is reloaded without the force flag, this check is invoked to see if the page data has expired.
     * Compares the current time with this._expiration.
     */
    isExpired: function () {
      return +new Date() > this._expiration;
    },

    /**
     * Check if the user is allowed to call this view
     */
    checkAllowed: function () {
      return true;
    },

    /**
     *  Closes the current overlay and goes back in the application history if this is a main content.
     */
    closeAndBack: function () {
      app.closeOverlay();
      if (this.$el.parent()[0] === app.config.$content[0]) {
        app.back();
      }
    },

    /**
     * i18n based on the current module/action.
     */
    _t: function (name, submodule) {
      submodule = submodule || this.action;
      return $.t(name, submodule, this.module);
    }

  });

  app.base.formView = app.base.pageView.extend({

    initialize: function () {
      if ( ! app.base.pageView.prototype.initialize.apply(this, arguments)) {
        return false;
      }

      if (_.isFunction(this.saved)) {
        this.model.once('saved', this.saved);
      }
      if (_.isFunction(this.error)) {
        this.model.bind('error', this.error);
      }
    },

    /**
     * Initialize and link the formHandler
     */
    afterRender: function (html) {
      this.$el.html(html);
      this.handler = new app.formHandler(this);
      this.handler.link();
    }

  });

  app.base.listView = app.base.pageView.extend({

    initialize: function () {
      var self = this;

      if (this.collection) {
        if ( ! this.collection.getByCid || typeof(this.collection.getByCid) !== 'function') {
          this.collection = new this.collection();
          this.collection_generated = true;
        }
        this.addLocals({collection: this.collection});
      }

      return app.base.pageView.prototype.initialize.apply(this, arguments);
    },

    /**
     * Make sure the collection doesn't have event listeners to improve gc
     */
    _unload: function () {
      this.collection.unbind();
      app.base.pageView.prototype._unload.apply(this, arguments);
    },

    /**
     * Overwrite load to fetch a collection instead of the model
     */
    load: function (callback) {
      this.collection.fetch({
        success: function (collection) {
          callback(null, collection);
        },
        error: function (collection, response) {
          var json = JSON.parse(response.responseText);
          callback(json.data, null);
        }
      });
    }

  });

  app.base.paginatedListView = app.base.listView.extend({

    pagination_pager_selector: '.pagination',

    initialize: function () {
      var self = this;
      var do_auto_render = this.auto_render;

      if (do_auto_render) {
        this.auto_render = false;
      }
      this.current_page = 1;

      if ( ! app.base.listView.prototype.initialize.apply(this, arguments)) {
        return false;
      }

      this.collection.getPage(1, function () {
        if (do_auto_render) {
          self.render();
        }
      });

      this.bind('rendered', self.renderPagination, this);

      this.$el.delegate(this.pagination_pager_selector+' a', 'click', function (e) {
        e.preventDefault();
        var $target = $(e.target);
        var $li = $target.closest('li');
        if ($li.hasClass('disabled') || $li.hasClass('active')) {
          return false;
        } else {
          self.paginator($target.data('page'));
        }
      });
    },

    /**
     * Overwrite the default rendering to render the page that is currently selected
     */
    render: function () {
      this.current_page = this.getPageFromHash();
      this.paginator(this.current_page);
    },

    /**
     * Render the pagination into an element specified by this.pagination_pager_selector
     */
    renderPagination: function () {
      var $pagination = this.$el.find(this.pagination_pager_selector);

      if (this.collection.total <= this.collection.per_page) {
        // no pagination needed
        $pagination.hide();
        return false;
      }

      if ($pagination.length === 0) {
        console.log('Collection is paginated but no pagination element found with this selector:', this.pagination_pager_selector);
        return false;
      }

      var locals = _.extend({}, this.locals, {
        current: parseInt(this.current_page, 10),
        num_pages: Math.ceil(this.collection.total / this.collection.per_page)
      });
      app.template('page', 'pagination', locals, function (html) {
        $pagination.html(html);
      });
    },

    /**
     * Checks if there is a page=[\d] in the hash. If so, returns the [\d] otherwise 1.
     */
    getPageFromHash: function () {
      var page = 1;
      window.location.hash
        .split('/')
          .forEach(
            function (part) {
              var match = part.match(/^page=([\d]+)$/i);
              if (match) {
                page = match[1];
              }
            }
          );
      return parseInt(page, 10);
    },

    /**
     * If the current window.location.hash does not have the current page numer in it the hash is changed.
     */
    setPageHash: function () {
      var current_hash = window.location.hash;
      var current_hash_page = current_hash.match(/\/page=([\d]+)/);
      if ( ! current_hash_page && this.current_page === 1) {
        return false;
      }
      var new_hash_page = this.current_page === 1 ? '' : '/page='+this.current_page;
      var new_hash = '';

      if (current_hash_page) {
        new_hash = current_hash.replace(/\/page=([\d]+)/, new_hash_page);
      } else {
        new_hash = current_hash+new_hash_page;
      }
      app.navigate(new_hash);
    },

    /**
     * Renders and sets a page.
     */
    paginator: function (page) {
      var self = this;
      var new_page = parseInt(page, 10);

      if (isNaN(new_page)) {
        return false;
      }

      this.collection.getPage(new_page, function (collection, page) {
        self.current_page = page;
        self.setPageHash();
        self.locals.data = collection;
        self.successRender(self.locals);
      });
    }
  });
});<commit_msg>Fix models to have a ref to the view again<commit_after>_r(function (app) {

  _.extend(Backbone.View.prototype, Backbone.Events);

  app.base.pageView = Backbone.View.extend({

    auto_render: false,
    model_generated: false,
    max_age: 10000,
    $el: window.app.config.$content,
    wait_for_user_loaded: true,
    reload_on_login: true,

    initialize: function (module, action, $el, params) {
      var self = this;

      _.extend(this, Backbone.Events);

      this.$el = $el || this.$el;
      this.el = this.$el[0];
      this.module = module || this.module;
      this.action = action || this.action;
      this.i18n = [module, action];
      this.rendered = false;
      this.params = Array.isArray(params) ? params : [];

      if ( ! this.checkAllowed.call(this)) {
        this.closeAndBack();
        return false;
      }

      if (this.model) {
        if ( ! this.model.get || ! typeof(this.model.get) === 'function') {
          this.model = new this.model();
          this.model_generated = true;
        }
        this.addLocals({_model: this.model});
        this.model.view = this;
      }

      _.bindAll(this);

      this.addLocals({
        _t: this._t,
        _view: this
      });
      if (this.init && typeof(this.init) === 'function') {
        this.init();
      }

      if (this.auto_render) {
        if (this.wait_for_user_loaded && ! app.user_self.loaded) {
          app.once('user_loaded', function () {
            self.render();
          });
        } else {
          this.render();
        }
      }

      if (this.reload_on_login) {
        app.once('login', function () {
          if (self.reload_on_login && self.$el.parent().length !== 0 && (app.current.view === self || ! self.$el.hasClass('main_content'))) {
            self.render();
          }
        });
      }

      this._gc_interval = setInterval(function () {
        self._check_gc();
      }, 250);

      this._expiration = +new Date() + this.max_age;
      return true;
    },

    _check_gc: function () {
      if (this.rendered && this.$el.parent().length === 0) {
        clearInterval(this._gc_interval);
        this._unload();
      }
    },

    _unload: function () {
      if (this.model_generated) {
        this.model.unbind();
        delete this.model;
      }
      this.unbind();
    },

    addLocals: function (locals) {
      if (!this.locals) {
        this.locals = {};
      }
      this.locals = _.extend(this.locals, locals);
    },

    successRender: function (locals) {
      var self = this;
      app.template(this.module, this.action, locals, function (html) {
        if (html === false) {
          self.errorRender(locals);
        } else {
          if (self.afterRender(html) !== false) {
            self.delegateEvents();
            self.rendered = true;
            self.trigger('rendered');
          }
        }
      });
    },

    errorRender: function (locals) {
      var self = this;
      var module = 'page';
      var action = 'error';
      if (this.own_error_template) {
        module = this.module;
        if (_.isString(this.own_error_template)) {
          action = this.own_error_template;
        }
      }
      app.template(module, action, locals, function (html) {
        self.afterRender.call(self, html)
        self.trigger('error');
        self.rendered = true;
      });
    },

    render: function () {
      var self = this;
      this.rendered = false;
      this.load(function (err, data) {
        var locals = _.extend({
          success: !err,
          data: data,
        }, self.locals);
        if (err) {
          self.errorRender(locals);
        } else {
          self.successRender(locals);
        }
      });
    },

    load: function (callback) {
      callback();
    },

    /**
     * Called after the rendering of this view has produced some html.
     * Used for putting the html in an element or handling errors.
     */
    afterRender: function (html, error) {
      this.$el.html(html);
    },

    /**
     * If the page is reloaded without the force flag, this check is invoked to see if the page data has expired.
     * Compares the current time with this._expiration.
     */
    isExpired: function () {
      return +new Date() > this._expiration;
    },

    /**
     * Check if the user is allowed to call this view
     */
    checkAllowed: function () {
      return true;
    },

    /**
     *  Closes the current overlay and goes back in the application history if this is a main content.
     */
    closeAndBack: function () {
      app.closeOverlay();
      if (this.$el.parent()[0] === app.config.$content[0]) {
        app.back();
      }
    },

    /**
     * i18n based on the current module/action.
     */
    _t: function (name, submodule) {
      submodule = submodule || this.action;
      return $.t(name, submodule, this.module);
    }

  });

  app.base.formView = app.base.pageView.extend({

    initialize: function () {
      if ( ! app.base.pageView.prototype.initialize.apply(this, arguments)) {
        return false;
      }

      if (_.isFunction(this.saved)) {
        this.model.once('saved', this.saved);
      }
      if (_.isFunction(this.error)) {
        this.model.bind('error', this.error);
      }
    },

    /**
     * Initialize and link the formHandler
     */
    afterRender: function (html) {
      this.$el.html(html);
      this.handler = new app.formHandler(this);
      this.handler.link();
    }

  });

  app.base.listView = app.base.pageView.extend({

    initialize: function () {
      var self = this;

      if (this.collection) {
        if ( ! this.collection.getByCid || typeof(this.collection.getByCid) !== 'function') {
          this.collection = new this.collection();
          this.collection_generated = true;
        }
        this.addLocals({collection: this.collection});
      }

      return app.base.pageView.prototype.initialize.apply(this, arguments);
    },

    /**
     * Make sure the collection doesn't have event listeners to improve gc
     */
    _unload: function () {
      this.collection.unbind();
      app.base.pageView.prototype._unload.apply(this, arguments);
    },

    /**
     * Overwrite load to fetch a collection instead of the model
     */
    load: function (callback) {
      this.collection.fetch({
        success: function (collection) {
          callback(null, collection);
        },
        error: function (collection, response) {
          var json = JSON.parse(response.responseText);
          callback(json.data, null);
        }
      });
    }

  });

  app.base.paginatedListView = app.base.listView.extend({

    pagination_pager_selector: '.pagination',

    initialize: function () {
      var self = this;
      var do_auto_render = this.auto_render;

      if (do_auto_render) {
        this.auto_render = false;
      }
      this.current_page = 1;

      if ( ! app.base.listView.prototype.initialize.apply(this, arguments)) {
        return false;
      }

      this.collection.getPage(1, function () {
        if (do_auto_render) {
          self.render();
        }
      });

      this.bind('rendered', self.renderPagination, this);

      this.$el.delegate(this.pagination_pager_selector+' a', 'click', function (e) {
        e.preventDefault();
        var $target = $(e.target);
        var $li = $target.closest('li');
        if ($li.hasClass('disabled') || $li.hasClass('active')) {
          return false;
        } else {
          self.paginator($target.data('page'));
        }
      });
    },

    /**
     * Overwrite the default rendering to render the page that is currently selected
     */
    render: function () {
      this.current_page = this.getPageFromHash();
      this.paginator(this.current_page);
    },

    /**
     * Render the pagination into an element specified by this.pagination_pager_selector
     */
    renderPagination: function () {
      var $pagination = this.$el.find(this.pagination_pager_selector);

      if (this.collection.total <= this.collection.per_page) {
        // no pagination needed
        $pagination.hide();
        return false;
      }

      if ($pagination.length === 0) {
        console.log('Collection is paginated but no pagination element found with this selector:', this.pagination_pager_selector);
        return false;
      }

      var locals = _.extend({}, this.locals, {
        current: parseInt(this.current_page, 10),
        num_pages: Math.ceil(this.collection.total / this.collection.per_page)
      });
      app.template('page', 'pagination', locals, function (html) {
        $pagination.html(html);
      });
    },

    /**
     * Checks if there is a page=[\d] in the hash. If so, returns the [\d] otherwise 1.
     */
    getPageFromHash: function () {
      var page = 1;
      window.location.hash
        .split('/')
          .forEach(
            function (part) {
              var match = part.match(/^page=([\d]+)$/i);
              if (match) {
                page = match[1];
              }
            }
          );
      return parseInt(page, 10);
    },

    /**
     * If the current window.location.hash does not have the current page numer in it the hash is changed.
     */
    setPageHash: function () {
      var current_hash = window.location.hash;
      var current_hash_page = current_hash.match(/\/page=([\d]+)/);
      if ( ! current_hash_page && this.current_page === 1) {
        return false;
      }
      var new_hash_page = this.current_page === 1 ? '' : '/page='+this.current_page;
      var new_hash = '';

      if (current_hash_page) {
        new_hash = current_hash.replace(/\/page=([\d]+)/, new_hash_page);
      } else {
        new_hash = current_hash+new_hash_page;
      }
      app.navigate(new_hash);
    },

    /**
     * Renders and sets a page.
     */
    paginator: function (page) {
      var self = this;
      var new_page = parseInt(page, 10);

      if (isNaN(new_page)) {
        return false;
      }

      this.collection.getPage(new_page, function (collection, page) {
        self.current_page = page;
        self.setPageHash();
        self.locals.data = collection;
        self.successRender(self.locals);
      });
    }
  });
});<|endoftext|>


<commit_before>import BABYLON from 'babylonjs';
import {getHeightAtCoordinates, secondsToTicks} from '../../powerUtils/mainUtils';
import PowerRemember from '../../powerRememberObjects';
import JointObject from '../../powerObjects/jointObject';
import PointCursor from '../../cursors/pointCursor';

const manaCostCreate = 2000; // mana cost of the power
const manaCostFreeze = 20;
const collisionDamage = 5; // the amount of damage it does when it collides
const jointLength = 20;
const mass = 1;
const maxItems = 3;

const directionVec = new BABYLON.Vector3(0, 0, -1);  // point spawn for the cursor

const fixedRotation = false;
const meshSize = 5;

/*
* Creates a box that is chained to the character
*/
export default class BlockChain extends PowerRemember {
    constructor(game, hero) {
      super(game, hero, maxItems);
    }

    createMesh () {
      // Set the spawn vector
      const vectorStart = new BABYLON.Vector3(
        this.cursor.mesh.position.x,
        (getHeightAtCoordinates(this.game.scene, this.cursor.mesh.position.x, this.cursor.mesh.position.z)) - (meshSize/2) - 2,
        this.cursor.mesh.position.z
      );

      // Set the target vector
      const vectorEnd = new BABYLON.Vector3(
        this.cursor.mesh.position.x,
        Math.max((getHeightAtCoordinates(this.game.scene, this.cursor.mesh.position.x, this.cursor.mesh.position.z)) + (meshSize/2) + 2,
        this.hero.mask.position.y),
        this.cursor.mesh.position.z
      );

      // Create the mesh
      const mesh = new BABYLON.Mesh.CreateBox('mesh', meshSize, this.game.scene);
      mesh.position.copyFrom(vectorStart);
      BABYLON.Tags.EnableFor(mesh);
      BABYLON.Tags.AddTagsTo(mesh, "checkJump");
      mesh.physicsImpostor = new BABYLON.PhysicsImpostor(mesh, BABYLON.PhysicsImpostor.BoxImpostor, {mass:0, friction:0.1, restitution:1}, this.game.scene);
      // run spawn
      // Create a new joint, needs to be a new joint
      var distJoint = new BABYLON.DistanceJoint( {maxDistance: jointLength} );
      // Create the joint object that we will use for binding the power to the hero
      //this.powerObjects.push(new JointObject(this.game, this.hero, mesh, vectorStart, vectorEnd, 10, secondsToTicks(10), 0, 150, this.hero.mask, joint, mass, collisionDamage));
      this.addObject(new JointObject(this.game, this.hero,
        // basePowerObject values
        {mesh:mesh, vectorStart:vectorStart, vectorEnd:vectorEnd, range:10, lifeSpan:secondsToTicks(10),
        dropHeight:10, dropRange:150, collisionCallBack:true, damageMult:collisionDamage},
        // projectileObject values
        {target:this.hero.mask, joint:distJoint, mass:mass} ));

      mesh.physicsImpostor.physicsBody.collisionFilterGroup = this.game.scene.collisionGroupGround;
      mesh.physicsImpostor.physicsBody.collisionFilterMask = this.game.scene.collisionGroupNormal | this.game.scene.collisionGroupGround;
      if (!fixedRotation) {
        mesh.rotationQuaternion.copyFrom(this.hero.mask.rotationQuaternion);
      }
    }

    buttonDown(i) {
      // Consume and check to see if there is enough mana or see if we have max amount of objects
      if (this.powerIsFull() || !this.hero.consumeMana(manaCostCreate)){
        return;
      }
      // Create three walls that protect the player
      this.cursor = new PointCursor(this.game, this.hero,
        {direction:directionVec, distance: jointLength, fixed: true} );
    }

    buttonUp(i) {
      // Make sure a cursor is present
      if (!this.cursor) {
        return;
      }

      this.createMesh();
      this.cursor.destroy();
      this.cursor = undefined;
    }
}
<commit_msg>buffed psychblock power<commit_after>import BABYLON from 'babylonjs';
import {getHeightAtCoordinates, secondsToTicks} from '../../powerUtils/mainUtils';
import PowerRemember from '../../powerRememberObjects';
import JointObject from '../../powerObjects/jointObject';
import PointCursor from '../../cursors/pointCursor';

const manaCostCreate = 2000; // mana cost of the power
const manaCostFreeze = 20;
const collisionDamage = 5; // the amount of damage it does when it collides
const jointLength = 20;
const mass = 1;
const maxItems = 3;

const directionVec = new BABYLON.Vector3(0, 0, -1);  // point spawn for the cursor

const fixedRotation = false;
const meshSize = 5;

/*
* Creates a box that is chained to the character
*/
export default class BlockChain extends PowerRemember {
    constructor(game, hero) {
      super(game, hero, maxItems);
    }

    createMesh () {
      // Set the spawn vector
      const vectorStart = new BABYLON.Vector3(
        this.cursor.mesh.position.x,
        (getHeightAtCoordinates(this.game.scene, this.cursor.mesh.position.x, this.cursor.mesh.position.z)) - (meshSize/2) - 2,
        this.cursor.mesh.position.z
      );

      // Set the target vector
      const vectorEnd = new BABYLON.Vector3(
        this.cursor.mesh.position.x,
        Math.max((getHeightAtCoordinates(this.game.scene, this.cursor.mesh.position.x, this.cursor.mesh.position.z)) + (meshSize/2) + 2,
        this.hero.mask.position.y),
        this.cursor.mesh.position.z
      );

      // Create the mesh
      const mesh = new BABYLON.Mesh.CreateBox('mesh', meshSize, this.game.scene);
      mesh.position.copyFrom(vectorStart);
      BABYLON.Tags.EnableFor(mesh);
      BABYLON.Tags.AddTagsTo(mesh, "checkJump");
      mesh.physicsImpostor = new BABYLON.PhysicsImpostor(mesh, BABYLON.PhysicsImpostor.BoxImpostor, {mass:0, friction:0.001, restitution:1.1}, this.game.scene);
      // run spawn
      // Create a new joint, needs to be a new joint
      var distJoint = new BABYLON.DistanceJoint( {maxDistance: jointLength} );
      // Create the joint object that we will use for binding the power to the hero
      //this.powerObjects.push(new JointObject(this.game, this.hero, mesh, vectorStart, vectorEnd, 10, secondsToTicks(10), 0, 150, this.hero.mask, joint, mass, collisionDamage));
      this.addObject(new JointObject(this.game, this.hero,
        // basePowerObject values
        {mesh:mesh, vectorStart:vectorStart, vectorEnd:vectorEnd, range:10, lifeSpan:secondsToTicks(10),
        dropHeight:10, dropRange:150, collisionCallBack:true, damageMult:collisionDamage},
        // projectileObject values
        {target:this.hero.mask, joint:distJoint, mass:mass} ));

      mesh.physicsImpostor.physicsBody.collisionFilterGroup = this.game.scene.collisionGroupGround;
      mesh.physicsImpostor.physicsBody.collisionFilterMask = this.game.scene.collisionGroupNormal | this.game.scene.collisionGroupGround;
      mesh.physicsImpostor.physicsBody.linearDamping = 0.0001;
      mesh.physicsImpostor.physicsBody.angularDamping = 0.0001;
      mesh.physicsImpostor.physicsBody.updateMassProperties();
      if (!fixedRotation) {
        mesh.rotationQuaternion.copyFrom(this.hero.mask.rotationQuaternion);
      }
    }

    buttonDown(i) {
      // Consume and check to see if there is enough mana or see if we have max amount of objects
      if (this.powerIsFull() || !this.hero.consumeMana(manaCostCreate)){
        return;
      }
      // Create three walls that protect the player
      this.cursor = new PointCursor(this.game, this.hero,
        {direction:directionVec, distance: jointLength, fixed: true} );
    }

    buttonUp(i) {
      // Make sure a cursor is present
      if (!this.cursor) {
        return;
      }

      this.createMesh();
      this.cursor.destroy();
      this.cursor = undefined;
    }
}
<|endoftext|>


<commit_before>import Moment from 'moment';
import Rainbow from 'rainbowvis.js';
import fetchJsonp from 'fetch-jsonp';

//  Actions
import WeatherActions from '../actions/WeatherActions';

//  The stores
import SettingsStore from '../stores/SettingsStore';

class WeatherAPIUtils {

    /* Get the Forecast.io weather for the given coordiates */
    getCurrentForecastIOWeather(latitude, longitude) {

        let settings = SettingsStore.getSettings();
        let apikey = settings.weathersource_apikey;

        //  Get the weather for the given lat/long coordinates
        let url = `https://api.darksky.net/forecast/${apikey}/${latitude},${longitude}`;
        let weatherUtils = this;

        fetchJsonp(url)
            .then(function (response) {
                return response.json()
            }).then(function (json) {
                let weatherdata = weatherUtils.convertForecastIOToWeather(json);

                //	Call the action to receive the data:
                WeatherActions.recieveWeatherData(weatherdata);
            }).catch(function (ex) {
                console.log('parsing failed', ex)
            })

    }

    /* Convert Forecast.io data to the standard model */
    convertForecastIOToWeather(fdata) {

        //  Map the daily data to the common format
        let dailyData = fdata.daily.data.map(function (day) {

            return {
                summary: day.summary,
                date: day.time,
                icon: day.icon,
                high: day.temperatureMax,
                low: day.temperatureMin,
                precipProbability: day.precipProbability
            };
        });

        //  Convert the rest of the data to the common weather format
        let weatherdata = {
            currently: {
                icon: fdata.currently.icon,
                temperature: fdata.currently.temperature,
                windspeed: fdata.currently.windSpeed,
                wind_direction: fdata.currently.windBearing,
                humidity: fdata.currently.humidity,
                apparent_temp: fdata.currently.apparentTemperature,
                sunrise: fdata.daily.data[0].sunriseTime * 1000,
                sunset: fdata.daily.data[0].sunsetTime * 1000,
            },
            daily: {
                data: dailyData
            },
            alerts: [],
            source: "Darksky.net",
            lastupdated: new Date()
        };

        return weatherdata;
    }

    /* Get Yahoo weather for the given coordinates */
    getCurrentYahooWeather(latitude, longitude) {

        //  Format the yahoo url
        let url = `https://query.yahooapis.com/v1/public/yql?q=select * from weather.forecast where woeid in (SELECT woeid FROM geo.places WHERE text="(${latitude},${longitude})")&format=json&env=store://datatables.org/alltableswithkeys`
        let weatherUtils = this;

        fetch(url, { mode: 'cors' })
            .then(
            function (response) {
                if (response.status !== 200) {
                    console.log('Looks like there was a problem. Status Code: ' + response.status);
                    return;
                }

                // Receive data
                response.json().then(function (data) {
                    //  Convert the data to the common weather format
                    let weatherdata = weatherUtils.convertYahooToWeather(data);

                    //  Call the action to receive the data:
                    WeatherActions.recieveWeatherData(weatherdata);
                });
            }
            )
            .catch(function (err) {
                console.log('Fetch Error :-S', err);
            });
    }

    /* Convert Yahoo data format to the standard model */
    convertYahooToWeather(ydata) {

        let yw = ydata.query.results.channel;

        //  Map the daily data to the common format
        let dailyData = yw.item.forecast.map(function (day) {

            //  Sanity check the high and low:
            let low = parseInt(yw.item.condition.temp, 10);
            if (parseInt(day.low, 10) < parseInt(day.high, 10)) {
                low = parseInt(day.low, 10);
            }

            return {
                summary: day.text,
                date: Moment(day.date, "D MMM YYYY").unix(),
                icon: day.code,
                high: parseInt(day.high, 10),
                low: low,
                precipProbability: 0
            };
        });

        let startofday = Moment().startOf('day').format('MMM D, YYYY');

        //  Convert the rest of the data to the common weather format
        let weatherdata = {
            currently: {
                icon: yw.item.condition.code,
                temperature: parseInt(yw.item.condition.temp, 10),
                windspeed: parseInt(yw.wind.speed, 10),
                wind_direction: parseInt(yw.wind.direction, 10),
                humidity: parseInt(yw.atmosphere.humidity, 10) / 100,
                apparent_temp: parseInt(yw.wind.chill, 10),
                sunrise: Date.parse(startofday + ' ' + yw.astronomy.sunrise),
                sunset: Date.parse(startofday + ' ' + yw.astronomy.sunset),
            },
            daily: {
                data: dailyData
            },
            alerts: [],
            source: "Yahoo weather",
            lastupdated: new Date()
        };

        return weatherdata;
    }

    /* Get pollen counts for the given zipcode */
    getPollen(zipcode) {
        //  The base url for the service - change this to your service location:
        let baseurl = "https://mbxawsgue8.execute-api.us-east-1.amazonaws.com/v1/";

        //  Get the pollen for the given zipcode
        let url = baseurl + zipcode;

        fetch(url, { mode: 'cors' })
            .then(
            function (response) {
                if (response.status !== 200) {
                    console.log('Looks like there was a problem. Status Code: ' + response.status);
                    return;
                }

                // Receive data
                response.json().then(function (data) {
                    //  Call the action to receive the data:
                    WeatherActions.recievePollenData(data, zipcode);
                });
            }
            )
            .catch(function (err) {
                console.log('Fetch Error :-S', err);
            });
    }

    /* Get the color for the given temperature */
    getTempColor(temperature) {

        //  Create a new spectrum:
        let rainbow = new Rainbow();

        //  Set our spectrum colors:
        rainbow.setSpectrum("FFC0E4", "D0338D", "33D0C4", "337DD0", "33D035", "DAD82D", "F08E00", "D03333");

        //  Get the color for the given temperature:
        let appColor = "#" + rainbow.colorAt(temperature);

        return appColor;
    }

    // Gets the wind direction icon to use for a given wind directional heading
    getWindDirectionIcon(direction) {

        let retval = "wi wi-wind ";

        switch (true) {
            case direction < 23:
                retval += "from-23-deg";
                break;
            case direction < 45:
                retval += "from-45-deg";
                break;
            case direction < 68:
                retval += "from-68-deg";
                break;
            case direction < 90:
                retval += "from-90-deg";
                break;
            case direction < 113:
                retval += "from-113-deg";
                break;
            case direction < 135:
                retval += "from-135-deg";
                break;
            case direction < 158:
                retval += "from-158-deg";
                break;
            case direction < 180:
                retval += "from-180-deg";
                break;
            case direction < 203:
                retval += "from-203-deg";
                break;
            case direction < 225:
                retval += "from-225-deg";
                break;
            case direction < 248:
                retval += "from-248-deg";
                break;
            case direction < 270:
                retval += "from-270-deg";
                break;
            case direction < 293:
                retval += "from-293-deg";
                break;
            case direction < 313:
                retval += "from-313-deg";
                break;
            case direction < 336:
                retval += "from-336-deg";
                break;
            default:
                retval += "from-0-deg";
                break;
        }

        return retval;

    }

}

export default new WeatherAPIUtils();<commit_msg>Update wind direction with exact degrees<commit_after>import Moment from 'moment';
import Rainbow from 'rainbowvis.js';
import fetchJsonp from 'fetch-jsonp';

//  Actions
import WeatherActions from '../actions/WeatherActions';

//  The stores
import SettingsStore from '../stores/SettingsStore';

class WeatherAPIUtils {

    /* Get the Forecast.io weather for the given coordiates */
    getCurrentForecastIOWeather(latitude, longitude) {

        let settings = SettingsStore.getSettings();
        let apikey = settings.weathersource_apikey;

        //  Get the weather for the given lat/long coordinates
        let url = `https://api.darksky.net/forecast/${apikey}/${latitude},${longitude}`;
        let weatherUtils = this;

        fetchJsonp(url)
            .then(function (response) {
                return response.json()
            }).then(function (json) {
                let weatherdata = weatherUtils.convertForecastIOToWeather(json);

                //	Call the action to receive the data:
                WeatherActions.recieveWeatherData(weatherdata);
            }).catch(function (ex) {
                console.log('parsing failed', ex)
            })

    }

    /* Convert Forecast.io data to the standard model */
    convertForecastIOToWeather(fdata) {

        //  Map the daily data to the common format
        let dailyData = fdata.daily.data.map(function (day) {

            return {
                summary: day.summary,
                date: day.time,
                icon: day.icon,
                high: day.temperatureMax,
                low: day.temperatureMin,
                precipProbability: day.precipProbability
            };
        });

        //  Convert the rest of the data to the common weather format
        let weatherdata = {
            currently: {
                icon: fdata.currently.icon,
                temperature: fdata.currently.temperature,
                windspeed: fdata.currently.windSpeed,
                wind_direction: fdata.currently.windBearing,
                humidity: fdata.currently.humidity,
                apparent_temp: fdata.currently.apparentTemperature,
                sunrise: fdata.daily.data[0].sunriseTime * 1000,
                sunset: fdata.daily.data[0].sunsetTime * 1000,
            },
            daily: {
                data: dailyData
            },
            alerts: [],
            source: "Darksky.net",
            lastupdated: new Date()
        };

        return weatherdata;
    }

    /* Get Yahoo weather for the given coordinates */
    getCurrentYahooWeather(latitude, longitude) {

        //  Format the yahoo url
        let url = `https://query.yahooapis.com/v1/public/yql?q=select * from weather.forecast where woeid in (SELECT woeid FROM geo.places WHERE text="(${latitude},${longitude})")&format=json&env=store://datatables.org/alltableswithkeys`
        let weatherUtils = this;

        fetch(url, { mode: 'cors' })
            .then(
            function (response) {
                if (response.status !== 200) {
                    console.log('Looks like there was a problem. Status Code: ' + response.status);
                    return;
                }

                // Receive data
                response.json().then(function (data) {
                    //  Convert the data to the common weather format
                    let weatherdata = weatherUtils.convertYahooToWeather(data);

                    //  Call the action to receive the data:
                    WeatherActions.recieveWeatherData(weatherdata);
                });
            }
            )
            .catch(function (err) {
                console.log('Fetch Error :-S', err);
            });
    }

    /* Convert Yahoo data format to the standard model */
    convertYahooToWeather(ydata) {

        let yw = ydata.query.results.channel;

        //  Map the daily data to the common format
        let dailyData = yw.item.forecast.map(function (day) {

            //  Sanity check the high and low:
            let low = parseInt(yw.item.condition.temp, 10);
            if (parseInt(day.low, 10) < parseInt(day.high, 10)) {
                low = parseInt(day.low, 10);
            }

            return {
                summary: day.text,
                date: Moment(day.date, "D MMM YYYY").unix(),
                icon: day.code,
                high: parseInt(day.high, 10),
                low: low,
                precipProbability: 0
            };
        });

        let startofday = Moment().startOf('day').format('MMM D, YYYY');

        //  Convert the rest of the data to the common weather format
        let weatherdata = {
            currently: {
                icon: yw.item.condition.code,
                temperature: parseInt(yw.item.condition.temp, 10),
                windspeed: parseInt(yw.wind.speed, 10),
                wind_direction: parseInt(yw.wind.direction, 10),
                humidity: parseInt(yw.atmosphere.humidity, 10) / 100,
                apparent_temp: parseInt(yw.wind.chill, 10),
                sunrise: Date.parse(startofday + ' ' + yw.astronomy.sunrise),
                sunset: Date.parse(startofday + ' ' + yw.astronomy.sunset),
            },
            daily: {
                data: dailyData
            },
            alerts: [],
            source: "Yahoo weather",
            lastupdated: new Date()
        };

        return weatherdata;
    }

    /* Get pollen counts for the given zipcode */
    getPollen(zipcode) {
        //  The base url for the service - change this to your service location:
        let baseurl = "https://mbxawsgue8.execute-api.us-east-1.amazonaws.com/v1/";

        //  Get the pollen for the given zipcode
        let url = baseurl + zipcode;

        fetch(url, { mode: 'cors' })
            .then(
            function (response) {
                if (response.status !== 200) {
                    console.log('Looks like there was a problem. Status Code: ' + response.status);
                    return;
                }

                // Receive data
                response.json().then(function (data) {
                    //  Call the action to receive the data:
                    WeatherActions.recievePollenData(data, zipcode);
                });
            }
            )
            .catch(function (err) {
                console.log('Fetch Error :-S', err);
            });
    }

    /* Get the color for the given temperature */
    getTempColor(temperature) {

        //  Create a new spectrum:
        let rainbow = new Rainbow();

        //  Set our spectrum colors:
        rainbow.setSpectrum("FFC0E4", "D0338D", "33D0C4", "337DD0", "33D035", "DAD82D", "F08E00", "D03333");

        //  Get the color for the given temperature:
        let appColor = "#" + rainbow.colorAt(temperature);

        return appColor;
    }

    // Gets the wind direction icon to use for a given wind directional heading
    getWindDirectionIcon(direction) {

        let retval = `wi wi-wind from-${direction}-deg`;

        return retval;

    }

}

export default new WeatherAPIUtils();<|endoftext|>


<commit_before>import Vue from 'vue'
import store from '@/store/'

import { Util, Url, Crypto } from '@/utils/'

export default class MessageManager {

    constructor () {
        this.openWebSocket();
    }

    openWebSocket() {
        this.socket = new WebSocket( Url.get('websocket') + Url.getAccountParam());

        this.socket.onopen = () => {
            let subscribe = JSON.stringify({
                "command": "subscribe",
                "identifier": JSON.stringify({
                    "channel": "NotificationsChannel"
                })
            });

            this.socket.send(subscribe);
        };

        this.socket.onmessage = (e) => this.handleMessage(e);

        this.socket.onclose = () => {
            setTimeout(this.openWebSocket, 50); //TODO handle if cannot open again
        };
    }

    closeWebSocket() {
        this.socket.close();
    }

    handleMessage (e) {

        if (e.data.indexOf("ping") != -1)  // Is keep alive event
            return;

        let json = JSON.parse(e.data);

        if (typeof json.message == "undefined")
            return;

        if (json.message.operation == "added_message") {
            let message = Crypto.decryptMessage(json.message.content)

            store.state.msgbus.$emit('newMessage', message);
        } else if (json.message.operation == "read_conversation") {
            store.state.msgbus.$emit('conversationRead', json.message.content.id);
        }

    }

    static fetchSettings () {
        let constructed_url = Url.get('settings') + Url.getAccountParam();
        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then( response => {
                    MessageManager.processSettings(response);
                    resolve(response);

                }).catch(response => reject(response));
        });

        return promise
    }
    static processSettings (response) {
        response = response.data

        store.commit('theme_base', response.base_theme);
        store.commit('theme_round', response.rounder_bubbles);
        store.commit('theme_global_colors', {
            default: Util.expandColor(response.color),
            dark: Util.expandColor(response.color_dark),
            accent: Util.expandColor(response.color_accent),
        });
        store.commit('theme_use_global', response.use_global_theme);
    }

    static fetchConversations (index) {
        const constructed_url =
            Url.get('conversations') + index + Url.getAccountParam()

        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then(response => {
                    response = response.data
                    // Decrypt Conversations items
                    for(var i = 0; i < response.length; i++)
                        response[i] = Crypto.decryptConversation(response[i]);

                    resolve(response); // Resolve response
                })
                .catch(response => reject(response));
        });

        return promise
    }

    static fetchThread (conversation_id) {

        const limit = 70;

        const constructed_url =
            Url.get('messages') + Url.getAccountParam()
                + "&conversation_id=" + conversation_id + "&limit=" + limit + "&web=true";

        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then(response => {
                    response = response.data
                    // Decrypt Conversations items
                    for(var i = 0; i < response.length; i++)
                        response[i] = Crypto.decryptMessage(response[i]);

                    resolve(response); // Resolve response
                })
                .catch(response => reject(response));
        });

        return promise
    }

    static sendMessage (data, mime_type, thread_id) {

        let account_id = store.state.account_id;

        let id = MessageManager.generateId();

        if (mime_type == "text/plain")
            data =  Util.entityEncode(data)

        let snippet = mime_type == "text/plain" ? ("You: " + data) : "Photo";

        let encrypted = Crypto.encrypt(data);
        let snippetEncrypted = Crypto.encrypt(snippet);

        let timestamp = new Date().getTime();

        // Define request
        let request = {
            account_id: account_id,
            device_id: id,
            device_conversation_id: thread_id,
            message_type: 2,
            data: encrypted,
            timestamp: timestamp,
            mime_type: Crypto.encrypt(mime_type),
            read: true,
            seen: true
        };

        let conversationRequest = {
            account_id: account_id,
            read: true,
            timestamp: timestamp,
            snippet: snippetEncrypted
        };

        // Update on servers
        let constructed_url = Url.get('add_message');
        Vue.http.post(constructed_url, request, {'Content-Type': 'application/json'})
            .catch(response => console.log(response));

        constructed_url = Url.get('update_conversation') + thread_id;
        Vue.http.post(constructed_url, conversationRequest, {'Content-Type': 'application/json'})
            .catch(response => console.log(response));

        // Submit event
        let event_object = {
            device_id: id,
            conversation_id: thread_id,
            timestamp: timestamp,
            mime_type: mime_type,
            message_type: 2,
            data: data,
            read: true,
            snippet: snippet
        }

        store.state.msgbus.$emit('newMessage', event_object);

    }

    static markAsRead (thread_id) {

        // Read conversation
        let constructed_url = Url.get('read') + "/" + thread_id + Url.getAccountParam();
        Vue.http.post(constructed_url);

        // Dismiss notifiction
        constructed_url = Url.get('dismiss') + Url.getAccountParam()
                + "&id=" + thread_id;
        Vue.http.post(constructed_url);
    }

    static generateId () {
        let min = 1;
        let max = 922337203685477;

        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
}
<commit_msg>Attempt socket reconnection<commit_after>import Vue from 'vue'
import store from '@/store/'

import { Util, Url, Crypto } from '@/utils/'

export default class MessageManager {

    constructor () {
        this.openWebSocket();

        this.has_disconnected = false;
    }

    openWebSocket() {

        let this_ = this;

        this.socket = new WebSocket(Url.get('websocket') + Url.getAccountParam());

        this.socket.onopen = () => {

            console.log("open web socket");
            let subscribe = JSON.stringify({
                "command": "subscribe",
                "identifier": JSON.stringify({
                    "channel": "NotificationsChannel"
                })
            });

            this.socket.send(subscribe);

            if (this_.has_disconnected)
                store.state.msgbus.$emit('refresh-btn');

            this.has_disconnected = false;
        };

        this.socket.onmessage = (e) => this.handleMessage(e);

        this.socket.onclose = () => {
            console.log("closed web socket");
        };

        this.socket.onerror = (e) => {
            console.log("error web socket");
            this.has_disconnected = true;

            let reopenTimeout = Math.floor(Math.random() * 20);
            while (reopenTimeout < 5) {
                reopenTimeout = Math.floor(Math.random() * 20);
            }

            setTimeout(this_.openWebSocket, reopenTimeout);
        }
    }

    closeWebSocket() {
        this.socket.close();
    }

    handleMessage (e) {

        if (e.data.indexOf("ping") != -1)  // Is keep alive event
            return;

        let json = JSON.parse(e.data);

        if (typeof json.message == "undefined")
            return;

        if (json.message.operation == "added_message") {
            let message = Crypto.decryptMessage(json.message.content)

            store.state.msgbus.$emit('newMessage', message);
        } else if (json.message.operation == "read_conversation") {
            store.state.msgbus.$emit('conversationRead', json.message.content.id);
        }

    }

    static fetchSettings () {
        let constructed_url = Url.get('settings') + Url.getAccountParam();
        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then( response => {
                    MessageManager.processSettings(response);
                    resolve(response);

                }).catch(response => reject(response));
        });

        return promise
    }
    static processSettings (response) {
        response = response.data

        store.commit('theme_base', response.base_theme);
        store.commit('theme_round', response.rounder_bubbles);
        store.commit('theme_global_colors', {
            default: Util.expandColor(response.color),
            dark: Util.expandColor(response.color_dark),
            accent: Util.expandColor(response.color_accent),
        });
        store.commit('theme_use_global', response.use_global_theme);
    }

    static fetchConversations (index) {
        const constructed_url =
            Url.get('conversations') + index + Url.getAccountParam()

        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then(response => {
                    response = response.data
                    // Decrypt Conversations items
                    for(var i = 0; i < response.length; i++)
                        response[i] = Crypto.decryptConversation(response[i]);

                    resolve(response); // Resolve response
                })
                .catch(response => reject(response));
        });

        return promise
    }

    static fetchThread (conversation_id) {

        const limit = 70;

        const constructed_url =
            Url.get('messages') + Url.getAccountParam()
                + "&conversation_id=" + conversation_id + "&limit=" + limit + "&web=true";

        const promise = new Promise((resolve, reject) => {
            Vue.http.get( constructed_url )
                .then(response => {
                    response = response.data
                    // Decrypt Conversations items
                    for(var i = 0; i < response.length; i++)
                        response[i] = Crypto.decryptMessage(response[i]);

                    resolve(response); // Resolve response
                })
                .catch(response => reject(response));
        });

        return promise
    }

    static sendMessage (data, mime_type, thread_id) {

        let account_id = store.state.account_id;

        let id = MessageManager.generateId();

        if (mime_type == "text/plain")
            data =  Util.entityEncode(data)

        let snippet = mime_type == "text/plain" ? ("You: " + data) : "Photo";

        let encrypted = Crypto.encrypt(data);
        let snippetEncrypted = Crypto.encrypt(snippet);

        let timestamp = new Date().getTime();

        // Define request
        let request = {
            account_id: account_id,
            device_id: id,
            device_conversation_id: thread_id,
            message_type: 2,
            data: encrypted,
            timestamp: timestamp,
            mime_type: Crypto.encrypt(mime_type),
            read: true,
            seen: true
        };

        let conversationRequest = {
            account_id: account_id,
            read: true,
            timestamp: timestamp,
            snippet: snippetEncrypted
        };

        // Update on servers
        let constructed_url = Url.get('add_message');
        Vue.http.post(constructed_url, request, {'Content-Type': 'application/json'})
            .catch(response => console.log(response));

        constructed_url = Url.get('update_conversation') + thread_id;
        Vue.http.post(constructed_url, conversationRequest, {'Content-Type': 'application/json'})
            .catch(response => console.log(response));

        // Submit event
        let event_object = {
            device_id: id,
            conversation_id: thread_id,
            timestamp: timestamp,
            mime_type: mime_type,
            message_type: 2,
            data: data,
            read: true,
            snippet: snippet
        }

        store.state.msgbus.$emit('newMessage', event_object);

    }

    static markAsRead (thread_id) {

        // Read conversation
        let constructed_url = Url.get('read') + "/" + thread_id + Url.getAccountParam();
        Vue.http.post(constructed_url);

        // Dismiss notifiction
        constructed_url = Url.get('dismiss') + Url.getAccountParam()
                + "&id=" + thread_id;
        Vue.http.post(constructed_url);
    }

    static generateId () {
        let min = 1;
        let max = 922337203685477;

        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
}
<|endoftext|>