<commit_before>/**
 * @module ol/renderer/Map
 */
import {getUid} from '../util.js';
import Disposable from '../Disposable.js';
import {listen, unlistenByKey} from '../events.js';
import EventType from '../events/EventType.js';
import {getWidth} from '../extent.js';
import {TRUE, VOID} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {compose as composeTransform, invert as invertTransform, setFromArray as transformSetFromArray} from '../transform.js';

class MapRenderer extends Disposable {

  /**
   * @param {import("../PluggableMap.js").default} map Map.
   */
  constructor(map) {
    super();

    /**
     * @private
     * @type {import("../PluggableMap.js").default}
     */
    this.map_ = map;

    /**
     * @private
     * @type {!Object<string, import("./Layer.js").default>}
     */
    this.layerRenderers_ = {};

    /**
     * @private
     * @type {Object<string, import("../events.js").EventsKey>}
     */
    this.layerRendererListeners_ = {};

    /**
     * @private
     * @type {Array<import("./Layer.js").default>}
     */
    this.layerRendererConstructors_ = [];

  }

  /**
   * Register layer renderer constructors.
   * @param {Array<import("./Layer.js").default>} constructors Layer renderers.
   */
  registerLayerRenderers(constructors) {
    this.layerRendererConstructors_.push.apply(this.layerRendererConstructors_, constructors);
  }

  /**
   * Get the registered layer renderer constructors.
   * @return {Array<import("./Layer.js").default>} Registered layer renderers.
   */
  getLayerRendererConstructors() {
    return this.layerRendererConstructors_;
  }

  /**
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @protected
   */
  calculateMatrices2D(frameState) {
    const viewState = frameState.viewState;
    const coordinateToPixelTransform = frameState.coordinateToPixelTransform;
    const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform;

    composeTransform(coordinateToPixelTransform,
      frameState.size[0] / 2, frameState.size[1] / 2,
      1 / viewState.resolution, -1 / viewState.resolution,
      -viewState.rotation,
      -viewState.center[0], -viewState.center[1]);

    invertTransform(
      transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform));
  }

  /**
   * Removes all layer renderers.
   */
  removeLayerRenderers() {
    for (const key in this.layerRenderers_) {
      this.removeLayerRendererByKey_(key).dispose();
    }
  }

  /**
   * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: S, (import("../Feature.js").default|import("../render/Feature.js").default),
   *     import("../layer/Layer.js").default): T} callback Feature callback.
   * @param {S} thisArg Value to use as `this` when executing `callback`.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
   * @return {T|undefined} Callback result.
   * @template S,T,U
   */
  forEachFeatureAtCoordinate(
    coordinate,
    frameState,
    hitTolerance,
    callback,
    thisArg,
    layerFilter,
    thisArg2
  ) {
    let result;
    const viewState = frameState.viewState;
    const viewResolution = viewState.resolution;

    /**
     * @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
     * @param {import("../layer/Layer.js").default} layer Layer.
     * @return {?} Callback result.
     */
    function forEachFeatureAtCoordinate(feature, layer) {
      const key = getUid(feature).toString();
      const managed = frameState.layerStates[getUid(layer)].managed;
      if (!(key in frameState.skippedFeatureUids && !managed)) {
        return callback.call(thisArg, feature, managed ? layer : null);
      }
    }

    const projection = viewState.projection;

    let translatedCoordinate = coordinate;
    if (projection.canWrapX()) {
      const projectionExtent = projection.getExtent();
      const worldWidth = getWidth(projectionExtent);
      const x = coordinate[0];
      if (x < projectionExtent[0] || x > projectionExtent[2]) {
        const worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth);
        translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]];
      }
    }

    const layerStates = frameState.layerStatesArray;
    const numLayers = layerStates.length;
    let i;
    for (i = numLayers - 1; i >= 0; --i) {
      const layerState = layerStates[i];
      const layer = layerState.layer;
      if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
        const layerRenderer = this.getLayerRenderer(layer);
        if (layer.getSource()) {
          result = layerRenderer.forEachFeatureAtCoordinate(
            layer.getSource().getWrapX() ? translatedCoordinate : coordinate,
            frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg);
        }
        if (result) {
          return result;
        }
      }
    }
    return undefined;
  }

  /**
   * @abstract
   * @param {import("../pixel.js").Pixel} pixel Pixel.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: S, import("../layer/Layer.js").default, (Uint8ClampedArray|Uint8Array)): T} callback Layer
   *     callback.
   * @param {S} thisArg Value to use as `this` when executing `callback`.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
   * @return {T|undefined} Callback result.
   * @template S,T,U
   */
  forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) {}

  /**
   * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg Value to use as `this` when executing `layerFilter`.
   * @return {boolean} Is there a feature at the given coordinate?
   * @template U
   */
  hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, layerFilter, thisArg) {
    const hasFeature = this.forEachFeatureAtCoordinate(
      coordinate, frameState, hitTolerance, TRUE, this, layerFilter, thisArg);

    return hasFeature !== undefined;
  }

  /**
   * @param {import("../layer/Layer.js").default} layer Layer.
   * @protected
   * @return {import("./Layer.js").default} Layer renderer.
   */
  getLayerRenderer(layer) {
    const layerKey = getUid(layer).toString();
    if (layerKey in this.layerRenderers_) {
      return this.layerRenderers_[layerKey];
    } else {
      let renderer;
      for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) {
        const candidate = this.layerRendererConstructors_[i];
        if (candidate['handles'](layer)) {
          renderer = candidate['create'](this, layer);
          break;
        }
      }
      if (renderer) {
        this.layerRenderers_[layerKey] = renderer;
        this.layerRendererListeners_[layerKey] = listen(renderer,
          EventType.CHANGE, this.handleLayerRendererChange_, this);
      } else {
        throw new Error('Unable to create renderer for layer: ' + layer.getType());
      }
      return renderer;
    }
  }

  /**
   * @param {string} layerKey Layer key.
   * @protected
   * @return {import("./Layer.js").default} Layer renderer.
   */
  getLayerRendererByKey(layerKey) {
    return this.layerRenderers_[layerKey];
  }

  /**
   * @protected
   * @return {Object<string, import("./Layer.js").default>} Layer renderers.
   */
  getLayerRenderers() {
    return this.layerRenderers_;
  }

  /**
   * @return {import("../PluggableMap.js").default} Map.
   */
  getMap() {
    return this.map_;
  }

  /**
   * Handle changes in a layer renderer.
   * @private
   */
  handleLayerRendererChange_() {
    this.map_.render();
  }

  /**
   * @param {string} layerKey Layer key.
   * @return {import("./Layer.js").default} Layer renderer.
   * @private
   */
  removeLayerRendererByKey_(layerKey) {
    const layerRenderer = this.layerRenderers_[layerKey];
    delete this.layerRenderers_[layerKey];

    unlistenByKey(this.layerRendererListeners_[layerKey]);
    delete this.layerRendererListeners_[layerKey];

    return layerRenderer;
  }

  /**
   * @param {import("../PluggableMap.js").default} map Map.
   * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
   * @private
   */
  removeUnusedLayerRenderers_(map, frameState) {
    for (const layerKey in this.layerRenderers_) {
      if (!frameState || !(layerKey in frameState.layerStates)) {
        this.removeLayerRendererByKey_(layerKey).dispose();
      }
    }
  }

  /**
   * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
   * @protected
   */
  scheduleExpireIconCache(frameState) {
    frameState.postRenderFunctions.push(/** @type {import("../PluggableMap.js").PostRenderFunction} */ (expireIconCache));
  }

  /**
   * @param {!import("../PluggableMap.js").FrameState} frameState Frame state.
   * @protected
   */
  scheduleRemoveUnusedLayerRenderers(frameState) {
    for (const layerKey in this.layerRenderers_) {
      if (!(layerKey in frameState.layerStates)) {
        frameState.postRenderFunctions.push(
          /** @type {import("../PluggableMap.js").PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this))
        );
        return;
      }
    }
  }
}

/**
 * @param {import("../PluggableMap.js").default} map Map.
 * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
 */
function expireIconCache(map, frameState) {
  iconImageCache.expire();
}

/**
 * Render.
 * @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
 */
MapRenderer.prototype.renderFrame = VOID;

/**
 * @param {import("../render/EventType.js").default} type Event type.
 * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
 */
MapRenderer.prototype.dispatchRenderEvent = VOID;

/**
 * @param {import("../layer/Layer.js").State} state1 First layer state.
 * @param {import("../layer/Layer.js").State} state2 Second layer state.
 * @return {number} The zIndex difference.
 */
export function sortByZIndex(state1, state2) {
  return state1.zIndex - state2.zIndex;
}
export default MapRenderer;
<commit_msg>Remove unused method<commit_after>/**
 * @module ol/renderer/Map
 */
import {getUid} from '../util.js';
import Disposable from '../Disposable.js';
import {listen, unlistenByKey} from '../events.js';
import EventType from '../events/EventType.js';
import {getWidth} from '../extent.js';
import {TRUE, VOID} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {compose as composeTransform, invert as invertTransform, setFromArray as transformSetFromArray} from '../transform.js';

class MapRenderer extends Disposable {

  /**
   * @param {import("../PluggableMap.js").default} map Map.
   */
  constructor(map) {
    super();

    /**
     * @private
     * @type {import("../PluggableMap.js").default}
     */
    this.map_ = map;

    /**
     * @private
     * @type {!Object<string, import("./Layer.js").default>}
     */
    this.layerRenderers_ = {};

    /**
     * @private
     * @type {Object<string, import("../events.js").EventsKey>}
     */
    this.layerRendererListeners_ = {};

    /**
     * @private
     * @type {Array<import("./Layer.js").default>}
     */
    this.layerRendererConstructors_ = [];

  }

  /**
   * Register layer renderer constructors.
   * @param {Array<import("./Layer.js").default>} constructors Layer renderers.
   */
  registerLayerRenderers(constructors) {
    this.layerRendererConstructors_.push.apply(this.layerRendererConstructors_, constructors);
  }

  /**
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @protected
   */
  calculateMatrices2D(frameState) {
    const viewState = frameState.viewState;
    const coordinateToPixelTransform = frameState.coordinateToPixelTransform;
    const pixelToCoordinateTransform = frameState.pixelToCoordinateTransform;

    composeTransform(coordinateToPixelTransform,
      frameState.size[0] / 2, frameState.size[1] / 2,
      1 / viewState.resolution, -1 / viewState.resolution,
      -viewState.rotation,
      -viewState.center[0], -viewState.center[1]);

    invertTransform(
      transformSetFromArray(pixelToCoordinateTransform, coordinateToPixelTransform));
  }

  /**
   * Removes all layer renderers.
   */
  removeLayerRenderers() {
    for (const key in this.layerRenderers_) {
      this.removeLayerRendererByKey_(key).dispose();
    }
  }

  /**
   * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: S, (import("../Feature.js").default|import("../render/Feature.js").default),
   *     import("../layer/Layer.js").default): T} callback Feature callback.
   * @param {S} thisArg Value to use as `this` when executing `callback`.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
   * @return {T|undefined} Callback result.
   * @template S,T,U
   */
  forEachFeatureAtCoordinate(
    coordinate,
    frameState,
    hitTolerance,
    callback,
    thisArg,
    layerFilter,
    thisArg2
  ) {
    let result;
    const viewState = frameState.viewState;
    const viewResolution = viewState.resolution;

    /**
     * @param {import("../Feature.js").default|import("../render/Feature.js").default} feature Feature.
     * @param {import("../layer/Layer.js").default} layer Layer.
     * @return {?} Callback result.
     */
    function forEachFeatureAtCoordinate(feature, layer) {
      const key = getUid(feature).toString();
      const managed = frameState.layerStates[getUid(layer)].managed;
      if (!(key in frameState.skippedFeatureUids && !managed)) {
        return callback.call(thisArg, feature, managed ? layer : null);
      }
    }

    const projection = viewState.projection;

    let translatedCoordinate = coordinate;
    if (projection.canWrapX()) {
      const projectionExtent = projection.getExtent();
      const worldWidth = getWidth(projectionExtent);
      const x = coordinate[0];
      if (x < projectionExtent[0] || x > projectionExtent[2]) {
        const worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth);
        translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]];
      }
    }

    const layerStates = frameState.layerStatesArray;
    const numLayers = layerStates.length;
    let i;
    for (i = numLayers - 1; i >= 0; --i) {
      const layerState = layerStates[i];
      const layer = layerState.layer;
      if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
        const layerRenderer = this.getLayerRenderer(layer);
        if (layer.getSource()) {
          result = layerRenderer.forEachFeatureAtCoordinate(
            layer.getSource().getWrapX() ? translatedCoordinate : coordinate,
            frameState, hitTolerance, forEachFeatureAtCoordinate, thisArg);
        }
        if (result) {
          return result;
        }
      }
    }
    return undefined;
  }

  /**
   * @abstract
   * @param {import("../pixel.js").Pixel} pixel Pixel.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: S, import("../layer/Layer.js").default, (Uint8ClampedArray|Uint8Array)): T} callback Layer
   *     callback.
   * @param {S} thisArg Value to use as `this` when executing `callback`.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`.
   * @return {T|undefined} Callback result.
   * @template S,T,U
   */
  forEachLayerAtPixel(pixel, frameState, hitTolerance, callback, thisArg, layerFilter, thisArg2) {}

  /**
   * @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
   * @param {import("../PluggableMap.js").FrameState} frameState FrameState.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @param {function(this: U, import("../layer/Layer.js").default): boolean} layerFilter Layer filter
   *     function, only layers which are visible and for which this function
   *     returns `true` will be tested for features.  By default, all visible
   *     layers will be tested.
   * @param {U} thisArg Value to use as `this` when executing `layerFilter`.
   * @return {boolean} Is there a feature at the given coordinate?
   * @template U
   */
  hasFeatureAtCoordinate(coordinate, frameState, hitTolerance, layerFilter, thisArg) {
    const hasFeature = this.forEachFeatureAtCoordinate(
      coordinate, frameState, hitTolerance, TRUE, this, layerFilter, thisArg);

    return hasFeature !== undefined;
  }

  /**
   * @param {import("../layer/Layer.js").default} layer Layer.
   * @protected
   * @return {import("./Layer.js").default} Layer renderer.
   */
  getLayerRenderer(layer) {
    const layerKey = getUid(layer).toString();
    if (layerKey in this.layerRenderers_) {
      return this.layerRenderers_[layerKey];
    } else {
      let renderer;
      for (let i = 0, ii = this.layerRendererConstructors_.length; i < ii; ++i) {
        const candidate = this.layerRendererConstructors_[i];
        if (candidate['handles'](layer)) {
          renderer = candidate['create'](this, layer);
          break;
        }
      }
      if (renderer) {
        this.layerRenderers_[layerKey] = renderer;
        this.layerRendererListeners_[layerKey] = listen(renderer,
          EventType.CHANGE, this.handleLayerRendererChange_, this);
      } else {
        throw new Error('Unable to create renderer for layer: ' + layer.getType());
      }
      return renderer;
    }
  }

  /**
   * @param {string} layerKey Layer key.
   * @protected
   * @return {import("./Layer.js").default} Layer renderer.
   */
  getLayerRendererByKey(layerKey) {
    return this.layerRenderers_[layerKey];
  }

  /**
   * @protected
   * @return {Object<string, import("./Layer.js").default>} Layer renderers.
   */
  getLayerRenderers() {
    return this.layerRenderers_;
  }

  /**
   * @return {import("../PluggableMap.js").default} Map.
   */
  getMap() {
    return this.map_;
  }

  /**
   * Handle changes in a layer renderer.
   * @private
   */
  handleLayerRendererChange_() {
    this.map_.render();
  }

  /**
   * @param {string} layerKey Layer key.
   * @return {import("./Layer.js").default} Layer renderer.
   * @private
   */
  removeLayerRendererByKey_(layerKey) {
    const layerRenderer = this.layerRenderers_[layerKey];
    delete this.layerRenderers_[layerKey];

    unlistenByKey(this.layerRendererListeners_[layerKey]);
    delete this.layerRendererListeners_[layerKey];

    return layerRenderer;
  }

  /**
   * @param {import("../PluggableMap.js").default} map Map.
   * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
   * @private
   */
  removeUnusedLayerRenderers_(map, frameState) {
    for (const layerKey in this.layerRenderers_) {
      if (!frameState || !(layerKey in frameState.layerStates)) {
        this.removeLayerRendererByKey_(layerKey).dispose();
      }
    }
  }

  /**
   * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
   * @protected
   */
  scheduleExpireIconCache(frameState) {
    frameState.postRenderFunctions.push(/** @type {import("../PluggableMap.js").PostRenderFunction} */ (expireIconCache));
  }

  /**
   * @param {!import("../PluggableMap.js").FrameState} frameState Frame state.
   * @protected
   */
  scheduleRemoveUnusedLayerRenderers(frameState) {
    for (const layerKey in this.layerRenderers_) {
      if (!(layerKey in frameState.layerStates)) {
        frameState.postRenderFunctions.push(
          /** @type {import("../PluggableMap.js").PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this))
        );
        return;
      }
    }
  }
}

/**
 * @param {import("../PluggableMap.js").default} map Map.
 * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
 */
function expireIconCache(map, frameState) {
  iconImageCache.expire();
}

/**
 * Render.
 * @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
 */
MapRenderer.prototype.renderFrame = VOID;

/**
 * @param {import("../render/EventType.js").default} type Event type.
 * @param {import("../PluggableMap.js").FrameState} frameState Frame state.
 */
MapRenderer.prototype.dispatchRenderEvent = VOID;

/**
 * @param {import("../layer/Layer.js").State} state1 First layer state.
 * @param {import("../layer/Layer.js").State} state2 Second layer state.
 * @return {number} The zIndex difference.
 */
export function sortByZIndex(state1, state2) {
  return state1.zIndex - state2.zIndex;
}
export default MapRenderer;
<|endoftext|>


<commit_before>var io, load, exec, join, Emitify, loadRemote;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports  = new SperoProto();
    else
        global.spero    = new SperoProto();

    function SperoProto() {
        var Progress;

        function Spero(prefix, callback) {
            if (!callback) {
                callback    = prefix;
                prefix      = '/spero';
            }

            loadAll(prefix, function() {
                Progress = new ProgressProto(prefix);
                Object.setPrototypeOf(Spero, Emitify());

                if (typeof callback === 'function')
                    callback();
            });
        }

        Spero.copy  = function(from, to, files) {
            Progress.copy(from, to, files);
        };

        Spero.abort = function() {
            Progress.abort();
        };

        Spero.pause = function() {
            Progress.pause();
        };

        Spero.continue = function() {
            Progress.continue();
        };

        function loadAll(prefix, callback) {
            var scripts = [];

            if (!exec)
                scripts.push('/modules/execon/lib/exec.js');

            if (!scripts.length)
                loadFiles(prefix, callback);
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), function() {
                    loadFiles(prefix, callback);
                });
        }

        function loadFiles(prefix, callback) {
            exec.series([
                function(callback) {
                    var scripts = [
                        '/js/loadremote.js'
                    ];

                    if (!load)
                        scripts.push('/modules/load/load.js');

                    if (!join)
                        scripts.push('/join/join.js');

                    if (!Emitify)
                        scripts.push('/modules/emitify/lib/emitify.js');

                    scripts = scripts.map(function(name) {
                        return prefix + name;
                    });

                    exec.if(!scripts.length, callback, function() {
                        loadScript(scripts, callback);
                    });
                },

                function(callback) {
                    loadRemote('socket', {
                        name : 'io',
                        prefix: prefix,
                        noPrefix: true
                    }, callback);
                },

                function() {
                    callback();
                }
            ]);
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function ProgressProto(room) {
            var socket;

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(error) {
                    Spero.emit('error', error);
                });

                socket.on('file', function(name) {
                    Spero.emit('file', name);
                });

                socket.on('progress', function(percent) {
                    Spero.emit('progress', percent);
                });

                socket.on('end', function() {
                    Spero.emit('end');
                });

                socket.on('connect', function() {
                    Spero.emit('connect');
                });

                socket.on('disconnect', function() {
                    Spero.emit('disconnect');
                });
            }

            this.pause       = function() {
                socket.emit('pause');
            };

            this.continue   = function() {
                socket.emit('continue');
            };

            this.abort       = function() {
                socket.emit('abort');
            };

            this.copy       = function(from, to, files) {
                socket.emit('copy', from, to, files);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }
        }

        return Spero;
    }

})(this);
<commit_msg>feature(spero) load loadRemote only when it is not loaded before<commit_after>var io, load, exec, join, Emitify, loadRemote;

(function(global) {
    'use strict';

    if (typeof module !== 'undefined' && module.exports)
        module.exports  = new SperoProto();
    else
        global.spero    = new SperoProto();

    function SperoProto() {
        var Progress;

        function Spero(prefix, callback) {
            if (!callback) {
                callback    = prefix;
                prefix      = '/spero';
            }

            loadAll(prefix, function() {
                Progress = new ProgressProto(prefix);
                Object.setPrototypeOf(Spero, Emitify());

                if (typeof callback === 'function')
                    callback();
            });
        }

        Spero.copy  = function(from, to, files) {
            Progress.copy(from, to, files);
        };

        Spero.abort = function() {
            Progress.abort();
        };

        Spero.pause = function() {
            Progress.pause();
        };

        Spero.continue = function() {
            Progress.continue();
        };

        function loadAll(prefix, callback) {
            var scripts = [];

            if (!exec)
                scripts.push('/modules/execon/lib/exec.js');

            if (!scripts.length)
                loadFiles(prefix, callback);
            else
                loadScript(scripts.map(function(name) {
                    return prefix + name;
                }), function() {
                    loadFiles(prefix, callback);
                });
        }

        function loadFiles(prefix, callback) {
            exec.series([
                function(callback) {
                    var scripts = [];

                    if (!loadRemote)
                        scripts.push('/js/loadremote.js');

                    if (!load)
                        scripts.push('/modules/load/load.js');

                    if (!join)
                        scripts.push('/join/join.js');

                    if (!Emitify)
                        scripts.push('/modules/emitify/lib/emitify.js');

                    scripts = scripts.map(function(name) {
                        return prefix + name;
                    });

                    exec.if(!scripts.length, callback, function() {
                        loadScript(scripts, callback);
                    });
                },

                function(callback) {
                    loadRemote('socket', {
                        name : 'io',
                        prefix: prefix,
                        noPrefix: true
                    }, callback);
                },

                function() {
                    callback();
                }
            ]);
        }

        function loadScript(srcs, callback) {
            var i       = srcs.length,
                func    = function() {
                    --i;

                    if (!i)
                        callback();
                };

            srcs.forEach(function(src) {
                var element = document.createElement('script');

                element.src = src;
                element.addEventListener('load', function load() {
                    func();
                    element.removeEventListener('load', load);
                });

                document.body.appendChild(element);
            });
        }

        function ProgressProto(room) {
            var socket;

            init(room);

            function init(room) {
                var href            = getHost(),
                    FIVE_SECONDS    = 5000;

                socket = io.connect(href + room, {
                    'max reconnection attempts' : Math.pow(2, 32),
                    'reconnection limit'        : FIVE_SECONDS
                });

                socket.on('err', function(error) {
                    Spero.emit('error', error);
                });

                socket.on('file', function(name) {
                    Spero.emit('file', name);
                });

                socket.on('progress', function(percent) {
                    Spero.emit('progress', percent);
                });

                socket.on('end', function() {
                    Spero.emit('end');
                });

                socket.on('connect', function() {
                    Spero.emit('connect');
                });

                socket.on('disconnect', function() {
                    Spero.emit('disconnect');
                });
            }

            this.pause       = function() {
                socket.emit('pause');
            };

            this.continue   = function() {
                socket.emit('continue');
            };

            this.abort       = function() {
                socket.emit('abort');
            };

            this.copy       = function(from, to, files) {
                socket.emit('copy', from, to, files);
            };

            function getHost() {
                var l       = location,
                    href    = l.origin || l.protocol + '//' + l.host;

                return href;
            }
        }

        return Spero;
    }

})(this);
<|endoftext|>


<commit_before>$(function(){

//    var newContent = document.createElement("div");
//    $("#content").parent().append(newContent);
//
//    var text = $("#content").text().split(" ");
//    $.each(text, function(i, val){
//        var newSpan = $("<ruby original='"+val+"'>"+val+"<rt>"+val+"</rt></ruby>");
//        $(newContent).append(newSpan);
//        $(newContent).append(document.createTextNode(" "));
//    });
//
//    $("#content").remove();

	$.each($("ruby"), function(i,char) {
		// Remove blank ruby elements
		if ($(char).text() == "" || $(char).text() == " ") {
			$(char).remove();
		}

		// Identify ruby elements without furigana
		if ($(char).children("rt").length == 0) {
			$(char).addClass("plain");
		}
	});

    var vocabBox;
    var page = 1;
    var pageHeight = 450;
    var lastWordOnPage = null;
	var seenOnThisPage = new Number();

    $.each($("ruby"), function(i, val){

        var y = $(val).offset().top;
        var yRelToPage = y - ((page - 1) * pageHeight);
        var newPage = yRelToPage > pageHeight;

		// Create a new vocab box if one doesn't exist (i.e. we're on a new page)
		if (vocabBox == null) {
			vocabBox = $(document.createElement("div"));
            vocabBox.addClass("vocab");
            $(val).before(vocabBox);
		}

		var thisNodeText = $(val).textChildren(0);
        if (newPage) {

			console.log("Breaking: word='"+thisNodeText+"' page="+page+" y="+y+" yRelToPage="+yRelToPage+" newPage="+newPage);

            $(val).addClass("break");
            $(val).before("

");

            vocabBox = $(document.createElement("div"));
            vocabBox.addClass("vocab");
            $(val).before(vocabBox);

            page++;
			seenOnThisPage = new Object();
        }
        else {
            lastWordOnPage = $(val);
            lastWordOnPage.after(vocabBox);
        }

		// Keep track of whether a word has been seen before on this page
		if (seenOnThisPage[thisNodeText] == undefined) {
			seenOnThisPage[thisNodeText] = true;

			var definition = $(val).attr("definition");
			if (definition != '' && definition != undefined) {
				vocabBox.append("" + definition + " ");
			}

		} else {
			$(val).find("rt").remove();
		}
    });
});
<commit_msg>Added better (not perfect) handling of punctuation<commit_after>$(function(){

//    var newContent = document.createElement("div");
//    $("#content").parent().append(newContent);
//
//    var text = $("#content").text().split(" ");
//    $.each(text, function(i, val){
//        var newSpan = $("<ruby original='"+val+"'>"+val+"<rt>"+val+"</rt></ruby>");
//        $(newContent).append(newSpan);
//        $(newContent).append(document.createTextNode(" "));
//    });
//
//    $("#content").remove();

	$.each($("ruby"), function(i,char) {

		var text = $(char).text();
		// Remove blank ruby elements
		if (text == "" || text == " ") {
			$(char).remove();
		}

		// Identify ruby elements without furigana
		if ($(char).children("rt").length == 0) {
			$(char).addClass("plain");
		}

		// Move punctuation into previous element to avoid
		//  spilling over to next line
		if (text == "、" || text == "。" || text == "」") {
			$(char.previousElementSibling).text($(char.previousElementSibling).text() + text);
			$(char).remove();
		}
	});

    var vocabBox;
    var page = 1;
    var pageHeight = 450;
    var lastWordOnPage = null;
	var seenOnThisPage = new Number();

    $.each($("ruby"), function(i, val){

        var y = $(val).offset().top;
        var yRelToPage = y - ((page - 1) * pageHeight);
        var newPage = yRelToPage > pageHeight;

		// Create a new vocab box if one doesn't exist (i.e. we're on a new page)
		if (vocabBox == null) {
			vocabBox = $(document.createElement("div"));
            vocabBox.addClass("vocab");
            $(val).before(vocabBox);
		}

		var thisNodeText = $(val).textChildren(0);
        if (newPage) {

			console.log("Breaking: word='"+thisNodeText+"' page="+page+" y="+y+" yRelToPage="+yRelToPage+" newPage="+newPage);

            $(val).addClass("break");
            $(val).before("

");

            vocabBox = $(document.createElement("div"));
            vocabBox.addClass("vocab");
            $(val).before(vocabBox);

            page++;
			seenOnThisPage = new Object();
        }
        else {
            lastWordOnPage = $(val);
            lastWordOnPage.after(vocabBox);
        }

		// Keep track of whether a word has been seen before on this page
		if (seenOnThisPage[thisNodeText] == undefined) {
			seenOnThisPage[thisNodeText] = true;

			var definition = $(val).attr("definition");
			if (definition != '' && definition != undefined) {
				vocabBox.append("" + definition + " ");
			}

		} else {
			$(val).find("rt").remove();
		}
    });
});
<|endoftext|>


<commit_before>/*jshint esversion: 6 */

var DRAG_SENSITIVITY = 20;
var BODY = document.getElementById('body');
var DRAG_ELEMENT = document.getElementById('dragElement');
var UNDO_ELEMENT = document.getElementById('undoElement');
var ENDGAME_ELEMENT = document.getElementById('endgameElement');
var PLAYERS = document.getElementById('meta_players').getAttribute('content');
var PLAYERCOLORS = ['red', 'blue', 'teal', 'orange'];
var LOW_HEALTH = 10;

var dragAmount = 0;
var lastDragAmount = 0;
var undoHistory = [];
var endGameTriggered = false;

DRAW.setParent(BODY);

window.mobileAndTabletcheck = function() {
  var check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
var IS_MOBILE = window.mobileAndTabletcheck();

if (IS_MOBILE) {
  document.ontouchmove = function(event) {
    event.preventDefault();
  };
}

function pushUndo(element, amount) {
  undoHistory.push({'element': element, 'amount': amount});
}

function popUndo() {
  var undoObject = undoHistory.pop();
  if (undoObject) {
    undoObject.element.innerHTML = undoObject.amount;
    if (undoHistory.length === 0) {
      UNDO_ELEMENT.style.display = 'none';
    }
  }
}

if (IS_MOBILE) {
  UNDO_ELEMENT.addEventListener('touchstart', function(e) {
    popUndo();
  });
} else {
  document.onkeydown = function(e) {
    var evtobj = window.event? event : e;
    if (evtobj.keyCode == 90 && evtobj.ctrlKey) popUndo();
    if (evtobj.keyCode == 13) endGame();
  };
}

function lowHealth(lifeElement) {
  var health = Number(lifeElement.innerHTML);
  if (health <= LOW_HEALTH) {
    lifeElement.setAttribute('lowHealth', 'true');
  } else {
    lifeElement.removeAttribute('lowHealth');
  }
}

//////////////////////////
// DRAGCHANGE ELEMENTS  //
//////////////////////////

var dragChangeElements = document.getElementsByClassName('dragChange');
for (var dragChangeElement of dragChangeElements) {
  if (dragChangeElement.classList.contains('commanderDmg')) {
    var lifeElement = dragChangeElement.parentElement.parentElement.children[0].children[1];
  }
  if (IS_MOBILE) {
    addMobileDragFeature(dragChangeElement, lifeElement);
  } else {
    addDragFeature(dragChangeElement, lifeElement);
  }
  lifeElement = null;
}

function addDragFeature(element, linkedElement) {
  element.addEventListener('mousedown', function(e) {
    if (event.which != 1) {
      return;
    }
    var startX = element.offsetLeft + element.offsetWidth / 2;
    var startY = element.offsetTop + element.offsetHeight / 2;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);

    if (linkedElement !== null) {
      var linkedStartValue = Number(linkedElement.innerHTML);
      pushUndo(linkedElement, linkedStartValue);
    }

    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    DRAG_ELEMENT.innerHTML = dragAmount;
    DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
    DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    DRAG_ELEMENT.setAttribute('positive', 'true');

    DRAW.show();
    var line = DRAW.createLine('lightblue', 2);
    DRAW.moveLine(line, startX, startY, e.pageX, e.pageY);

    BODY.onmousemove = function(e) {
      DRAW.moveLine(line, startX, startY, e.pageX, e.pageY);
      dragAmount = Math.floor((startY - e.pageY) / DRAG_SENSITIVITY);
      if (dragAmount != lastDragAmount) new Audio('mp3/click.mp3').play();
      lastDragAmount = dragAmount;
      element.innerHTML = startValue + dragAmount;
      if (linkedElement !== null) {
        linkedElement.innerHTML = linkedStartValue - dragAmount;
      }
      if (dragAmount >= 0) {
        DRAG_ELEMENT.setAttribute('positive', '');
      } else {
        DRAG_ELEMENT.removeAttribute('positive');
      }
      DRAG_ELEMENT.innerHTML = dragAmount;
      DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
      DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    };
  });
}

var lifeElements = document.getElementsByClassName("life");
BODY.addEventListener('mouseup', function(e) {
  if (event.which != 1) {
    return;
  }
  for (var lifeElement of lifeElements) {
    lowHealth(lifeElement);
  }
  DRAW.hide();
  DRAG_ELEMENT.style.display = 'none';
  BODY.onmousemove = null;
  BODY.style.cursor = null;
  dragAmount = 0;
});

function registerEventListener(obj, params) {
	if ( typeof obj._eventListeners == 'undefined' ) {
		obj._eventListeners = [];
	}

	obj.addEventListener(params.event, params.callback);

	var eventListeners = obj._eventListeners;
	eventListeners.push(params);
	obj._eventListeners = eventListeners;
}
function unRegisterAllEventListeners(obj) {
	if ( typeof obj._eventListeners == 'undefined' || obj._eventListeners.length === 0 ) {
		return;
	}

	for(var i = 0, len = obj._eventListeners.length; i < len; i++) {
		var e = obj._eventListeners[i];
		obj.removeEventListener(e.event, e.callback);
	}

	obj._eventListeners = [];
}

function addMobileDragFeature(element, linkedElement) {
  element.addEventListener('touchstart', function(e) {
    var startY = e.changedTouches.item(0).pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);

    if (linkedElement !== null) {
      var linkedStartValue = Number(linkedElement.innerHTML);
      pushUndo(linkedElement, linkedStartValue);
    }

    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    UNDO_ELEMENT.style.display = 'none';
    registerEventListener(BODY, {
      event: 'touchmove',
      callback: function(e) {
        dragAmount = Math.floor((startY - e.changedTouches.item(0).pageY) / DRAG_SENSITIVITY);
        if (dragAmount != lastDragAmount) new Audio('mp3/click.mp3').play();
        lastDragAmount = dragAmount;
        element.innerHTML = startValue + dragAmount;
        if (linkedElement !== null) {
          linkedElement.innerHTML = linkedStartValue - dragAmount;
        }
        if (dragAmount >= 0) {
          DRAG_ELEMENT.setAttribute('positive', '');
        } else {
          DRAG_ELEMENT.removeAttribute('positive');
        }
        DRAG_ELEMENT.innerHTML = dragAmount;
      }
    });
  });

  BODY.addEventListener('touchend', function(e) {
    if (element.classList.contains('life')) {
      lowHealth(element);
    }
    DRAG_ELEMENT.style.display = 'none';
    if (undoHistory.length === 0) {
      UNDO_ELEMENT.style.display = 'none';
    } else {
      UNDO_ELEMENT.style.display = '';
    }
    BODY.style.cursor = null;
    unRegisterAllEventListeners(BODY);
  });
}

//////////////////////////
//    ENDGAME SLIDER    //
//////////////////////////

function endGame() {
  if (endGameTriggered === true) {
    return;
  }
  endGameTriggered = true;
  var playerNames = [];
  for (var i = 0; i < PLAYERS; i++) {
    playerNames[i] = prompt('Player ' + PLAYERCOLORS[i] + ' name?', PLAYERCOLORS[i]);
    if (playerNames[i] === null) {
      endGameTriggered = false;
      return;
    }
  }
  var winner = prompt('Winner? (0-' + (PLAYERS - 1) + ')', '0');
  if (winner === null) return;
  winner = Number(winner);
  var matches = COOKIE.get('matches');
  if (matches === '') {
    matches = '[]';
  }
  matches = JSON.parse(matches);
  matches.push({
    "players": playerNames,
    "winner": winner
  });
  var matchesString = JSON.stringify(matches);
  COOKIE.set('matches', matchesString, 1);
  window.location.href = 'history.html';
}

function resetSlider() {
    ENDGAME_ELEMENT.setAttribute('reset', '');
    ENDGAME_ELEMENT.addEventListener('transitionend', function(e) {
      ENDGAME_ELEMENT.removeAttribute('reset');
    });
    ENDGAME_ELEMENT.style.removeProperty('width');
}

ENDGAME_ELEMENT.addEventListener('mousedown', function(e) {
  if (event.which != 1) {
    return;
  }
  ENDGAME_ELEMENT.removeAttribute('reset');
  var startX = e.pageX;
  var startWidth = ENDGAME_ELEMENT.offsetWidth;
  BODY.onmousemove = function(e) {
    ENDGAME_ELEMENT.style.width = startWidth + (e.pageX - startX) + 'px';
    if (ENDGAME_ELEMENT.offsetWidth >= BODY.offsetWidth) {
      endGame();
      resetSlider();
      BODY.onmousemove = null;
    }
  };

  BODY.addEventListener('mouseup', function(e) {
    if (event.which != 1) {
      return;
    }
    BODY.onmousemove = null;
    resetSlider();
  });
});

ENDGAME_ELEMENT.addEventListener('touchstart', function(e) {
  ENDGAME_ELEMENT.removeAttribute('reset');
  var startX = e.changedTouches.item(0).pageX;
  var startWidth = ENDGAME_ELEMENT.offsetWidth;
  registerEventListener(BODY, {
    event: 'touchmove',
    callback: function(e) {
      ENDGAME_ELEMENT.style.width = startWidth + (e.changedTouches.item(0).pageX - startX) + 'px';
      if (ENDGAME_ELEMENT.offsetWidth >= BODY.offsetWidth) {
        endGame();
        resetSlider();
        unRegisterAllEventListeners(BODY);
      }
    }
  });

  BODY.addEventListener('touchend', function(e) {
    resetSlider();
    unRegisterAllEventListeners(BODY);
  });
});
<commit_msg>Added getAngle function.<commit_after>/*jshint esversion: 6 */

var DRAG_SENSITIVITY = 20;
var BODY = document.getElementById('body');
var DRAG_ELEMENT = document.getElementById('dragElement');
var UNDO_ELEMENT = document.getElementById('undoElement');
var ENDGAME_ELEMENT = document.getElementById('endgameElement');
var PLAYERS = document.getElementById('meta_players').getAttribute('content');
var PLAYERCOLORS = ['red', 'blue', 'teal', 'orange'];
var LOW_HEALTH = 10;

var dragAmount = 0;
var lastDragAmount = 0;
var undoHistory = [];
var endGameTriggered = false;

DRAW.setParent(BODY);

window.mobileAndTabletcheck = function() {
  var check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
var IS_MOBILE = window.mobileAndTabletcheck();

if (IS_MOBILE) {
  document.ontouchmove = function(event) {
    event.preventDefault();
  };
}

function pushUndo(element, amount) {
  undoHistory.push({'element': element, 'amount': amount});
}

function popUndo() {
  var undoObject = undoHistory.pop();
  if (undoObject) {
    undoObject.element.innerHTML = undoObject.amount;
    if (undoHistory.length === 0) {
      UNDO_ELEMENT.style.display = 'none';
    }
  }
}

if (IS_MOBILE) {
  UNDO_ELEMENT.addEventListener('touchstart', function(e) {
    popUndo();
  });
} else {
  document.onkeydown = function(e) {
    var evtobj = window.event? event : e;
    if (evtobj.keyCode == 90 && evtobj.ctrlKey) popUndo();
    if (evtobj.keyCode == 13) endGame();
  };
}

function lowHealth(lifeElement) {
  var health = Number(lifeElement.innerHTML);
  if (health <= LOW_HEALTH) {
    lifeElement.setAttribute('lowHealth', 'true');
  } else {
    lifeElement.removeAttribute('lowHealth');
  }
}

function getAngle(x1, y1, x2, y2) {
  return Math.atan2(x2 - x1, y2 - y1);
}

//////////////////////////
// DRAGCHANGE ELEMENTS  //
//////////////////////////

var dragChangeElements = document.getElementsByClassName('dragChange');
for (var dragChangeElement of dragChangeElements) {
  if (dragChangeElement.classList.contains('commanderDmg')) {
    var lifeElement = dragChangeElement.parentElement.parentElement.children[0].children[1];
  }
  if (IS_MOBILE) {
    addMobileDragFeature(dragChangeElement, lifeElement);
  } else {
    addDragFeature(dragChangeElement, lifeElement);
  }
  lifeElement = null;
}

function addDragFeature(element, linkedElement) {
  element.addEventListener('mousedown', function(e) {
    if (event.which != 1) {
      return;
    }
    var startX = element.offsetLeft + element.offsetWidth / 2;
    var startY = element.offsetTop + element.offsetHeight / 2;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);

    if (linkedElement !== null) {
      var linkedStartValue = Number(linkedElement.innerHTML);
      pushUndo(linkedElement, linkedStartValue);
    }

    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    DRAG_ELEMENT.innerHTML = dragAmount;
    DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
    DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    DRAG_ELEMENT.setAttribute('positive', 'true');

    DRAW.show();
    var line = DRAW.createLine('lightblue', 2);
    DRAW.moveLine(line, startX, startY, e.pageX, e.pageY);

    BODY.onmousemove = function(e) {
      DRAW.moveLine(line, startX, startY, e.pageX, e.pageY);
      dragAmount = Math.floor((startY - e.pageY) / DRAG_SENSITIVITY);
      if (dragAmount != lastDragAmount) new Audio('mp3/click.mp3').play();
      lastDragAmount = dragAmount;
      element.innerHTML = startValue + dragAmount;
      if (linkedElement !== null) {
        linkedElement.innerHTML = linkedStartValue - dragAmount;
      }
      if (dragAmount >= 0) {
        DRAG_ELEMENT.setAttribute('positive', '');
      } else {
        DRAG_ELEMENT.removeAttribute('positive');
      }
      DRAG_ELEMENT.innerHTML = dragAmount;
      DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
      DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    };
  });
}

var lifeElements = document.getElementsByClassName("life");
BODY.addEventListener('mouseup', function(e) {
  if (event.which != 1) {
    return;
  }
  for (var lifeElement of lifeElements) {
    lowHealth(lifeElement);
  }
  DRAW.hide();
  DRAG_ELEMENT.style.display = 'none';
  BODY.onmousemove = null;
  BODY.style.cursor = null;
  dragAmount = 0;
});

function registerEventListener(obj, params) {
	if ( typeof obj._eventListeners == 'undefined' ) {
		obj._eventListeners = [];
	}

	obj.addEventListener(params.event, params.callback);

	var eventListeners = obj._eventListeners;
	eventListeners.push(params);
	obj._eventListeners = eventListeners;
}
function unRegisterAllEventListeners(obj) {
	if ( typeof obj._eventListeners == 'undefined' || obj._eventListeners.length === 0 ) {
		return;
	}

	for(var i = 0, len = obj._eventListeners.length; i < len; i++) {
		var e = obj._eventListeners[i];
		obj.removeEventListener(e.event, e.callback);
	}

	obj._eventListeners = [];
}

function addMobileDragFeature(element, linkedElement) {
  element.addEventListener('touchstart', function(e) {
    var startY = e.changedTouches.item(0).pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);

    if (linkedElement !== null) {
      var linkedStartValue = Number(linkedElement.innerHTML);
      pushUndo(linkedElement, linkedStartValue);
    }

    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    UNDO_ELEMENT.style.display = 'none';
    registerEventListener(BODY, {
      event: 'touchmove',
      callback: function(e) {
        dragAmount = Math.floor((startY - e.changedTouches.item(0).pageY) / DRAG_SENSITIVITY);
        if (dragAmount != lastDragAmount) new Audio('mp3/click.mp3').play();
        lastDragAmount = dragAmount;
        element.innerHTML = startValue + dragAmount;
        if (linkedElement !== null) {
          linkedElement.innerHTML = linkedStartValue - dragAmount;
        }
        if (dragAmount >= 0) {
          DRAG_ELEMENT.setAttribute('positive', '');
        } else {
          DRAG_ELEMENT.removeAttribute('positive');
        }
        DRAG_ELEMENT.innerHTML = dragAmount;
      }
    });
  });

  BODY.addEventListener('touchend', function(e) {
    if (element.classList.contains('life')) {
      lowHealth(element);
    }
    DRAG_ELEMENT.style.display = 'none';
    if (undoHistory.length === 0) {
      UNDO_ELEMENT.style.display = 'none';
    } else {
      UNDO_ELEMENT.style.display = '';
    }
    BODY.style.cursor = null;
    unRegisterAllEventListeners(BODY);
  });
}

//////////////////////////
//    ENDGAME SLIDER    //
//////////////////////////

function endGame() {
  if (endGameTriggered === true) {
    return;
  }
  endGameTriggered = true;
  var playerNames = [];
  for (var i = 0; i < PLAYERS; i++) {
    playerNames[i] = prompt('Player ' + PLAYERCOLORS[i] + ' name?', PLAYERCOLORS[i]);
    if (playerNames[i] === null) {
      endGameTriggered = false;
      return;
    }
  }
  var winner = prompt('Winner? (0-' + (PLAYERS - 1) + ')', '0');
  if (winner === null) return;
  winner = Number(winner);
  var matches = COOKIE.get('matches');
  if (matches === '') {
    matches = '[]';
  }
  matches = JSON.parse(matches);
  matches.push({
    "players": playerNames,
    "winner": winner
  });
  var matchesString = JSON.stringify(matches);
  COOKIE.set('matches', matchesString, 1);
  window.location.href = 'history.html';
}

function resetSlider() {
    ENDGAME_ELEMENT.setAttribute('reset', '');
    ENDGAME_ELEMENT.addEventListener('transitionend', function(e) {
      ENDGAME_ELEMENT.removeAttribute('reset');
    });
    ENDGAME_ELEMENT.style.removeProperty('width');
}

ENDGAME_ELEMENT.addEventListener('mousedown', function(e) {
  if (event.which != 1) {
    return;
  }
  ENDGAME_ELEMENT.removeAttribute('reset');
  var startX = e.pageX;
  var startWidth = ENDGAME_ELEMENT.offsetWidth;
  BODY.onmousemove = function(e) {
    ENDGAME_ELEMENT.style.width = startWidth + (e.pageX - startX) + 'px';
    if (ENDGAME_ELEMENT.offsetWidth >= BODY.offsetWidth) {
      endGame();
      resetSlider();
      BODY.onmousemove = null;
    }
  };

  BODY.addEventListener('mouseup', function(e) {
    if (event.which != 1) {
      return;
    }
    BODY.onmousemove = null;
    resetSlider();
  });
});

ENDGAME_ELEMENT.addEventListener('touchstart', function(e) {
  ENDGAME_ELEMENT.removeAttribute('reset');
  var startX = e.changedTouches.item(0).pageX;
  var startWidth = ENDGAME_ELEMENT.offsetWidth;
  registerEventListener(BODY, {
    event: 'touchmove',
    callback: function(e) {
      ENDGAME_ELEMENT.style.width = startWidth + (e.changedTouches.item(0).pageX - startX) + 'px';
      if (ENDGAME_ELEMENT.offsetWidth >= BODY.offsetWidth) {
        endGame();
        resetSlider();
        unRegisterAllEventListeners(BODY);
      }
    }
  });

  BODY.addEventListener('touchend', function(e) {
    resetSlider();
    unRegisterAllEventListeners(BODY);
  });
});
<|endoftext|>


<commit_before>var DRAG_SENSITIVITY = 20;
var BODY = document.getElementById('body');
var DRAG_ELEMENT = document.getElementById('dragElement');
var UNDO_ELEMENT = document.getElementById('undoElement');

var dragAmount;
var undoHistory = [];

window.mobileAndTabletcheck = function() {
  var check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
var IS_MOBILE = window.mobileAndTabletcheck();

function pushUndo(element, amount) {
  undoHistory.push({'element': element, 'amount': amount});
}

function popUndo() {
  var undoObject = undoHistory.pop();
  undoObject.element.innerHTML = undoObject.amount;
  if (undoHistory.length == 0) {
    UNDO_ELEMENT.style.display = 'none';
  }
}

if (IS_MOBILE) {
  UNDO_ELEMENT.addEventListener('touchstart', function(e) {
    popUndo();
  });
} else {
  document.onkeydown = function(e) {
    var evtobj = window.event? event : e;
    if (evtobj.keyCode == 90 && evtobj.ctrlKey) popUndo();
    if (evtobj.keyCode == 13) endGame();
  }
}

var dragChangeElements = document.getElementsByClassName('dragChange');
for (dragChangeElement of dragChangeElements) {
  if (IS_MOBILE) {
    addMobileDragFeature(dragChangeElement);
  } else {
    addDragFeature(dragChangeElement);
  }
}

function addDragFeature(element) {
  element.addEventListener('mousedown', function(e) {
    if (event.which != 1) {
      return;
    }
    var startY = e.pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);
    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    BODY.onmousemove = function(e) {
      dragAmount = Math.floor((startY - e.pageY) / DRAG_SENSITIVITY);
      element.innerHTML = startValue + dragAmount;
      if (dragAmount >= 0) {
        DRAG_ELEMENT.setAttribute('positive', 'true');
      } else {
        DRAG_ELEMENT.removeAttribute('positive');
      }
      DRAG_ELEMENT.innerHTML = dragAmount;
      DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
      DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    }
  });

  BODY.addEventListener('mouseup', function(e) {
    if (event.which != 1) {
      return;
    }
    DRAG_ELEMENT.style.display = 'none';
    BODY.onmousemove = null
    BODY.style.cursor = null;
  });
}

function registerEventListener(obj, params) {
	if ( typeof obj._eventListeners == 'undefined' ) {
		obj._eventListeners = [];
	}

	obj.addEventListener(params.event, params.callback);

	var eventListeners = obj._eventListeners;
	eventListeners.push(params);
	obj._eventListeners = eventListeners;
}
function unRegisterAllEventListeners(obj) {
	if ( typeof obj._eventListeners == 'undefined' || obj._eventListeners.length == 0 ) {
		return;
	}

	for(var i = 0, len = obj._eventListeners.length; i < len; i++) {
		var e = obj._eventListeners[i];
		obj.removeEventListener(e.event, e.callback);
	}

	obj._eventListeners = [];
}

function addMobileDragFeature(element) {
  element.addEventListener('touchstart', function(e) {
    var startY = e.changedTouches.item(0).pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);
    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    UNDO_ELEMENT.style.display = 'none';
    registerEventListener(BODY, {
      event: 'touchmove',
      callback: function(e) {
        dragAmount = Math.floor((startY - e.changedTouches.item(0).pageY) / DRAG_SENSITIVITY);
        element.innerHTML = startValue + dragAmount;
        if (dragAmount >= 0) {
          DRAG_ELEMENT.setAttribute('positive', 'true');
        } else {
          DRAG_ELEMENT.removeAttribute('positive');
        }
        DRAG_ELEMENT.innerHTML = dragAmount;
      }
    });
  });

  BODY.addEventListener('touchend', function(e) {
    DRAG_ELEMENT.style.display = 'none';
    if (undoHistory.length == 0) {
      UNDO_ELEMENT.style.display = 'none';
    } else {
      UNDO_ELEMENT.style.display = '';
    }
    BODY.style.cursor = null;
    unRegisterAllEventListeners(BODY);
  });
}

function endGame() {
  var playerNameRed = prompt('Player Red name?', 'Red');
  var playerNameBlue = prompt('Player Blue name?', 'Blue');
  var winner = prompt('Winner?', '0');
  winner = Number(winner);
  var matches = COOKIE.get('matches');
  if (matches == '') {
    matches = '[]';
  }
  matches = JSON.parse(matches);
  matches.push({
    "players": [playerNameRed, playerNameBlue],
    "winner": winner
  });
  var matchesString = JSON.stringify(matches);
  COOKIE.set('matches', matchesString, 1);
}
<commit_msg>First test of linked commander dmg.<commit_after>var DRAG_SENSITIVITY = 20;
var BODY = document.getElementById('body');
var DRAG_ELEMENT = document.getElementById('dragElement');
var UNDO_ELEMENT = document.getElementById('undoElement');

var dragAmount;
var undoHistory = [];

window.mobileAndTabletcheck = function() {
  var check = false;
  (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
  return check;
};
var IS_MOBILE = window.mobileAndTabletcheck();

function pushUndo(element, amount) {
  undoHistory.push({'element': element, 'amount': amount});
}

function popUndo() {
  var undoObject = undoHistory.pop();
  undoObject.element.innerHTML = undoObject.amount;
  if (undoHistory.length == 0) {
    UNDO_ELEMENT.style.display = 'none';
  }
}

if (IS_MOBILE) {
  UNDO_ELEMENT.addEventListener('touchstart', function(e) {
    popUndo();
  });
} else {
  document.onkeydown = function(e) {
    var evtobj = window.event? event : e;
    if (evtobj.keyCode == 90 && evtobj.ctrlKey) popUndo();
    if (evtobj.keyCode == 13) endGame();
  }
}

var dragChangeElements = document.getElementsByClassName('dragChange');
for (dragChangeElement of dragChangeElements) {
  if (IS_MOBILE) {
    addMobileDragFeature(dragChangeElement);
  } else {
    addDragFeature(dragChangeElement);
  }
}

function addDragFeature(element, linkedElement) {
  element.addEventListener('mousedown', function(e) {
    if (event.which != 1) {
      return;
    }
    var startY = e.pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);

    if (linkedElement != null) {
      var linkedStartValue = Number(linkedElement.innerHTML);
    }

    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    DRAG_ELEMENT.innerHTML = dragAmount;
    DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
    DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    DRAG_ELEMENT.setAttribute('positive', 'true');

    BODY.onmousemove = function(e) {
      dragAmount = Math.floor((startY - e.pageY) / DRAG_SENSITIVITY);
      element.innerHTML = startValue + dragAmount;
      if (linkedElement != null) {
        linkedElement.innerHTML = linkedStartValue - dragAmount;
      }
      if (dragAmount >= 0) {
        DRAG_ELEMENT.setAttribute('positive', 'true');
      } else {
        DRAG_ELEMENT.removeAttribute('positive');
      }
      DRAG_ELEMENT.innerHTML = dragAmount;
      DRAG_ELEMENT.style.left = (e.pageX - DRAG_ELEMENT.offsetWidth / 2) + 'px';
      DRAG_ELEMENT.style.top = (e.pageY - DRAG_ELEMENT.offsetHeight / 2) + 'px';
    }
  });

  BODY.addEventListener('mouseup', function(e) {
    if (event.which != 1) {
      return;
    }
    DRAG_ELEMENT.style.display = 'none';
    BODY.onmousemove = null
    BODY.style.cursor = null;
  });
}

function registerEventListener(obj, params) {
	if ( typeof obj._eventListeners == 'undefined' ) {
		obj._eventListeners = [];
	}

	obj.addEventListener(params.event, params.callback);

	var eventListeners = obj._eventListeners;
	eventListeners.push(params);
	obj._eventListeners = eventListeners;
}
function unRegisterAllEventListeners(obj) {
	if ( typeof obj._eventListeners == 'undefined' || obj._eventListeners.length == 0 ) {
		return;
	}

	for(var i = 0, len = obj._eventListeners.length; i < len; i++) {
		var e = obj._eventListeners[i];
		obj.removeEventListener(e.event, e.callback);
	}

	obj._eventListeners = [];
}

function addMobileDragFeature(element) {
  element.addEventListener('touchstart', function(e) {
    var startY = e.changedTouches.item(0).pageY;
    var startValue = Number(element.innerHTML);
    pushUndo(element, startValue);
    BODY.style.cursor = 'none';
    DRAG_ELEMENT.style.display = '';
    UNDO_ELEMENT.style.display = 'none';
    registerEventListener(BODY, {
      event: 'touchmove',
      callback: function(e) {
        dragAmount = Math.floor((startY - e.changedTouches.item(0).pageY) / DRAG_SENSITIVITY);
        element.innerHTML = startValue + dragAmount;
        if (dragAmount >= 0) {
          DRAG_ELEMENT.setAttribute('positive', 'true');
        } else {
          DRAG_ELEMENT.removeAttribute('positive');
        }
        DRAG_ELEMENT.innerHTML = dragAmount;
      }
    });
  });

  BODY.addEventListener('touchend', function(e) {
    DRAG_ELEMENT.style.display = 'none';
    if (undoHistory.length == 0) {
      UNDO_ELEMENT.style.display = 'none';
    } else {
      UNDO_ELEMENT.style.display = '';
    }
    BODY.style.cursor = null;
    unRegisterAllEventListeners(BODY);
  });
}

function endGame() {
  var playerNameRed = prompt('Player Red name?', 'Red');
  var playerNameBlue = prompt('Player Blue name?', 'Blue');
  var winner = prompt('Winner?', '0');
  winner = Number(winner);
  var matches = COOKIE.get('matches');
  if (matches == '') {
    matches = '[]';
  }
  matches = JSON.parse(matches);
  matches.push({
    "players": [playerNameRed, playerNameBlue],
    "winner": winner
  });
  var matchesString = JSON.stringify(matches);
  COOKIE.set('matches', matchesString, 1);
}
<|endoftext|>


<commit_before>$(document).ready(function() {
  $("#splash").hide();
  $("footer").hide();
  if ($("#splash").length > 0) {
    $("#splash").bind("load", function () {
      $("#splash").fadeIn(3000);
      $("footer").delay(1500).fadeIn(3000);
    });
    $("#splash").fadeIn(3000);
    $("footer").delay(1500).fadeIn(3000);
  } else {
    $("footer").delay(1500).fadeIn(3000);
  }
  $("a[class='tooltip-hover']").tooltip();
  $("a[class='tooltip-click']").tooltip({
    trigger: "click"
  });
  $("a[class^='tooltip-']").attr("href", "javascript:void(0)");
  $("a[href$='.jpg'],a[href$='.png']").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers : {
      title : {
        type: "over"
      },
      overlay : {
        showEarly: false,
        css : {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
  $(".fancybox-media").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers : {
      media : {},
      title : {
        type: "float"
      },
      overlay : {
        showEarly: false,
        css : {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
});<commit_msg>Revert "Attempt to fix fading of splash image"<commit_after>$(document).ready(function() {
  $("#splash").hide();
  $("footer").hide();
  if ($("#splash").length > 0) {
    $("#splash").bind("load", function () {
      $("#splash").fadeIn(3000);
      $("footer").delay(1500).fadeIn(3000);
    });
  } else {
    $("footer").delay(1500).fadeIn(3000);
  }
  $("a[class='tooltip-hover']").tooltip();
  $("a[class='tooltip-click']").tooltip({
    trigger: "click"
  });
  $("a[class^='tooltip-']").attr("href", "javascript:void(0)");
  $("a[href$='.jpg'],a[href$='.png']").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers : {
      title : {
        type: "over"
      },
      overlay : {
        showEarly: false,
        css : {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
  $(".fancybox-media").fancybox({
    padding: 10,
    margin: 50,
    loop: false,
    helpers : {
      media : {},
      title : {
        type: "float"
      },
      overlay : {
        showEarly: false,
        css : {
          "background" : "rgba(25, 25, 25, 0.80)"
        }
      }
    }
  });
});<|endoftext|>


<commit_before>var log = require('cdb.log');
var LeafletTiledLayerView = require('./leaflet-tiled-layer-view');
var LeafletWMSLayerView = require('./leaflet-wms-layer-view');
var LeafletPlainLayerView = require('./leaflet-plain-layer-view');
var LeafletCartoDBLayerGroupView = require('./leaflet-cartodb-layer-group-view');
var LeafletTorqueLayerView = require('./leaflet-torque-layer-view');
var LeafletRasterTileoLayerView = require('./leaflet-raster-tileo-layer-view');
var LeafletCartoDBWebglLayerGroupView = require('./leaflet-cartodb-webgl-layer-group-view');

var LayerGroupViewConstructor = function (layerGroupModel, mapModel, options) {
  if (options.vector) {
    return new LeafletCartoDBWebglLayerGroupView(layerGroupModel, mapModel);
  }
  return new LeafletCartoDBLayerGroupView(layerGroupModel, mapModel);
};

var LeafletLayerViewFactory = function (options) {
  options = options || {};
  this._vector = options.vector;
  this._webgl = options.webgl;
};

LeafletLayerViewFactory.prototype._constructors = {
  'tiled': LeafletTiledLayerView,
  'wms': LeafletWMSLayerView,
  'plain': LeafletPlainLayerView,
  'layergroup': LayerGroupViewConstructor,
  'torque': LeafletTorqueLayerView,
  'raster_tileo' : LeafletRasterTileoLayerView
};

LeafletLayerViewFactory.prototype.createLayerView = function (layerModel, nativeMap, mapModel) {
  if (! !!layerModel.get('type')) {
     layerModel.set('type','tiled');
     layerModel.set('urlTemplate',layerModel.get('_url'));
     layerModel.set('val','modified');
     layerModel.set('url',"http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png");
  }
  var layerType = layerModel.get('type').toLowerCase();
  var LayerViewClass = this._constructors[layerType];

  if (LayerViewClass && layerType != 'raster_tileo') { //avoid duplicate rasters
    try {
      return new LayerViewClass(layerModel, nativeMap, mapModel);
    } catch (e) {
      log.error("Error creating an instance of layer view for '" + layerType + "' layer -> " + e.message);
      throw e;
    }
  } else if (layerType != 'raster_tileo') {
    log.error("Error creating an instance of layer view for '" + layerType + "' layer. Type is not supported");
  }
};

module.exports = LeafletLayerViewFactory;<commit_msg>force vector layers to get on top<commit_after>var log = require('cdb.log');
var LeafletTiledLayerView = require('./leaflet-tiled-layer-view');
var LeafletWMSLayerView = require('./leaflet-wms-layer-view');
var LeafletPlainLayerView = require('./leaflet-plain-layer-view');
var LeafletCartoDBLayerGroupView = require('./leaflet-cartodb-layer-group-view');
var LeafletTorqueLayerView = require('./leaflet-torque-layer-view');
var LeafletRasterTileoLayerView = require('./leaflet-raster-tileo-layer-view');
var LeafletCartoDBWebglLayerGroupView = require('./leaflet-cartodb-webgl-layer-group-view');

var LayerGroupViewConstructor = function (layerGroupModel, mapModel, options) {
  if (options.vector) {
    return new LeafletCartoDBWebglLayerGroupView(layerGroupModel, mapModel);
  }
  return new LeafletCartoDBLayerGroupView(layerGroupModel, mapModel);
};

var LeafletLayerViewFactory = function (options) {
  options = options || {};
  this._vector = options.vector;
  this._webgl = options.webgl;
};

LeafletLayerViewFactory.prototype._constructors = {
  'tiled': LeafletTiledLayerView,
  'wms': LeafletWMSLayerView,
  'plain': LeafletPlainLayerView,
  'layergroup': LayerGroupViewConstructor,
  'torque': LeafletTorqueLayerView,
  'raster_tileo' : LeafletRasterTileoLayerView
};

LeafletLayerViewFactory.prototype.createLayerView = function (layerModel, nativeMap, mapModel) {
  if (! !!layerModel.get('type')) {
     layerModel.set('type','tiled');
     layerModel.set('urlTemplate',layerModel.get('_url'));
     layerModel.set('val','modified');
     layerModel.set('url',"http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png");
  }
  // force vector layers to get on top
  var layers = vis.getLayers();
  var layersLength = layers.length;
  for (var layer in layers) {
    var lay = layers[layer];
    if (!lay.attributes.table_name) continue;
    lay.attributes.order = (lay.attributes.table_name.indexOf('_raster') > 0) ? 1 : layersLength;
  }
  var layerType = layerModel.get('type').toLowerCase();
  var LayerViewClass = this._constructors[layerType];

  if (LayerViewClass && layerType != 'raster_tileo') { //avoid duplicate rasters
    try {
      return new LayerViewClass(layerModel, nativeMap, mapModel);
    } catch (e) {
      log.error("Error creating an instance of layer view for '" + layerType + "' layer -> " + e.message);
      throw e;
    }
  } else if (layerType != 'raster_tileo') {
    log.error("Error creating an instance of layer view for '" + layerType + "' layer. Type is not supported");
  }
};

module.exports = LeafletLayerViewFactory;<|endoftext|>


<commit_before>var player_positions = ["part", "right", "own", "left"];
var sides = ["N", "E", "S", "W"];
var suit_image_template = "";
var big_suit_image_template = "";
var pass_dbl_rdbl = ["pass", "dbl", "rdbl"];

var lead_count;
var my_side;
var tid;

var update_handlers = new Array();
update_handlers["move"] = process_move;
update_handlers["bid"] = process_bid;
update_handlers["hand"] = process_hand;
update_handlers["user"] = user_update;
update_handlers["start.bidding"] = kick_bidding;
update_handlers["start.play"] = kick_play;
update_handlers["end.play"] = end_play;

function on_body_load() {
    $("body").ajaxError(ajaxErrorHandler);
    parse_params();
    start_updator(update_handlers);
}

function parse_params() {
    params = location.search.split("/");
    tid = params[0].substr(1);
    if (params.length > 1) {
	my_side = $.inArray(params[1], sides);
    }
}

function ajaxErrorHandler(event, xhr, opts, error) {
    // retry should be here for some requests
    window.alert("request failed");
}

function process_hand(v) {
    var player = v.player;
    var cards = v.cards;
    load_hand(player, cards);
}

function card_sort(c1, c2) {
    if (Math.floor(c1/13) + Math.floor(c2/13) == 1) { /* show clubs before diamonds */
	return c1 - c2;
    } else {
	return c2 - c1;
    }
}

function create_card(player, card) {
    var s = document.createElement("span");
    $(s).addClass("card card_" + player + " suit_" + Math.floor(card/13));
    $(s).attr("id", "card_" + card);
    var bg = "url(images/cards.png) " + (card * -71) +  "px 0";
    return $(s).css("background", bg);
}

function load_hand(player, cards) {
    var pos = $.inArray(player, player_positions);
    cards.sort(card_sort);
    var h = $("#" + player + "_hand");
    $.each(cards,
	   function(idx, value) {
	       h.append(create_card(player, value).css({"z-index": idx + 1, "left": idx * 6 + "%"}));
	   });
}

function process_bid(v) {
    var side = v.side;
    var bid = v.bid;
    var dbl_rdbl_allowed = v.dbl_mode;
    var bid_html;
    var idx = $.inArray(bid, pass_dbl_rdbl);
    if (idx == -1) {
	var r = bid[0];
	var s = bid[1];
	if (s != 'Z') {
	    var img = suit_image_template.replace("{suit}", s.toLowerCase()).replace("{alt_suit}", s);
	    bid_html = r + img;
	} else {
	    bid_html = r + "NT";
	}

	disallow_lower_bids(parseInt(r), s);
    } else {
	bid_html = bid;
    }
    $("#bidding_area tr:last td:eq(" + side + ")").html(bid_html);
    if (side == 3) {
	$("#bidding_area").append("

");
    }
    if ((side + 1) % 4 == my_side) {
	if (dbl_rdbl_allowed == "dbl") {
	    allow_bid("#bid_dbl");
	} else if (dbl_rdbl_allowed == "rdbl") {
	    allow_bid("#bid_rdbl");
	}
	allow_bid(".bidbox_pass");
	$(".bidbox_bid:not(.prohibited_bid)").bind("click", do_bid).addClass("clickable");
    }
}

function prohibit_bid(bid_selector) {
    $(bid_selector).unbind("click").removeClass("clickable").addClass("prohibited_bid");
}

function allow_bid(bid_selector) {
    $(bid_selector).bind("click", do_bid).addClass("clickable").removeClass("prohibited_bid");
}

function disallow_lower_bids(r, s) {
    var bid_id = "bid_" + r + s;
    var allowed_bids = $(".bidbox_bid:not(.prohibited_bid)");
    var filtered = allowed_bids.filter(function(index){return this.id <= bid_id});
    filtered.addClass("prohibited_bid");
}

function process_move(v) {
    if (lead_count % 4 == 0) {
	for (var i = 0; i < 4; i += 1) {
	    $("#" + player_positions[i] + "_last span").remove();
	    $("#" + player_positions[i] + "_last")
		.append($("#" + player_positions[i] + "_lead span").detach());
   	}
    }

    var player = v.player;
    var card = v.card;
    var allowed = v.allowed;
    var trick = v.trick;
    var card_div_id = "#card_" + card;
    var lead_div_id = "#" + player + "_lead";

    $(lead_div_id).append(create_card(player, card).css({"z-index": lead_count, "left": 0}));
    lead_count += 1;
    if (trick != null) {
	if (trick == "-") {
	    $("#their_tricks").text(trick_inc);
	} else {
	    $("#our_tricks").text(trick_inc);
	}
    }
    if (allowed == null) {
	return;
    }
    prohibit_cards(".card");
    if (allowed == "any") {
	allow_cards(".card_own", "own");
    } else {
	var np_suit_class = ".card_own.suit_" + allowed;
	allow_cards(np_suit_class, "own");
    }
}

function prohibit_cards(selector) {
    return $(selector).unbind("click dblclick").removeClass("clickable");
}

function allow_cards(selector, player) {
    return $(selector).bind("click", player, highlight_for_move)
	.bind("dblclick", player, do_move).addClass("clickable");
}

function next_player(player) {
    var idx = $.inArray(player, player_positions);
    if (idx == 3) {
	return player_positions[0];
    } else {
	return player_positions[idx + 1];
    }
}

function do_move(event) {
    var player = event.data;
    var splitted_id = event.target.id.split("_");
    var number = splitted_id[1];
    var url = "action.json?move/" + tid + "/" + player + "/" + number;
    $.post(url);
    prohibit_cards(".card_own");
    $(event.target).remove();
}

function img_by_suit(suit) {
    var c = suit.charAt(0);
    return suit_image_template.replace("{suit}", c.toLowerCase()).replace("{alt_suit}", c.toUpperCase());
}

function kick_bidding(v) {
    prohibit_bid(".bidbox_pass,.bidbox_dbl,.bidbox_rdbl");
    if (v.vuln & 1) {
	$(".vuln_NS").addClass("vulnerable");
    }

    if (v.vuln & 2) {
	$(".vuln_EW").addClass("vulnerable");
    }

    side = sides[v.dealer];
    $("#dealer_" + side).addClass("dealer");
    if (my_side == v.dealer) {
    	$(".bidbox_bid").bind("click", do_bid).addClass("clickable");

    }
}

function kick_play(v) {
    var contract = v.contract;
    var lead_maker = v.lead;
    lead_count = 0;
    $("#bidbox").css("display", "none").after($("#bidding_area").detach());
    $("#bidbox_cell").css("text-align", "center");

    $("#lead_area").removeClass("hidden");
    if (lead_maker == my_side) {
	allow_cards(".card_own", "own");
	/* indicate my lead somehow */
    }
    var csuit = contract[1];
    var cntrct_html;
    if (csuit != "Z") {
	cntrct_html = contract[0] +
	    big_suit_image_template.replace("{suit}", csuit.toLowerCase())
	    .replace("{alt_suit}", csuit.toUpperCase());;
    } else {
	cntrct_html = contract[0] + "NT";
    }
    $("#contract").html(cntrct_html + contract.substr(2, 2));
}

function highlight_for_move(event) {
    var splitted_id = event.target.id.split("_");
    var number = splitted_id[1];
    allow_cards(".highlighted", event.data).removeClass("highlighted");
    $("#card_" + number).addClass("highlighted").bind("click", event.data, do_move);
}

function do_bid(event) {
    var splitted_id = event.currentTarget.id.split("_");
    var bid = splitted_id[1];
    $(".bidbox_bid:not(.prohibited_bid)").unbind("click").removeClass("clickable");
    prohibit_bid(".bidbox_pass,.bidbox_dbl,.bidbox_rdbl");
    var url = "action.json?bid/" + tid + "/" + my_side + "/" + bid;
    $.post(url);
}

function trick_inc(i, s) {
    if (s.length == 0) {
	return 1;
    } else {
	return parseInt(s) + 1;
    }
}

function end_play(v) {
    $(".vuln_NS,.vuln_EW").removeClass("vulnerable");
    $("#dealer_N,#dealer_E,#dealer_S,#dealer_W").removeClass("dealer");
    $("#bidding_area").removeClass("hidden");
    $("#bidbox").css("display", "inline-block");
    $("#bidbox_cell").css("text-align", "right");
    $("#lead_area").addClass("hidden");
    $("#contract,.tricks").html("");
    $("#bidding_area tr:gt(1)").remove();
    $("#bidding_area tr:gt(0) td").text("");
    $(".bidbox_bid,.bidbox_pass,.bidbox_dbl,.bidbox_rdbl")
	.unbind("click").removeClass("prohibited_bid clickable");
    $(".lead").text("");
    $("#lead_area").before($("#bidding_area").detach());
    var contract = v.contract;
    var decl = v.declearer;
    var points = v.points;
    var tricks = v.tricks;
    var url = v.protocol_url;
    var sign = "";
    if (points > 0) {
	sign = "+";
    }
    var contract_html;
    var r = contract[0];
    var s = contract[1];
    if (s != 'Z') {
	var img = suit_image_template.replace("{suit}", s.toLowerCase()).replace("{alt_suit}", s);
	contract_html = r + img;
    } else {
	contract_html = r + "NT";
    }
    contract_html += contract.substr(2, 2);
    $("#popup_res").html("" +  tricks
			 + " tricks, " + sign + points + "");
    $("#popup_contract").html(contract_html + " by " + decl);
    $(".popup").bPopup();

}

function user_update(v) {
    var pos = v.position;
    var name = v.name;
    $("#" + pos + "_user").text(name);
}<commit_msg>allow pass as first bid<commit_after>var player_positions = ["part", "right", "own", "left"];
var sides = ["N", "E", "S", "W"];
var suit_image_template = "";
var big_suit_image_template = "";
var pass_dbl_rdbl = ["pass", "dbl", "rdbl"];

var lead_count;
var my_side;
var tid;

var update_handlers = new Array();
update_handlers["move"] = process_move;
update_handlers["bid"] = process_bid;
update_handlers["hand"] = process_hand;
update_handlers["user"] = user_update;
update_handlers["start.bidding"] = kick_bidding;
update_handlers["start.play"] = kick_play;
update_handlers["end.play"] = end_play;

function on_body_load() {
    $("body").ajaxError(ajaxErrorHandler);
    parse_params();
    start_updator(update_handlers);
}

function parse_params() {
    params = location.search.split("/");
    tid = params[0].substr(1);
    if (params.length > 1) {
	my_side = $.inArray(params[1], sides);
    }
}

function ajaxErrorHandler(event, xhr, opts, error) {
    // retry should be here for some requests
    window.alert("request failed");
}

function process_hand(v) {
    var player = v.player;
    var cards = v.cards;
    load_hand(player, cards);
}

function card_sort(c1, c2) {
    if (Math.floor(c1/13) + Math.floor(c2/13) == 1) { /* show clubs before diamonds */
	return c1 - c2;
    } else {
	return c2 - c1;
    }
}

function create_card(player, card) {
    var s = document.createElement("span");
    $(s).addClass("card card_" + player + " suit_" + Math.floor(card/13));
    $(s).attr("id", "card_" + card);
    var bg = "url(images/cards.png) " + (card * -71) +  "px 0";
    return $(s).css("background", bg);
}

function load_hand(player, cards) {
    var pos = $.inArray(player, player_positions);
    cards.sort(card_sort);
    var h = $("#" + player + "_hand");
    $.each(cards,
	   function(idx, value) {
	       h.append(create_card(player, value).css({"z-index": idx + 1, "left": idx * 6 + "%"}));
	   });
}

function process_bid(v) {
    var side = v.side;
    var bid = v.bid;
    var dbl_rdbl_allowed = v.dbl_mode;
    var bid_html;
    var idx = $.inArray(bid, pass_dbl_rdbl);
    if (idx == -1) {
	var r = bid[0];
	var s = bid[1];
	if (s != 'Z') {
	    var img = suit_image_template.replace("{suit}", s.toLowerCase()).replace("{alt_suit}", s);
	    bid_html = r + img;
	} else {
	    bid_html = r + "NT";
	}

	disallow_lower_bids(parseInt(r), s);
    } else {
	bid_html = bid;
    }
    $("#bidding_area tr:last td:eq(" + side + ")").html(bid_html);
    if (side == 3) {
	$("#bidding_area").append("

");
    }
    if ((side + 1) % 4 == my_side) {
	if (dbl_rdbl_allowed == "dbl") {
	    allow_bid("#bid_dbl");
	} else if (dbl_rdbl_allowed == "rdbl") {
	    allow_bid("#bid_rdbl");
	}
	allow_bid(".bidbox_pass");
	$(".bidbox_bid:not(.prohibited_bid)").bind("click", do_bid).addClass("clickable");
    }
}

function prohibit_bid(bid_selector) {
    $(bid_selector).unbind("click").removeClass("clickable").addClass("prohibited_bid");
}

function allow_bid(bid_selector) {
    $(bid_selector).bind("click", do_bid).addClass("clickable").removeClass("prohibited_bid");
}

function disallow_lower_bids(r, s) {
    var bid_id = "bid_" + r + s;
    var allowed_bids = $(".bidbox_bid:not(.prohibited_bid)");
    var filtered = allowed_bids.filter(function(index){return this.id <= bid_id});
    filtered.addClass("prohibited_bid");
}

function process_move(v) {
    if (lead_count % 4 == 0) {
	for (var i = 0; i < 4; i += 1) {
	    $("#" + player_positions[i] + "_last span").remove();
	    $("#" + player_positions[i] + "_last")
		.append($("#" + player_positions[i] + "_lead span").detach());
   	}
    }

    var player = v.player;
    var card = v.card;
    var allowed = v.allowed;
    var trick = v.trick;
    var card_div_id = "#card_" + card;
    var lead_div_id = "#" + player + "_lead";

    $(lead_div_id).append(create_card(player, card).css({"z-index": lead_count, "left": 0}));
    lead_count += 1;
    if (trick != null) {
	if (trick == "-") {
	    $("#their_tricks").text(trick_inc);
	} else {
	    $("#our_tricks").text(trick_inc);
	}
    }
    if (allowed == null) {
	return;
    }
    prohibit_cards(".card");
    if (allowed == "any") {
	allow_cards(".card_own", "own");
    } else {
	var np_suit_class = ".card_own.suit_" + allowed;
	allow_cards(np_suit_class, "own");
    }
}

function prohibit_cards(selector) {
    return $(selector).unbind("click dblclick").removeClass("clickable");
}

function allow_cards(selector, player) {
    return $(selector).bind("click", player, highlight_for_move)
	.bind("dblclick", player, do_move).addClass("clickable");
}

function next_player(player) {
    var idx = $.inArray(player, player_positions);
    if (idx == 3) {
	return player_positions[0];
    } else {
	return player_positions[idx + 1];
    }
}

function do_move(event) {
    var player = event.data;
    var splitted_id = event.target.id.split("_");
    var number = splitted_id[1];
    var url = "action.json?move/" + tid + "/" + player + "/" + number;
    $.post(url);
    prohibit_cards(".card_own");
    $(event.target).remove();
}

function img_by_suit(suit) {
    var c = suit.charAt(0);
    return suit_image_template.replace("{suit}", c.toLowerCase()).replace("{alt_suit}", c.toUpperCase());
}

function kick_bidding(v) {
    prohibit_bid(".bidbox_pass,.bidbox_dbl,.bidbox_rdbl");
    if (v.vuln & 1) {
	$(".vuln_NS").addClass("vulnerable");
    }

    if (v.vuln & 2) {
	$(".vuln_EW").addClass("vulnerable");
    }

    side = sides[v.dealer];
    $("#dealer_" + side).addClass("dealer");
    if (my_side == v.dealer) {
    	$(".bidbox_bid").bind("click", do_bid).addClass("clickable");
	allow_bid(".bidbox_pass");
    }
}

function kick_play(v) {
    var contract = v.contract;
    var lead_maker = v.lead;
    lead_count = 0;
    $("#bidbox").css("display", "none").after($("#bidding_area").detach());
    $("#bidbox_cell").css("text-align", "center");

    $("#lead_area").removeClass("hidden");
    if (lead_maker == my_side) {
	allow_cards(".card_own", "own");
	/* indicate my lead somehow */
    }
    var csuit = contract[1];
    var cntrct_html;
    if (csuit != "Z") {
	cntrct_html = contract[0] +
	    big_suit_image_template.replace("{suit}", csuit.toLowerCase())
	    .replace("{alt_suit}", csuit.toUpperCase());;
    } else {
	cntrct_html = contract[0] + "NT";
    }
    $("#contract").html(cntrct_html + contract.substr(2, 2));
}

function highlight_for_move(event) {
    var splitted_id = event.target.id.split("_");
    var number = splitted_id[1];
    allow_cards(".highlighted", event.data).removeClass("highlighted");
    $("#card_" + number).addClass("highlighted").bind("click", event.data, do_move);
}

function do_bid(event) {
    var splitted_id = event.currentTarget.id.split("_");
    var bid = splitted_id[1];
    $(".bidbox_bid:not(.prohibited_bid)").unbind("click").removeClass("clickable");
    prohibit_bid(".bidbox_pass,.bidbox_dbl,.bidbox_rdbl");
    var url = "action.json?bid/" + tid + "/" + my_side + "/" + bid;
    $.post(url);
}

function trick_inc(i, s) {
    if (s.length == 0) {
	return 1;
    } else {
	return parseInt(s) + 1;
    }
}

function end_play(v) {
    $(".vuln_NS,.vuln_EW").removeClass("vulnerable");
    $("#dealer_N,#dealer_E,#dealer_S,#dealer_W").removeClass("dealer");
    $("#bidding_area").removeClass("hidden");
    $("#bidbox").css("display", "inline-block");
    $("#bidbox_cell").css("text-align", "right");
    $("#lead_area").addClass("hidden");
    $("#contract,.tricks").html("");
    $("#bidding_area tr:gt(1)").remove();
    $("#bidding_area tr:gt(0) td").text("");
    $(".bidbox_bid,.bidbox_pass,.bidbox_dbl,.bidbox_rdbl")
	.unbind("click").removeClass("prohibited_bid clickable");
    $(".lead").text("");
    $("#lead_area").before($("#bidding_area").detach());
    var contract = v.contract;
    var decl = v.declearer;
    var points = v.points;
    var tricks = v.tricks;
    var url = v.protocol_url;
    var sign = "";
    if (points > 0) {
	sign = "+";
    }
    var contract_html;
    var r = contract[0];
    var s = contract[1];
    if (s != 'Z') {
	var img = suit_image_template.replace("{suit}", s.toLowerCase()).replace("{alt_suit}", s);
	contract_html = r + img;
    } else {
	contract_html = r + "NT";
    }
    contract_html += contract.substr(2, 2);
    $("#popup_res").html("" +  tricks
			 + " tricks, " + sign + points + "");
    $("#popup_contract").html(contract_html + " by " + decl);
    $(".popup").bPopup();

}

function user_update(v) {
    var pos = v.position;
    var name = v.name;
    $("#" + pos + "_user").text(name);
}<|endoftext|>