import { getWindow } from 'ssr-window';
import $ from '../../shared/dom.js';
import { getTranslate } from '../../shared/utils.js';

export default function Zoom({ swiper, extendParams, on, emit }) {
  const window = getWindow();
  extendParams({
    zoom: {
      enabled: false,
      maxRatio: 3,
      minRatio: 1,
      toggle: true,
      containerClass: 'swiper-zoom-container',
      zoomedSlideClass: 'swiper-slide-zoomed',
    },
  });

  swiper.zoom = {
    enabled: false,
  };

  let currentScale = 1;
  let isScaling = false;
  let gesturesEnabled;
  let fakeGestureTouched;
  let fakeGestureMoved;
  const gesture = {
    $slideEl: undefined,
    slideWidth: undefined,
    slideHeight: undefined,
    $imageEl: undefined,
    $imageWrapEl: undefined,
    maxRatio: 3,
  };
  const image = {
    isTouched: undefined,
    isMoved: undefined,
    currentX: undefined,
    currentY: undefined,
    minX: undefined,
    minY: undefined,
    maxX: undefined,
    maxY: undefined,
    width: undefined,
    height: undefined,
    startX: undefined,
    startY: undefined,
    touchesStart: {},
    touchesCurrent: {},
  };
  const velocity = {
    x: undefined,
    y: undefined,
    prevPositionX: undefined,
    prevPositionY: undefined,
    prevTime: undefined,
  };

  let scale = 1;
  Object.defineProperty(swiper.zoom, 'scale', {
    get() {
      return scale;
    },
    set(value) {
      if (scale !== value) {
        const imageEl = gesture.$imageEl ? gesture.$imageEl[0] : undefined;
        const slideEl = gesture.$slideEl ? gesture.$slideEl[0] : undefined;
        emit('zoomChange', value, imageEl, slideEl);
      }
      scale = value;
    },
  });

  function getDistanceBetweenTouches(e) {
    if (e.targetTouches.length < 2) return 1;
    const x1 = e.targetTouches[0].pageX;
    const y1 = e.targetTouches[0].pageY;
    const x2 = e.targetTouches[1].pageX;
    const y2 = e.targetTouches[1].pageY;
    const distance = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
    return distance;
  }

  // Events
  function onGestureStart(e) {
    const support = swiper.support;
    const params = swiper.params.zoom;
    fakeGestureTouched = false;
    fakeGestureMoved = false;
    if (!support.gestures) {
      if (e.type !== 'touchstart' || (e.type === 'touchstart' && e.targetTouches.length < 2)) {
        return;
      }
      fakeGestureTouched = true;
      gesture.scaleStart = getDistanceBetweenTouches(e);
    }
    if (!gesture.$slideEl || !gesture.$slideEl.length) {
      gesture.$slideEl = $(e.target).closest(`.${swiper.params.slideClass}`);
      if (gesture.$slideEl.length === 0) gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
      gesture.$imageEl = gesture.$slideEl
        .find(`.${params.containerClass}`)
        .eq(0)
        .find('picture, img, svg, canvas, .swiper-zoom-target')
        .eq(0);
      gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
      gesture.maxRatio = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
      if (gesture.$imageWrapEl.length === 0) {
        gesture.$imageEl = undefined;
        return;
      }
    }
    if (gesture.$imageEl) {
      gesture.$imageEl.transition(0);
    }
    isScaling = true;
  }
  function onGestureChange(e) {
    const support = swiper.support;
    const params = swiper.params.zoom;
    const zoom = swiper.zoom;
    if (!support.gestures) {
      if (e.type !== 'touchmove' || (e.type === 'touchmove' && e.targetTouches.length < 2)) {
        return;
      }
      fakeGestureMoved = true;
      gesture.scaleMove = getDistanceBetweenTouches(e);
    }

    if (!gesture.$imageEl || gesture.$imageEl.length === 0) {
      if (e.type === 'gesturechange') onGestureStart(e);
      return;
    }
    if (support.gestures) {
      zoom.scale = e.scale * currentScale;
    } else {
      zoom.scale = (gesture.scaleMove / gesture.scaleStart) * currentScale;
    }
    if (zoom.scale > gesture.maxRatio) {
      zoom.scale = gesture.maxRatio - 1 + (zoom.scale - gesture.maxRatio + 1) ** 0.5;
    }
    if (zoom.scale < params.minRatio) {
      zoom.scale = params.minRatio + 1 - (params.minRatio - zoom.scale + 1) ** 0.5;
    }
    gesture.$imageEl.transform(`translate3d(0,0,0) scale(${zoom.scale})`);
  }
  function onGestureEnd(e) {
    const device = swiper.device;
    const support = swiper.support;
    const params = swiper.params.zoom;
    const zoom = swiper.zoom;
    if (!support.gestures) {
      if (!fakeGestureTouched || !fakeGestureMoved) {
        return;
      }
      if (
        e.type !== 'touchend' ||
        (e.type === 'touchend' && e.changedTouches.length < 2 && !device.android)
      ) {
        return;
      }
      fakeGestureTouched = false;
      fakeGestureMoved = false;
    }
    if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
    zoom.scale = Math.max(Math.min(zoom.scale, gesture.maxRatio), params.minRatio);
    gesture.$imageEl
      .transition(swiper.params.speed)
      .transform(`translate3d(0,0,0) scale(${zoom.scale})`);
    currentScale = zoom.scale;
    isScaling = false;
    if (zoom.scale === 1) gesture.$slideEl = undefined;
  }
  function onTouchStart(e) {
    const device = swiper.device;
    if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
    if (image.isTouched) return;
    if (device.android && e.cancelable) e.preventDefault();
    image.isTouched = true;
    image.touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX;
    image.touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY;
  }
  function onTouchMove(e) {
    const zoom = swiper.zoom;
    if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
    swiper.allowClick = false;
    if (!image.isTouched || !gesture.$slideEl) return;

    if (!image.isMoved) {
      image.width = gesture.$imageEl[0].offsetWidth;
      image.height = gesture.$imageEl[0].offsetHeight;
      image.startX = getTranslate(gesture.$imageWrapEl[0], 'x') || 0;
      image.startY = getTranslate(gesture.$imageWrapEl[0], 'y') || 0;
      gesture.slideWidth = gesture.$slideEl[0].offsetWidth;
      gesture.slideHeight = gesture.$slideEl[0].offsetHeight;
      gesture.$imageWrapEl.transition(0);
    }
    // Define if we need image drag
    const scaledWidth = image.width * zoom.scale;
    const scaledHeight = image.height * zoom.scale;

    if (scaledWidth < gesture.slideWidth && scaledHeight < gesture.slideHeight) return;

    image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0);
    image.maxX = -image.minX;
    image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0);
    image.maxY = -image.minY;

    image.touchesCurrent.x = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX;
    image.touchesCurrent.y = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY;

    if (!image.isMoved && !isScaling) {
      if (
        swiper.isHorizontal() &&
        ((Math.floor(image.minX) === Math.floor(image.startX) &&
          image.touchesCurrent.x < image.touchesStart.x) ||
          (Math.floor(image.maxX) === Math.floor(image.startX) &&
            image.touchesCurrent.x > image.touchesStart.x))
      ) {
        image.isTouched = false;
        return;
      }
      if (
        !swiper.isHorizontal() &&
        ((Math.floor(image.minY) === Math.floor(image.startY) &&
          image.touchesCurrent.y < image.touchesStart.y) ||
          (Math.floor(image.maxY) === Math.floor(image.startY) &&
            image.touchesCurrent.y > image.touchesStart.y))
      ) {
        image.isTouched = false;
        return;
      }
    }
    if (e.cancelable) {
      e.preventDefault();
    }
    e.stopPropagation();

    image.isMoved = true;
    image.currentX = image.touchesCurrent.x - image.touchesStart.x + image.startX;
    image.currentY = image.touchesCurrent.y - image.touchesStart.y + image.startY;

    if (image.currentX < image.minX) {
      image.currentX = image.minX + 1 - (image.minX - image.currentX + 1) ** 0.8;
    }
    if (image.currentX > image.maxX) {
      image.currentX = image.maxX - 1 + (image.currentX - image.maxX + 1) ** 0.8;
    }

    if (image.currentY < image.minY) {
      image.currentY = image.minY + 1 - (image.minY - image.currentY + 1) ** 0.8;
    }
    if (image.currentY > image.maxY) {
      image.currentY = image.maxY - 1 + (image.currentY - image.maxY + 1) ** 0.8;
    }

    // Velocity
    if (!velocity.prevPositionX) velocity.prevPositionX = image.touchesCurrent.x;
    if (!velocity.prevPositionY) velocity.prevPositionY = image.touchesCurrent.y;
    if (!velocity.prevTime) velocity.prevTime = Date.now();
    velocity.x =
      (image.touchesCurrent.x - velocity.prevPositionX) / (Date.now() - velocity.prevTime) / 2;
    velocity.y =
      (image.touchesCurrent.y - velocity.prevPositionY) / (Date.now() - velocity.prevTime) / 2;
    if (Math.abs(image.touchesCurrent.x - velocity.prevPositionX) < 2) velocity.x = 0;
    if (Math.abs(image.touchesCurrent.y - velocity.prevPositionY) < 2) velocity.y = 0;
    velocity.prevPositionX = image.touchesCurrent.x;
    velocity.prevPositionY = image.touchesCurrent.y;
    velocity.prevTime = Date.now();

    gesture.$imageWrapEl.transform(`translate3d(${image.currentX}px, ${image.currentY}px,0)`);
  }
  function onTouchEnd() {
    const zoom = swiper.zoom;
    if (!gesture.$imageEl || gesture.$imageEl.length === 0) return;
    if (!image.isTouched || !image.isMoved) {
      image.isTouched = false;
      image.isMoved = false;
      return;
    }
    image.isTouched = false;
    image.isMoved = false;
    let momentumDurationX = 300;
    let momentumDurationY = 300;
    const momentumDistanceX = velocity.x * momentumDurationX;
    const newPositionX = image.currentX + momentumDistanceX;
    const momentumDistanceY = velocity.y * momentumDurationY;
    const newPositionY = image.currentY + momentumDistanceY;

    // Fix duration
    if (velocity.x !== 0)
      momentumDurationX = Math.abs((newPositionX - image.currentX) / velocity.x);
    if (velocity.y !== 0)
      momentumDurationY = Math.abs((newPositionY - image.currentY) / velocity.y);
    const momentumDuration = Math.max(momentumDurationX, momentumDurationY);

    image.currentX = newPositionX;
    image.currentY = newPositionY;

    // Define if we need image drag
    const scaledWidth = image.width * zoom.scale;
    const scaledHeight = image.height * zoom.scale;
    image.minX = Math.min(gesture.slideWidth / 2 - scaledWidth / 2, 0);
    image.maxX = -image.minX;
    image.minY = Math.min(gesture.slideHeight / 2 - scaledHeight / 2, 0);
    image.maxY = -image.minY;
    image.currentX = Math.max(Math.min(image.currentX, image.maxX), image.minX);
    image.currentY = Math.max(Math.min(image.currentY, image.maxY), image.minY);

    gesture.$imageWrapEl
      .transition(momentumDuration)
      .transform(`translate3d(${image.currentX}px, ${image.currentY}px,0)`);
  }
  function onTransitionEnd() {
    const zoom = swiper.zoom;
    if (gesture.$slideEl && swiper.previousIndex !== swiper.activeIndex) {
      if (gesture.$imageEl) {
        gesture.$imageEl.transform('translate3d(0,0,0) scale(1)');
      }
      if (gesture.$imageWrapEl) {
        gesture.$imageWrapEl.transform('translate3d(0,0,0)');
      }

      zoom.scale = 1;
      currentScale = 1;

      gesture.$slideEl = undefined;
      gesture.$imageEl = undefined;
      gesture.$imageWrapEl = undefined;
    }
  }

  function zoomIn(e) {
    const zoom = swiper.zoom;
    const params = swiper.params.zoom;

    if (!gesture.$slideEl) {
      if (e && e.target) {
        gesture.$slideEl = $(e.target).closest(`.${swiper.params.slideClass}`);
      }
      if (!gesture.$slideEl) {
        if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) {
          gesture.$slideEl = swiper.$wrapperEl.children(`.${swiper.params.slideActiveClass}`);
        } else {
          gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
        }
      }

      gesture.$imageEl = gesture.$slideEl
        .find(`.${params.containerClass}`)
        .eq(0)
        .find('picture, img, svg, canvas, .swiper-zoom-target')
        .eq(0);
      gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
    }
    if (
      !gesture.$imageEl ||
      gesture.$imageEl.length === 0 ||
      !gesture.$imageWrapEl ||
      gesture.$imageWrapEl.length === 0
    )
      return;
    if (swiper.params.cssMode) {
      swiper.wrapperEl.style.overflow = 'hidden';
      swiper.wrapperEl.style.touchAction = 'none';
    }

    gesture.$slideEl.addClass(`${params.zoomedSlideClass}`);

    let touchX;
    let touchY;
    let offsetX;
    let offsetY;
    let diffX;
    let diffY;
    let translateX;
    let translateY;
    let imageWidth;
    let imageHeight;
    let scaledWidth;
    let scaledHeight;
    let translateMinX;
    let translateMinY;
    let translateMaxX;
    let translateMaxY;
    let slideWidth;
    let slideHeight;

    if (typeof image.touchesStart.x === 'undefined' && e) {
      touchX = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
      touchY = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
    } else {
      touchX = image.touchesStart.x;
      touchY = image.touchesStart.y;
    }

    zoom.scale = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
    currentScale = gesture.$imageWrapEl.attr('data-swiper-zoom') || params.maxRatio;
    if (e) {
      slideWidth = gesture.$slideEl[0].offsetWidth;
      slideHeight = gesture.$slideEl[0].offsetHeight;
      offsetX = gesture.$slideEl.offset().left + window.scrollX;
      offsetY = gesture.$slideEl.offset().top + window.scrollY;
      diffX = offsetX + slideWidth / 2 - touchX;
      diffY = offsetY + slideHeight / 2 - touchY;

      imageWidth = gesture.$imageEl[0].offsetWidth;
      imageHeight = gesture.$imageEl[0].offsetHeight;
      scaledWidth = imageWidth * zoom.scale;
      scaledHeight = imageHeight * zoom.scale;

      translateMinX = Math.min(slideWidth / 2 - scaledWidth / 2, 0);
      translateMinY = Math.min(slideHeight / 2 - scaledHeight / 2, 0);
      translateMaxX = -translateMinX;
      translateMaxY = -translateMinY;

      translateX = diffX * zoom.scale;
      translateY = diffY * zoom.scale;

      if (translateX < translateMinX) {
        translateX = translateMinX;
      }
      if (translateX > translateMaxX) {
        translateX = translateMaxX;
      }

      if (translateY < translateMinY) {
        translateY = translateMinY;
      }
      if (translateY > translateMaxY) {
        translateY = translateMaxY;
      }
    } else {
      translateX = 0;
      translateY = 0;
    }
    gesture.$imageWrapEl
      .transition(300)
      .transform(`translate3d(${translateX}px, ${translateY}px,0)`);
    gesture.$imageEl.transition(300).transform(`translate3d(0,0,0) scale(${zoom.scale})`);
  }
  function zoomOut() {
    const zoom = swiper.zoom;
    const params = swiper.params.zoom;

    if (!gesture.$slideEl) {
      if (swiper.params.virtual && swiper.params.virtual.enabled && swiper.virtual) {
        gesture.$slideEl = swiper.$wrapperEl.children(`.${swiper.params.slideActiveClass}`);
      } else {
        gesture.$slideEl = swiper.slides.eq(swiper.activeIndex);
      }
      gesture.$imageEl = gesture.$slideEl
        .find(`.${params.containerClass}`)
        .eq(0)
        .find('picture, img, svg, canvas, .swiper-zoom-target')
        .eq(0);
      gesture.$imageWrapEl = gesture.$imageEl.parent(`.${params.containerClass}`);
    }
    if (
      !gesture.$imageEl ||
      gesture.$imageEl.length === 0 ||
      !gesture.$imageWrapEl ||
      gesture.$imageWrapEl.length === 0
    )
      return;
    if (swiper.params.cssMode) {
      swiper.wrapperEl.style.overflow = '';
      swiper.wrapperEl.style.touchAction = '';
    }
    zoom.scale = 1;
    currentScale = 1;
    gesture.$imageWrapEl.transition(300).transform('translate3d(0,0,0)');
    gesture.$imageEl.transition(300).transform('translate3d(0,0,0) scale(1)');
    gesture.$slideEl.removeClass(`${params.zoomedSlideClass}`);
    gesture.$slideEl = undefined;
  }

  // Toggle Zoom
  function zoomToggle(e) {
    const zoom = swiper.zoom;

    if (zoom.scale && zoom.scale !== 1) {
      // Zoom Out
      zoomOut();
    } else {
      // Zoom In
      zoomIn(e);
    }
  }

  function getListeners() {
    const support = swiper.support;
    const passiveListener =
      swiper.touchEvents.start === 'touchstart' &&
      support.passiveListener &&
      swiper.params.passiveListeners
        ? { passive: true, capture: false }
        : false;
    const activeListenerWithCapture = support.passiveListener
      ? { passive: false, capture: true }
      : true;
    return { passiveListener, activeListenerWithCapture };
  }

  function getSlideSelector() {
    return `.${swiper.params.slideClass}`;
  }

  function toggleGestures(method) {
    const { passiveListener } = getListeners();
    const slideSelector = getSlideSelector();
    swiper.$wrapperEl[method]('gesturestart', slideSelector, onGestureStart, passiveListener);
    swiper.$wrapperEl[method]('gesturechange', slideSelector, onGestureChange, passiveListener);
    swiper.$wrapperEl[method]('gestureend', slideSelector, onGestureEnd, passiveListener);
  }
  function enableGestures() {
    if (gesturesEnabled) return;
    gesturesEnabled = true;
    toggleGestures('on');
  }
  function disableGestures() {
    if (!gesturesEnabled) return;
    gesturesEnabled = false;
    toggleGestures('off');
  }

  // Attach/Detach Events
  function enable() {
    const zoom = swiper.zoom;
    if (zoom.enabled) return;
    zoom.enabled = true;
    const support = swiper.support;
    const { passiveListener, activeListenerWithCapture } = getListeners();
    const slideSelector = getSlideSelector();

    // Scale image
    if (support.gestures) {
      swiper.$wrapperEl.on(swiper.touchEvents.start, enableGestures, passiveListener);
      swiper.$wrapperEl.on(swiper.touchEvents.end, disableGestures, passiveListener);
    } else if (swiper.touchEvents.start === 'touchstart') {
      swiper.$wrapperEl.on(
        swiper.touchEvents.start,
        slideSelector,
        onGestureStart,
        passiveListener,
      );
      swiper.$wrapperEl.on(
        swiper.touchEvents.move,
        slideSelector,
        onGestureChange,
        activeListenerWithCapture,
      );
      swiper.$wrapperEl.on(swiper.touchEvents.end, slideSelector, onGestureEnd, passiveListener);
      if (swiper.touchEvents.cancel) {
        swiper.$wrapperEl.on(
          swiper.touchEvents.cancel,
          slideSelector,
          onGestureEnd,
          passiveListener,
        );
      }
    }

    // Move image
    swiper.$wrapperEl.on(
      swiper.touchEvents.move,
      `.${swiper.params.zoom.containerClass}`,
      onTouchMove,
      activeListenerWithCapture,
    );
  }
  function disable() {
    const zoom = swiper.zoom;
    if (!zoom.enabled) return;
    const support = swiper.support;
    zoom.enabled = false;

    const { passiveListener, activeListenerWithCapture } = getListeners();
    const slideSelector = getSlideSelector();

    // Scale image
    if (support.gestures) {
      swiper.$wrapperEl.off(swiper.touchEvents.start, enableGestures, passiveListener);
      swiper.$wrapperEl.off(swiper.touchEvents.end, disableGestures, passiveListener);
    } else if (swiper.touchEvents.start === 'touchstart') {
      swiper.$wrapperEl.off(
        swiper.touchEvents.start,
        slideSelector,
        onGestureStart,
        passiveListener,
      );
      swiper.$wrapperEl.off(
        swiper.touchEvents.move,
        slideSelector,
        onGestureChange,
        activeListenerWithCapture,
      );
      swiper.$wrapperEl.off(swiper.touchEvents.end, slideSelector, onGestureEnd, passiveListener);
      if (swiper.touchEvents.cancel) {
        swiper.$wrapperEl.off(
          swiper.touchEvents.cancel,
          slideSelector,
          onGestureEnd,
          passiveListener,
        );
      }
    }

    // Move image
    swiper.$wrapperEl.off(
      swiper.touchEvents.move,
      `.${swiper.params.zoom.containerClass}`,
      onTouchMove,
      activeListenerWithCapture,
    );
  }

  on('init', () => {
    if (swiper.params.zoom.enabled) {
      enable();
    }
  });
  on('destroy', () => {
    disable();
  });
  on('touchStart', (_s, e) => {
    if (!swiper.zoom.enabled) return;
    onTouchStart(e);
  });
  on('touchEnd', (_s, e) => {
    if (!swiper.zoom.enabled) return;
    onTouchEnd(e);
  });
  on('doubleTap', (_s, e) => {
    if (
      !swiper.animating &&
      swiper.params.zoom.enabled &&
      swiper.zoom.enabled &&
      swiper.params.zoom.toggle
    ) {
      zoomToggle(e);
    }
  });
  on('transitionEnd', () => {
    if (swiper.zoom.enabled && swiper.params.zoom.enabled) {
      onTransitionEnd();
    }
  });
  on('slideChange', () => {
    if (swiper.zoom.enabled && swiper.params.zoom.enabled && swiper.params.cssMode) {
      onTransitionEnd();
    }
  });

  Object.assign(swiper.zoom, {
    enable,
    disable,
    in: zoomIn,
    out: zoomOut,
    toggle: zoomToggle,
  });
}


*
* @default 'swiper-button-lock'
*/
lockClass?: string;
}

* CSS class name added on swiper container when navigation is disabled by breakpoint

import { paramsList } from './params-list.js';
import { isObject } from './utils.js';

function getChangedParams(swiperParams, oldParams, children, oldChildren) {
  const keys = [];
  if (!oldParams) return keys;
  const addKey = (key) => {
    if (keys.indexOf(key) < 0) keys.push(key);
  };
  const oldChildrenKeys = oldChildren.map((child) => child.key);
  const childrenKeys = children.map((child) => child.key);
  if (oldChildrenKeys.join('') !== childrenKeys.join('')) addKey('children');
  if (oldChildren.length !== children.length) addKey('children');
  const watchParams = paramsList.filter((key) => key[0] === '_').map((key) => key.replace(/_/, ''));
  watchParams.forEach((key) => {
    if (key in swiperParams && key in oldParams) {
      if (isObject(swiperParams[key]) && isObject(oldParams[key])) {
        const newKeys = Object.keys(swiperParams[key]);
        const oldKeys = Object.keys(oldParams[key]);
        if (newKeys.length !== oldKeys.length) {
          addKey(key);
        } else {
          newKeys.forEach((newKey) => {
            if (swiperParams[key][newKey] !== oldParams[key][newKey]) {
              addKey(key);
            }
          });
          oldKeys.forEach((oldKey) => {
            if (swiperParams[key][oldKey] !== oldParams[key][oldKey]) addKey(key);
          });
        }
      } else if (swiperParams[key] !== oldParams[key]) {
        addKey(key);
      }
    }
  });
  return keys;
}

export { getChangedParams };


<!DOCTYPE html>

  <meta charset="utf-8">
  <title>Swiper demo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">


// Status bar indicator - icon & tooltip updated by run()

var statusIconHtml = Mustache.render("

", Strings);

// Clicking indicator toggles error panel, if any errors in current file

keep keep keep keep keep replace replace keep keep keep

StatusBar.addIndicator(INDICATOR_ID, $(statusIconHtml), true, "", "", "status-language");

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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, brackets, window, document, Mustache */

/**
 * A status bar with support for file information and busy and status indicators. This is a semi-generic
 * container; for the code that decides what content appears in the status bar, see client modules like
 * EditorStatusBar. (Although in practice StatusBar's HTML structure and initialization
 * assume it's only used for this one purpose, and all the APIs are on a singleton).
 */
define(function (require, exports, module) {
    'use strict';

    var AppInit         = require("utils/AppInit"),
        StatusBarHTML   = require("text!widgets/StatusBar.html"),
        EditorManager   = require("editor/EditorManager"),
        Strings         = require("strings");

    var _init = false;

    // Indicates if the busy cursor is active to avoid unnecesary operations
    var _busyCursor = false;

    // A simple regexp to sanitize indicator ids
    var _indicatorIDRegexp = new RegExp("[^a-zA-Z 0-9]+", "g");

    // These vars are initialized by the AppInit.htmlReady handler
    // below since they refer to DOM elements
    var $editorContainer,
        $statusBar,
        $indicators,
        $busyIndicator;

    /**
     * Shows the 'busy' indicator
     * @param {boolean} updateCursor Sets the cursor to "wait"
     */
    function showBusyIndicator(updateCursor) {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        if (updateCursor) {
            _busyCursor = true;
            $("*").addClass("busyCursor");
        }

        $busyIndicator.addClass("spin");
    }

    /**
     * Hides the 'busy' indicator
     */
    function hideBusyIndicator() {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        // Check if we are using the busyCursor class to avoid
        // unnecesary calls to $('*').removeClass()
        if (_busyCursor) {
            _busyCursor = false;
            $("*").removeClass("busyCursor");
        }

        $busyIndicator.removeClass("spin");
    }

    /**
     * Registers a new status indicator
     * @param {string} id Registration id of the indicator to be updated.
     * @param {(DOMNode|jQueryObject)=} indicator Optional DOMNode for the indicator
     * @param {boolean=} visible Shows or hides the indicator over the statusbar.
     * @param {string=} style Sets the attribute "class" of the indicator.
     * @param {string=} tooltip Sets the attribute "title" of the indicator.
     */
    function addIndicator(id, indicator, visible, style, tooltip) {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        indicator = indicator || document.createElement("div");
        tooltip = tooltip || "";
        style = style || "";
        id = id.replace(_indicatorIDRegexp, "-") || "";

        var $indicator = $(indicator);

        $indicator.attr("id", id);
        $indicator.attr("title", tooltip);
        $indicator.addClass("indicator");
        $indicator.addClass(style);

        if (!visible) {
            $indicator.hide();
        }

    }

    /**
     * Updates a status indicator
     * @param {string} id Registration id of the indicator to be updated.
     * @param {boolean} visible Shows or hides the indicator over the statusbar.
     * @param {string=} style Sets the attribute "class" of the indicator.
     * @param {string=} tooltip Sets the attribute "title" of the indicator.
     */
    function updateIndicator(id, visible, style, tooltip) {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        var $indicator = $("#" + id.replace(_indicatorIDRegexp, "-"));

        if ($indicator) {

            if (visible) {
                $indicator.show();
            } else {
                $indicator.hide();
            }

            if (style) {
                $indicator.removeClass();
                $indicator.addClass(style);
            } else {
                $indicator.removeClass();
                $indicator.addClass("indicator");
            }

            if (tooltip) {
                $indicator.attr("title", tooltip);
            }
        }
    }

    /**
     * Hide the statusbar
     */
    function hide() {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        if ($statusBar.is(":visible")) {
            $statusBar.hide();
            EditorManager.resizeEditor();  // changes available ht for editor area
        }
    }

    /**
     * Show the statusbar
     */
    function show() {
        if (!_init) {
            console.error("StatusBar API invoked before status bar created");
            return;
        }

        if (!$statusBar.is(":visible")) {
            $statusBar.show();
            EditorManager.resizeEditor();  // changes available ht for editor area
        }
    }

    AppInit.htmlReady(function () {
        var $parent = $(".main-view .content");
        $parent.append(Mustache.render(StatusBarHTML, Strings));

        // Initialize items dependent on HTML DOM
        $statusBar          = $("#status-bar");
        $indicators         = $("#status-indicators");
        $busyIndicator      = $("#status-bar .spinner");

        _init = true;

        // hide on init
        hide();
    });

    exports.showBusyIndicator = showBusyIndicator;
    exports.hideBusyIndicator = hideBusyIndicator;
    exports.addIndicator = addIndicator;
    exports.updateIndicator = updateIndicator;
    exports.hide = hide;
    exports.show = show;
});


#!/usr/bin/env node
var Traverse = require('traverse');

var obj = [ 'five', 6, -3, [ 7, 8, -2, 1 ], { f : 10, g : -13 } ];

var s = '';
Traverse(obj).forEach(function to_s (node) {
    if (Array.isArray(node)) {
        this.before(function () { s += '[' });
        this.post(function (child) {
            if (!child.isLast) s += ',';
        });
        this.after(function () { s += ']' });
    }
    else if (typeof node == 'object') {
        this.before(function () { s += '{' });
        this.pre(function (x, key) {
            to_s(key);
            s += ':';
        });
        this.post(function (child) {
            if (!child.isLast) s += ',';
        });
        this.after(function () { s += '}' });
    }
    else if (typeof node == 'string') {
        s += '"' + node.toString().replace(/"/g, '\\"') + '"';
    }
    else if (typeof node == 'function') {
        s += 'null';
    }
    else {
        s += node.toString();
    }
});

console.log('JSON.stringify: ' + JSON.stringify(obj));
console.log('this stringify: ' + s);


Just like node's `fs` module, but it does an incremental back-off when
EMFILE is encountered.

Useful in asynchronous situations where one needs to try to open lots
and lots of files.


* @param {string} id Registration id of the indicator to be updated.

* @param {(DOMNode|jQueryObject)=} indicator Optional DOMNode for the indicator

* @param {boolean=} visible Shows or hides the indicator over the statusbar.

* @param {string=} style Sets the attribute "class" of the indicator.

* @param {string=} tooltip Sets the attribute "title" of the indicator.

* @param {string=} _beforeID For internal use by CodeInspection module.


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

var common = require('../common.js');
var assert = require('assert');
var R = require('../../lib/_stream_readable');
var util = require('util');

// tiny node-tap lookalike.
var tests = [];
var count = 0;

function test(name, fn) {
  count++;
  tests.push([name, fn]);
}

function run() {
  var next = tests.shift();
  if (!next)
    return console.error('ok');

  var name = next[0];
  var fn = next[1];
  console.log('# %s', name);
  fn({
    same: assert.deepEqual,
    equal: assert.equal,
    end: function () {
      count--;
      run();
    }
  });
}

// ensure all tests have run
process.on("exit", function () {
  assert.equal(count, 0);
});

process.nextTick(run);

/////

util.inherits(TestReader, R);

function TestReader(n, opts) {
  R.call(this, opts);

  this.pos = 0;
  this.len = n || 100;
}

TestReader.prototype._read = function(n) {
  setTimeout(function() {

    if (this.pos >= this.len) {
      return this.push(null);
    }

    n = Math.min(n, this.len - this.pos);
    if (n <= 0) {
      return this.push(null);
    }

    this.pos += n;
    var ret = new Buffer(n);
    ret.fill('a');

    console.log("this.push(ret)", ret)

    return this.push(ret);
  }.bind(this), 1);
};

test('setEncoding utf8', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('utf8');
  var out = [];
  var expect =
    [ 'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('setEncoding hex', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('hex');
  var out = [];
  var expect =
    [ '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('setEncoding hex with read(13)', function(t) {
  var tr = new TestReader(100);
  tr.setEncoding('hex');
  var out = [];
  var expect =
    [ "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "16161" ];

  tr.on('readable', function flow() {
    console.log("readable once")
    var chunk;
    while (null !== (chunk = tr.read(13)))
      out.push(chunk);
  });

  tr.on('end', function() {
    console.log("END")
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: utf8', function(t) {
  var tr = new TestReader(100, { encoding: 'utf8' });
  var out = [];
  var expect =
    [ 'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa',
      'aaaaaaaaaa' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: hex', function(t) {
  var tr = new TestReader(100, { encoding: 'hex' });
  var out = [];
  var expect =
    [ '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161',
      '6161616161' ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(10)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});

test('encoding: hex with read(13)', function(t) {
  var tr = new TestReader(100, { encoding: 'hex' });
  var out = [];
  var expect =
    [ "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "1616161616161",
      "6161616161616",
      "16161" ];

  tr.on('readable', function flow() {
    var chunk;
    while (null !== (chunk = tr.read(13)))
      out.push(chunk);
  });

  tr.on('end', function() {
    t.same(out, expect);
    t.end();
  });

  // just kick it off.
  tr.emit('readable');
});


<!DOCTYPE html>

	<meta charset="utf-8">
	<title>jQuery UI Autocomplete - Remote with caching</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

	<link rel="stylesheet" href="../demos.css">

	Birds:
	<input id="birds">

The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are bird names, displayed when at least two characters are entered into the field.

Similar to the remote datasource demo, though this adds some local caching to improve performance. The cache here saves just one query, and could be extended to cache multiple values, one for each term.


POST /upload HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=----TLV0SrKD4z1TRxRhAPUvZ
Content-Length: 221

------TLV0SrKD4z1TRxRhAPUvZ
Content-Disposition: form-data; name="file"; filename="plain.txt"
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

I am a plain text file

------TLV0SrKD4z1TRxRhAPUvZ--


function addIndicator(id, indicator, visible, style, tooltip) {

console.error("StatusBar API invoked before status bar created");

function addIndicator(id, indicator, visible, style, tooltip, _beforeID) {


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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */

define({
    // Relative to the samples folder
    "GETTING_STARTED"   : "pt-br/Primeiros Passos"
});


<!DOCTYPE html>

	<meta charset="utf-8">
	<title>jQuery UI Datepicker - Select a Date Range</title>
	<link rel="stylesheet" href="../../themes/base/jquery.ui.all.css">

	<link rel="stylesheet" href="../demos.css">

From
<input type="text" id="from" name="from"/>
to
<input type="text" id="to" name="to"/>

Select the date range to search for.


indicator = indicator || document.createElement("div");


// this keeps a queue of opened file descriptors, and will make
// fs operations wait until some have closed before trying to open more.

var fs = exports = module.exports = {}
fs._originalFs = require("fs")

Object.getOwnPropertyNames(fs._originalFs).forEach(function(prop) {
  var desc = Object.getOwnPropertyDescriptor(fs._originalFs, prop)
  Object.defineProperty(fs, prop, desc)
})

var queue = []
  , constants = require("constants")

fs._curOpen = 0

fs.MIN_MAX_OPEN = 64
fs.MAX_OPEN = 1024

// prevent EMFILE errors
function OpenReq (path, flags, mode, cb) {
  this.path = path
  this.flags = flags
  this.mode = mode
  this.cb = cb
}

function noop () {}

fs.open = gracefulOpen

function gracefulOpen (path, flags, mode, cb) {
  if (typeof mode === "function") cb = mode, mode = null
  if (typeof cb !== "function") cb = noop

  if (fs._curOpen >= fs.MAX_OPEN) {
    queue.push(new OpenReq(path, flags, mode, cb))
    setTimeout(flush)
    return
  }
  open(path, flags, mode, function (er, fd) {
    if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
      // that was too many.  reduce max, get back in queue.
      // this should only happen once in a great while, and only
      // if the ulimit -n is set lower than 1024.
      fs.MAX_OPEN = fs._curOpen - 1
      return fs.open(path, flags, mode, cb)
    }
    cb(er, fd)
  })
}

function open (path, flags, mode, cb) {
  cb = cb || noop
  fs._curOpen ++
  fs._originalFs.open.call(fs, path, flags, mode, function (er, fd) {
    if (er) onclose()
    cb(er, fd)
  })
}

fs.openSync = function (path, flags, mode) {
  var ret
  ret = fs._originalFs.openSync.call(fs, path, flags, mode)
  fs._curOpen ++
  return ret
}

function onclose () {
  fs._curOpen --
  flush()
}

function flush () {
  while (fs._curOpen < fs.MAX_OPEN) {
    var req = queue.shift()
    if (!req) return
    switch (req.constructor.name) {
      case 'OpenReq':
        open(req.path, req.flags || "r", req.mode || 0777, req.cb)
        break
      case 'ReaddirReq':
        readdir(req.path, req.cb)
        break
      case 'ReadFileReq':
        readFile(req.path, req.options, req.cb)
        break
      case 'WriteFileReq':
        writeFile(req.path, req.data, req.options, req.cb)
        break
      default:
        throw new Error('Unknown req type: ' + req.constructor.name)
    }
  }
}

fs.close = function (fd, cb) {
  cb = cb || noop
  fs._originalFs.close.call(fs, fd, function (er) {
    onclose()
    cb(er)
  })
}

fs.closeSync = function (fd) {
  try {
    return fs._originalFs.closeSync.call(fs, fd)
  } finally {
    onclose()
  }
}

// readdir takes a fd as well.
// however, the sync version closes it right away, so
// there's no need to wrap.
// It would be nice to catch when it throws an EMFILE,
// but that's relatively rare anyway.

fs.readdir = gracefulReaddir

function gracefulReaddir (path, cb) {
  if (fs._curOpen >= fs.MAX_OPEN) {
    queue.push(new ReaddirReq(path, cb))
    setTimeout(flush)
    return
  }

  readdir(path, function (er, files) {
    if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
      fs.MAX_OPEN = fs._curOpen - 1
      return fs.readdir(path, cb)
    }
    cb(er, files)
  })
}

function readdir (path, cb) {
  cb = cb || noop
  fs._curOpen ++
  fs._originalFs.readdir.call(fs, path, function (er, files) {
    onclose()
    cb(er, files)
  })
}

function ReaddirReq (path, cb) {
  this.path = path
  this.cb = cb
}

fs.readFile = gracefulReadFile

function gracefulReadFile(path, options, cb) {
  if (typeof options === "function") cb = options, options = null
  if (typeof cb !== "function") cb = noop

  if (fs._curOpen >= fs.MAX_OPEN) {
    queue.push(new ReadFileReq(path, options, cb))
    setTimeout(flush)
    return
  }

  readFile(path, options, function (er, data) {
    if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
      fs.MAX_OPEN = fs._curOpen - 1
      return fs.readFile(path, options, cb)
    }
    cb(er, data)
  })
}

function readFile (path, options, cb) {
  cb = cb || noop
  fs._curOpen ++
  fs._originalFs.readFile.call(fs, path, options, function (er, data) {
    onclose()
    cb(er, data)
  })
}

function ReadFileReq (path, options, cb) {
  this.path = path
  this.options = options
  this.cb = cb
}

fs.writeFile = gracefulWriteFile

function gracefulWriteFile(path, data, options, cb) {
  if (typeof options === "function") cb = options, options = null
  if (typeof cb !== "function") cb = noop

  if (fs._curOpen >= fs.MAX_OPEN) {
    queue.push(new WriteFileReq(path, data, options, cb))
    setTimeout(flush)
    return
  }

  writeFile(path, data, options, function (er) {
    if (er && er.code === "EMFILE" && fs._curOpen > fs.MIN_MAX_OPEN) {
      fs.MAX_OPEN = fs._curOpen - 1
      return fs.writeFile(path, data, options, cb)
    }
    cb(er)
  })
}

function writeFile (path, data, options, cb) {
  cb = cb || noop
  fs._curOpen ++
  fs._originalFs.writeFile.call(fs, path, data, options, function (er) {
    onclose()
    cb(er)
  })
}

function WriteFileReq (path, data, options, cb) {
  this.path = path
  this.data = data
  this.options = options
  this.cb = cb
}

// (re-)implement some things that are known busted or missing.

var constants = require("constants")

// lchmod, broken prior to 0.6.2
// back-port the fix here.
if (constants.hasOwnProperty('O_SYMLINK') &&
    process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
  fs.lchmod = function (path, mode, callback) {
    callback = callback || noop
    fs.open( path
           , constants.O_WRONLY | constants.O_SYMLINK
           , mode
           , function (err, fd) {
      if (err) {
        callback(err)
        return
      }
      // prefer to return the chmod error, if one occurs,
      // but still try to close, and report closing errors if they occur.
      fs.fchmod(fd, mode, function (err) {
        fs.close(fd, function(err2) {
          callback(err || err2)
        })
      })
    })
  }

  fs.lchmodSync = function (path, mode) {
    var fd = fs.openSync(path, constants.O_WRONLY | constants.O_SYMLINK, mode)

    // prefer to return the chmod error, if one occurs,
    // but still try to close, and report closing errors if they occur.
    var err, err2
    try {
      var ret = fs.fchmodSync(fd, mode)
    } catch (er) {
      err = er
    }
    try {
      fs.closeSync(fd)
    } catch (er) {
      err2 = er
    }
    if (err || err2) throw (err || err2)
    return ret
  }
}

// lutimes implementation, or no-op
if (!fs.lutimes) {
  if (constants.hasOwnProperty("O_SYMLINK")) {
    fs.lutimes = function (path, at, mt, cb) {
      fs.open(path, constants.O_SYMLINK, function (er, fd) {
        cb = cb || noop
        if (er) return cb(er)
        fs.futimes(fd, at, mt, function (er) {
          fs.close(fd, function (er2) {
            return cb(er || er2)
          })
        })
      })
    }

    fs.lutimesSync = function (path, at, mt) {
      var fd = fs.openSync(path, constants.O_SYMLINK)
        , err
        , err2
        , ret

      try {
        var ret = fs.futimesSync(fd, at, mt)
      } catch (er) {
        err = er
      }
      try {
        fs.closeSync(fd)
      } catch (er) {
        err2 = er
      }
      if (err || err2) throw (err || err2)
      return ret
    }

  } else if (fs.utimensat && constants.hasOwnProperty("AT_SYMLINK_NOFOLLOW")) {
    // maybe utimensat will be bound soonish?
    fs.lutimes = function (path, at, mt, cb) {
      fs.utimensat(path, at, mt, constants.AT_SYMLINK_NOFOLLOW, cb)
    }

    fs.lutimesSync = function (path, at, mt) {
      return fs.utimensatSync(path, at, mt, constants.AT_SYMLINK_NOFOLLOW)
    }

  } else {
    fs.lutimes = function (_a, _b, _c, cb) { process.nextTick(cb) }
    fs.lutimesSync = function () {}
  }
}

// https://github.com/isaacs/node-graceful-fs/issues/4
// Chown should not fail on einval or eperm if non-root.

fs.chown = chownFix(fs.chown)
fs.fchown = chownFix(fs.fchown)
fs.lchown = chownFix(fs.lchown)

fs.chownSync = chownFixSync(fs.chownSync)
fs.fchownSync = chownFixSync(fs.fchownSync)
fs.lchownSync = chownFixSync(fs.lchownSync)

function chownFix (orig) {
  if (!orig) return orig
  return function (target, uid, gid, cb) {
    return orig.call(fs, target, uid, gid, function (er, res) {
      if (chownErOk(er)) er = null
      cb(er, res)
    })
  }
}

function chownFixSync (orig) {
  if (!orig) return orig
  return function (target, uid, gid) {
    try {
      return orig.call(fs, target, uid, gid)
    } catch (er) {
      if (!chownErOk(er)) throw er
    }
  }
}

function chownErOk (er) {
  // if there's no getuid, or if getuid() is something other than 0,
  // and the error is EINVAL or EPERM, then just ignore it.
  // This specific case is a silent failure in cp, install, tar,
  // and most other unix tools that manage permissions.
  // When running as root, or if other types of errors are encountered,
  // then it's strict.
  if (!er || (!process.getuid || process.getuid() !== 0)
      && (er.code === "EINVAL" || er.code === "EPERM")) return true
}

// if lchmod/lchown do not exist, then make them no-ops
if (!fs.lchmod) {
  fs.lchmod = function (path, mode, cb) {
    process.nextTick(cb)
  }
  fs.lchmodSync = function () {}
}
if (!fs.lchown) {
  fs.lchown = function (path, uid, gid, cb) {
    process.nextTick(cb)
  }
  fs.lchownSync = function () {}
}

// on Windows, A/V software can lock the directory, causing this
// to fail with an EACCES or EPERM if the directory contains newly
// created files.  Try again on failure, for up to 1 second.
if (process.platform === "win32") {
  var rename_ = fs.rename
  fs.rename = function rename (from, to, cb) {
    var start = Date.now()
    rename_(from, to, function CB (er) {
      if (er
          && (er.code === "EACCES" || er.code === "EPERM")
          && Date.now() - start < 1000) {
        return rename_(from, to, CB)
      }
      cb(er)
    })
  }
}

// if read() returns EAGAIN, then just try it again.
var read = fs.read
fs.read = function (fd, buffer, offset, length, position, callback_) {
  var callback
  if (callback_ && typeof callback_ === 'function') {
    var eagCounter = 0
    callback = function (er, _, __) {
      if (er && er.code === 'EAGAIN' && eagCounter < 10) {
        eagCounter ++
        return read.call(fs, fd, buffer, offset, length, position, callback)
      }
      callback_.apply(this, arguments)
    }
  }
  return read.call(fs, fd, buffer, offset, length, position, callback)
}

var readSync = fs.readSync
fs.readSync = function (fd, buffer, offset, length, position) {
  var eagCounter = 0
  while (true) {
    try {
      return readSync.call(fs, fd, buffer, offset, length, position)
    } catch (er) {
      if (er.code === 'EAGAIN' && eagCounter < 10) {
        eagCounter ++
        continue
      }
      throw er
    }
  }
}


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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, window */

/**
 * Defines a ChangedDocumentTracker class to monitor changes to files in the current project.
 */
define(function (require, exports, module) {
    "use strict";

    var DocumentManager = require("document/DocumentManager"),
        ProjectManager  = require("project/ProjectManager");

    /**
     * Tracks "change" events on opened Documents. Used to monitor changes
     * to documents in-memory and update caches. Assumes all documents have
     * changed when the Brackets window loses and regains focus. Does not
     * read timestamps of files on disk. Clients may optionally track file
     * timestamps on disk independently.
     */
    function ChangedDocumentTracker() {
        var self = this;

        this._changedPaths = {};
        this._windowFocus = true;
        this._addListener = this._addListener.bind(this);
        this._removeListener = this._removeListener.bind(this);
        this._onChange = this._onChange.bind(this);
        this._onWindowFocus = this._onWindowFocus.bind(this);

        $(DocumentManager).on("afterDocumentCreate", function (event, doc) {
            // Only track documents in the current project
            if (ProjectManager.isWithinProject(doc.file.fullPath)) {
                self._addListener(doc);
            }
        });

        $(DocumentManager).on("beforeDocumentDelete", function (event, doc) {
            // In case a document somehow remains loaded after its project
            // has been closed, unconditionally attempt to remove the listener.
            self._removeListener(doc);
        });

        $(window).focus(this._onWindowFocus);
    }

    /**
     * @private
     * Assumes all files are changed when the window loses and regains focus.
     */
    ChangedDocumentTracker.prototype._addListener = function (doc) {
        $(doc).on("change", this._onChange);
    };

    /**
     * @private
     */
    ChangedDocumentTracker.prototype._removeListener = function (doc) {
        $(doc).off("change", this._onChange);
    };

    /**
     * @private
     * Assumes all files are changed when the window loses and regains focus.
     */
    ChangedDocumentTracker.prototype._onWindowFocus = function (event, doc) {
        this._windowFocus = true;
    };

    /**
     * @private
     * Tracks changed documents.
     */
    ChangedDocumentTracker.prototype._onChange = function (event, doc) {
        // if it was already changed, and the client hasn't reset the tracker,
        // then leave it changed.
        this._changedPaths[doc.file.fullPath] = true;
    };

    /**
     * Empty the set of dirty paths. Begin tracking new dirty documents.
     */
    ChangedDocumentTracker.prototype.reset = function () {
        this._changedPaths = {};
        this._windowFocus = false;
    };

    /**
     * Check if a file path is dirty.
     * @param {!string} file path
     * @return {!boolean} Returns true if the file was dirtied since the last reset.
     */
    ChangedDocumentTracker.prototype.isPathChanged = function (path) {
        return this._windowFocus || this._changedPaths[path];
    };

    /**
     * Get the set of changed paths since the last reset.
     * @return {Array.<string>} Changed file paths
     */
    ChangedDocumentTracker.prototype.getChangedPaths = function () {
        return $.makeArray(this._changedPaths);
    };

    module.exports = ChangedDocumentTracker;
});


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

/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define */

define(function (require, exports, module) {

    "use strict";

    // Registry for languages that have specific per-language URLs or file paths that we use
    // elsewhere in Brackets.
    //
    // TODO: dynamically populate the local prefix list below?
    module.exports = {
        root: true,
        "cs": true,
        "de": true,
        "es": true,
		"fa-ir": true,
        "fi": true,
        "fr": true,
        "it": true,
        "ja": true,
        "nb": true,
        "pl": true,
        "pt-br": true,
        "pt-pt": true,
        "ru": true,
        "sv": true,
        "zh-cn": true,
        "tr": true
    };
});


$indicator.hide();
}
}
/**
* Updates a status indicator

// The "busy" spinner should always be leftmost

var $busyIndicator = $("#status-bar .spinner");


.gitmodules
deps
docs
Makefile
node_modules
test
tools


/*
 * jsTree default-rtl theme 1.0
 * Supported features: dots/no-dots, icons/no-icons, focused, loading
 * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
 */

.jstree-default-rtl li,
.jstree-default-rtl ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
.jstree-default-rtl li { background-position:-90px 0; background-repeat:repeat-y; }
.jstree-default-rtl li.jstree-last { background:transparent; }
.jstree-default-rtl .jstree-open > ins { background-position:-72px 0; }
.jstree-default-rtl .jstree-closed > ins { background-position:-54px 0; }
.jstree-default-rtl .jstree-leaf > ins { background-position:-36px 0; }

.jstree-default-rtl .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
.jstree-default-rtl .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
.jstree-default-rtl a .jstree-icon { background-position:-56px -19px; }
.jstree-default-rtl a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }

.jstree-default-rtl.jstree-focused { background:#ffffee; }

.jstree-default-rtl .jstree-no-dots li,
.jstree-default-rtl .jstree-no-dots .jstree-leaf > ins { background:transparent; }
.jstree-default-rtl .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
.jstree-default-rtl .jstree-no-dots .jstree-closed > ins { background-position:0 0; }

.jstree-default-rtl .jstree-no-icons a .jstree-icon { display:none; }

.jstree-default-rtl .jstree-search { font-style:italic; }

.jstree-default-rtl .jstree-no-icons .jstree-checkbox { display:inline-block; }
.jstree-default-rtl .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }

#vakata-dragged.jstree-default-rtl ins { background:transparent !important; }
#vakata-dragged.jstree-default-rtl .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
#vakata-dragged.jstree-default-rtl .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
#jstree-marker.jstree-default-rtl { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }

.jstree-default-rtl a.jstree-search { color:aqua; }
.jstree-default-rtl .jstree-locked a { color:silver; cursor:default; }

#vakata-contextmenu.jstree-default-rtl-context,
#vakata-contextmenu.jstree-default-rtl-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
#vakata-contextmenu.jstree-default-rtl-context li { }
#vakata-contextmenu.jstree-default-rtl-context a { color:black; }
#vakata-contextmenu.jstree-default-rtl-context a:hover,
#vakata-contextmenu.jstree-default-rtl-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a,
#vakata-contextmenu.jstree-default-rtl-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
#vakata-contextmenu.jstree-default-rtl-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
#vakata-contextmenu.jstree-default-rtl-context li ul { margin-left:-4px; }

/* IE6 BEGIN */
.jstree-default-rtl li,
.jstree-default-rtl ins,
#vakata-dragged.jstree-default-rtl .jstree-invalid,
#vakata-dragged.jstree-default-rtl .jstree-ok,
#jstree-marker.jstree-default-rtl { _background-image:url("d.gif"); }
.jstree-default-rtl .jstree-open ins { _background-position:-72px 0; }
.jstree-default-rtl .jstree-closed ins { _background-position:-54px 0; }
.jstree-default-rtl .jstree-leaf ins { _background-position:-36px 0; }
.jstree-default-rtl a ins.jstree-icon { _background-position:-56px -19px; }
#vakata-contextmenu.jstree-default-rtl-context ins { _display:none; }
#vakata-contextmenu.jstree-default-rtl-context li { _zoom:1; }
.jstree-default-rtl .jstree-undetermined a .jstree-checkbox { _background-position:-18px -19px; }
.jstree-default-rtl .jstree-checked a .jstree-checkbox { _background-position:-36px -19px; }
.jstree-default-rtl .jstree-unchecked a .jstree-checkbox { _background-position:0px -19px; }
/* IE6 END */

/* RTL part */
.jstree-default-rtl .jstree-hovered, .jstree-default-rtl .jstree-clicked { padding:0 1px 0 2px; }
.jstree-default-rtl li { background-image:url("dots.gif"); background-position: 100% 0px; }
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox { background-position:-36px -19px; margin-left:2px; }
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox { background-position:0px -19px; margin-left:2px; }
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox { background-position:-18px -19px; margin-left:2px; }
.jstree-default-rtl .jstree-checked > a > .jstree-checkbox:hover { background-position:-36px -37px; }
.jstree-default-rtl .jstree-unchecked > a > .jstree-checkbox:hover { background-position:0px -37px; }
.jstree-default-rtl .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-18px -37px; }


diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js
index c5a741c..a2e0d8e 100644
--- a/lib/_stream_duplex.js
+++ b/lib/_stream_duplex.js
@@ -26,8 +26,8 @@

 module.exports = Duplex;
 var util = require('util');
-var Readable = require('_stream_readable');
-var Writable = require('_stream_writable');
+var Readable = require('./_stream_readable');
+var Writable = require('./_stream_writable');

 util.inherits(Duplex, Readable);

diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js
index a5e9864..330c247 100644
--- a/lib/_stream_passthrough.js
+++ b/lib/_stream_passthrough.js
@@ -25,7 +25,7 @@

 module.exports = PassThrough;

-var Transform = require('_stream_transform');
+var Transform = require('./_stream_transform');
 var util = require('util');
 util.inherits(PassThrough, Transform);

diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js
index 2259d2e..e6681ee 100644
--- a/lib/_stream_readable.js
+++ b/lib/_stream_readable.js
@@ -23,6 +23,9 @@ module.exports = Readable;
 Readable.ReadableState = ReadableState;

 var EE = require('events').EventEmitter;
+if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
+  return emitter.listeners(type).length;
+};
 var Stream = require('stream');
 var util = require('util');
 var StringDecoder;
diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js
index e925b4b..f08b05e 100644
--- a/lib/_stream_transform.js
+++ b/lib/_stream_transform.js
@@ -64,7 +64,7 @@

 module.exports = Transform;

-var Duplex = require('_stream_duplex');
+var Duplex = require('./_stream_duplex');
 var util = require('util');
 util.inherits(Transform, Duplex);

diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js
index a26f711..56ca47d 100644
--- a/lib/_stream_writable.js
+++ b/lib/_stream_writable.js
@@ -109,7 +109,7 @@ function WritableState(options, stream) {
 function Writable(options) {
   // Writable ctor is applied to Duplexes, though they're not
   // instanceof Writable, they're instanceof Readable.
-  if (!(this instanceof Writable) && !(this instanceof Stream.Duplex))
+  if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex')))
     return new Writable(options);

   this._writableState = new WritableState(options, this);


var CHECKSUM = {
build: 'eb7dc200-8100-11e7-b353-1b576d4fdd2c'
};
module.exports = CHECKSUM;


//  Phaser.Geom.Line

var Line = require('./Line');

Line.Angle = require('./Angle');
Line.CenterOn = require('./CenterOn');
Line.Clone = require('./Clone');
Line.CopyFrom = require('./CopyFrom');
Line.Equals = require('./Equals');
Line.GetMidPoint = require('./GetMidPoint');
Line.GetNormal = require('./GetNormal');
Line.GetPoint = require('./GetPoint');
Line.GetPointsOnLine = require('./GetPointsOnLine');
Line.Height = require('./Height');
Line.Length = require('./Length');
Line.NormalAngle = require('./NormalAngle');
Line.NormalX = require('./NormalX');
Line.NormalY = require('./NormalY');
Line.Offset = require('./Offset');
Line.PerpSlope = require('./PerpSlope');
Line.Random = require('./Random');
Line.ReflectAngle = require('./ReflectAngle');
Line.Rotate = require('./Rotate');
Line.RotateAroundPoint = require('./RotateAroundPoint');
Line.RotateAroundXY = require('./RotateAroundXY');
Line.SetToAngle = require('./SetToAngle');
Line.Slope = require('./Slope');
Line.Width = require('./Width');

module.exports = Line;


var SetPollRate = function (value)
{
    this.pollRate = value;
    this._pollTimer = 0;

    return this;
};

module.exports = SetPollRate;


//  Browser specific prefix, so not going to change between contexts, only between browsers
var prefix = '';

var Smoothing = function ()
{
    // Gets the Smoothing Enabled vendor prefix being used on the given context, or null if not set.
    var getPrefix = function (context)
    {
        var vendors = [ 'i', 'webkitI', 'msI', 'mozI', 'oI' ];

        for (var i = 0; i < vendors.length; i++)
        {
            var s = vendors[i] + 'mageSmoothingEnabled';

            if (s in context)
            {
                return s;
            }
        }

        return null;
    };

    /**
    * Sets the Image Smoothing property on the given context. Set to false to disable image smoothing.
    * By default browsers have image smoothing enabled, which isn't always what you visually want, especially
    * when using pixel art in a game. Note that this sets the property on the context itself, so that any image
    * drawn to the context will be affected. This sets the property across all current browsers but support is
    * patchy on earlier browsers, especially on mobile.
    */
    var enable = function (context)
    {
        if (prefix === '')
        {
            prefix = getPrefix(context);
        }

        if (prefix)
        {
            context[prefix] = true;
        }

        return context;
    };

    /**
    * Sets the Image Smoothing property on the given context. Set to false to disable image smoothing.
    * By default browsers have image smoothing enabled, which isn't always what you visually want, especially
    * when using pixel art in a game. Note that this sets the property on the context itself, so that any image
    * drawn to the context will be affected. This sets the property across all current browsers but support is
    * patchy on earlier browsers, especially on mobile.
    */
    var disable = function (context)
    {
        if (prefix === '')
        {
            prefix = getPrefix(context);
        }

        if (prefix)
        {
            context[prefix] = false;
        }

        return context;
    };

    /**
     * Returns `true` if the given context has image smoothing enabled, otherwise returns `false`.
     * Returns null if no smoothing prefix is available.
     */
    var isEnabled = function (context)
    {
        return (prefix !== null) ? context[prefix] : null;
    };

    return {
        disable: disable,
        enable: enable,
        getPrefix: getPrefix,
        isEnabled: isEnabled
    };
};

module.exports = Smoothing();


keep keep keep keep replace keep keep keep keep keep

var DeferredRenderer = function (renderer, lightLayer, interpolationPercentage, camera)
{
    var spriteList = lightLayer.sprites;
    var length = spriteList.length;
    var batch = renderer.spriteBatch;
    var gl = renderer.gl;

    if (this.renderMask !== this.renderFlags)
    {
        return;
    }

    if (renderer.currentRenderer !== null)
    {
        renderer.currentRenderer.flush();
    }

    batch.bind(lightLayer.gBufferShaderPass);
    batch.indexBufferObject.bind();
    lightLayer.updateLights(renderer, camera, this.lightPassShader);

    gl.bindFramebuffer(gl.FRAMEBUFFER, lightLayer.gBufferFbo);
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    for (var index = 0; index < length; ++index)
    {
        var spriteNormalPair = spriteList[index];
        var gameObject = spriteNormalPair.spriteRef;

        /* Inlined function of add sprite modified. */
        {
            var tempMatrix = batch.tempMatrix;
            var frame = gameObject.frame;
            var forceFlipY = (frame.texture.source[frame.sourceIndex].glTexture.isRenderTexture ? true : false);
            var flipX = gameObject.flipX;
            var flipY = gameObject.flipY ^ forceFlipY;
            var vertexDataBuffer = batch.vertexDataBuffer;
            var vertexBufferObjectF32 = vertexDataBuffer.floatView;
            var vertexBufferObjectU32 = vertexDataBuffer.uintView;
            var vertexOffset = 0;
            var uvs = frame.uvs;
            var width = frame.width * (flipX ? -1 : 1);
            var height = frame.height * (flipY ? -1 : 1);
            var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
            var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
            var scaleX = gameObject.scaleX;
            var scaleY = gameObject.scaleY;
            var rotation = -gameObject.rotation;
            var tempMatrixMatrix = tempMatrix.matrix;
            var x = -gameObject.displayOriginX + frame.x + ((frame.width) * (flipX ? 1 : 0.0));
            var y = -gameObject.displayOriginY + frame.y + ((frame.height) * (flipY ? 1 : 0.0));
            var xw = x + width;
            var yh = y + height;
            var cameraMatrix = camera.matrix.matrix;
            var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
            var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
            var alphaTL = gameObject._alphaTL;
            var alphaTR = gameObject._alphaTR;
            var alphaBL = gameObject._alphaBL;
            var alphaBR = gameObject._alphaBR;
            var tintTL = gameObject._tintTL;
            var tintTR = gameObject._tintTR;
            var tintBL = gameObject._tintBL;
            var tintBR = gameObject._tintBR;

            tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

            sra = tempMatrixMatrix[0];
            srb = tempMatrixMatrix[1];
            src = tempMatrixMatrix[2];
            srd = tempMatrixMatrix[3];
            sre = tempMatrixMatrix[4];
            srf = tempMatrixMatrix[5];

            cma = cameraMatrix[0];
            cmb = cameraMatrix[1];
            cmc = cameraMatrix[2];
            cmd = cameraMatrix[3];
            cme = cameraMatrix[4];
            cmf = cameraMatrix[5];

            mva = sra * cma + srb * cmc;
            mvb = sra * cmb + srb * cmd;
            mvc = src * cma + srd * cmc;
            mvd = src * cmb + srd * cmd;
            mve = sre * cma + srf * cmc + cme;
            mvf = sre * cmb + srf * cmd + cmf;

            tx0 = x * mva + y * mvc + mve;
            ty0 = x * mvb + y * mvd + mvf;
            tx1 = x * mva + yh * mvc + mve;
            ty1 = x * mvb + yh * mvd + mvf;
            tx2 = xw * mva + yh * mvc + mve;
            ty2 = xw * mvb + yh * mvd + mvf;
            tx3 = xw * mva + y * mvc + mve;
            ty3 = xw * mvb + y * mvd + mvf;

            if (renderer.currentTexture[0] !== frame.texture.source[frame.sourceIndex].glTexture ||
                renderer.currentTexture[1] !== spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture)
            {
                batch.flush(lightLayer.gBufferShaderPass, lightLayer.renderTarget);
                renderer.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
                renderer.setTexture(spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture, 1);
            }

            batch.drawIndexed = true;
            batch.drawingMesh = false;
            vertexOffset = vertexDataBuffer.allocate(24);
            batch.elementCount += 6;

            //  Top Left
            vertexBufferObjectF32[vertexOffset++] = tx0;
            vertexBufferObjectF32[vertexOffset++] = ty0;
            vertexBufferObjectF32[vertexOffset++] = uvs.x0;
            vertexBufferObjectF32[vertexOffset++] = uvs.y0;
            vertexBufferObjectU32[vertexOffset++] = tintTL;
            vertexBufferObjectF32[vertexOffset++] = alphaTL;

            //  Bottom Left
            vertexBufferObjectF32[vertexOffset++] = tx1;
            vertexBufferObjectF32[vertexOffset++] = ty1;
            vertexBufferObjectF32[vertexOffset++] = uvs.x1;
            vertexBufferObjectF32[vertexOffset++] = uvs.y1;
            vertexBufferObjectU32[vertexOffset++] = tintBL;
            vertexBufferObjectF32[vertexOffset++] = alphaBL;

            //  Bottom Right
            vertexBufferObjectF32[vertexOffset++] = tx2;
            vertexBufferObjectF32[vertexOffset++] = ty2;
            vertexBufferObjectF32[vertexOffset++] = uvs.x2;
            vertexBufferObjectF32[vertexOffset++] = uvs.y2;
            vertexBufferObjectU32[vertexOffset++] = tintBR;
            vertexBufferObjectF32[vertexOffset++] = alphaBR;

            //  Top Right
            vertexBufferObjectF32[vertexOffset++] = tx3;
            vertexBufferObjectF32[vertexOffset++] = ty3;
            vertexBufferObjectF32[vertexOffset++] = uvs.x3;
            vertexBufferObjectF32[vertexOffset++] = uvs.y3;
            vertexBufferObjectU32[vertexOffset++] = tintTR;
            vertexBufferObjectF32[vertexOffset++] = alphaTR;
        }
    }

    batch.flush(lightLayer.gBufferShaderPass, lightLayer.renderTarget);
    renderer.setTexture({texture: lightLayer.gBufferColorTex}, 0);
    renderer.setTexture({texture: lightLayer.gBufferNormalTex}, 1);

    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    gl.useProgram(lightLayer.lightPassShader.program);
    gl.bindBuffer(gl.ARRAY_BUFFER, lightLayer.lightPassVBO);
    gl.enableVertexAttribArray(0);
    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, Float32Array.BYTES_PER_ELEMENT, 0);
    gl.drawArrays(gl.TRIANGLES, 0, 3);

    batch.bind();
};

module.exports = DeferredRenderer;


// Cerulean 2.3.2
// Variables
// --------------------------------------------------

// GLOBAL VALUES
// --------------------------------------------------

// Grays
// -------------------------
@black:                 #000;
@grayDarker:            #222;
@grayDark:              #333;
@gray:                  #555;
@grayLight:             #999;
@grayLighter:           #F5F5F5;
@white:                 #fff;

// Accent colors
// -------------------------
@blue:                  #2FA4E7;
@blueDark:              #033C73;
@green:                 #73A839;
@red:                   #C71C22;
@yellow:                #F7B42C;
@orange:                #DD5600;
@pink:                  #F49AC1;
@purple:                #9760B3;

// Scaffolding
// -------------------------
@bodyBackground:        @white;
@textColor:             @gray;

// Links
// -------------------------
@linkColor:             @blue;
@linkColorHover:        darken(@linkColor, 15%);

// Typography
// -------------------------
@sansFontFamily:        "Helvetica Neue", Helvetica, Arial, sans-serif;
@serifFontFamily:       Georgia, "Times New Roman", Times, serif;
@monoFontFamily:        Menlo, Monaco, Consolas, "Courier New", monospace;

@baseFontSize:          14px;
@baseFontFamily:        @sansFontFamily;
@baseLineHeight:        20px;
@altFontFamily:         @monoFontFamily;

@headingsFontFamily:    'Telex', sans-serif; // empty to use BS default, @baseFontFamily
@headingsFontWeight:    bold;    // instead of browser default, bold
@headingsColor:         #317EAC; // empty to use BS default, @textColor

// Component sizing
// -------------------------
// Based on 14px font-size and 20px line-height

@fontSizeLarge:         @baseFontSize * 1.25; // ~18px
@fontSizeSmall:         @baseFontSize * 0.85; // ~12px
@fontSizeMini:          @baseFontSize * 0.75; // ~11px

@paddingLarge:          11px 19px; // 44px
@paddingSmall:          2px 10px;  // 26px
@paddingMini:           0px 6px;   // 22px

@baseBorderRadius:      4px;
@borderRadiusLarge:     6px;
@borderRadiusSmall:     3px;

// Tables
// -------------------------
@tableBackground:                   transparent; // overall background-color
@tableBackgroundAccent:             #f9f9f9; // for striping
@tableBackgroundHover:              #f5f5f5; // for hover
@tableBorder:                       #ddd; // table and cell border

// Buttons
// -------------------------
@btnBackground:                     @white;
@btnBackgroundHighlight:            darken(@white, 10%);
@btnBorder:                         darken(@white, 20%);

@btnPrimaryBackground:              @linkColor;
@btnPrimaryBackgroundHighlight:     spin(@btnPrimaryBackground, 15%);

@btnInfoBackground:                 @purple;
@btnInfoBackgroundHighlight:        #2f96b4;

@btnSuccessBackground:              @green;
@btnSuccessBackgroundHighlight:     #51a351;

@btnWarningBackground:              @orange;
@btnWarningBackgroundHighlight:     @orange;

@btnDangerBackground:               @red;
@btnDangerBackgroundHighlight:      #bd362f;

@btnInverseBackground:              @blueDark;
@btnInverseBackgroundHighlight:     @grayDarker;

// Forms
// -------------------------
@inputBackground:               @white;
@inputBorder:                   #ccc;
@inputBorderRadius:             @baseBorderRadius;
@inputDisabledBackground:       @grayLighter;
@formActionsBackground:         #f5f5f5;
@inputHeight:                   @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border

// Dropdowns
// -------------------------
@dropdownBackground:            @white;
@dropdownBorder:                rgba(0,0,0,.2);
@dropdownDividerTop:            #e5e5e5;
@dropdownDividerBottom:         @white;

@dropdownLinkColor:             @grayDark;
@dropdownLinkColorHover:        @white;
@dropdownLinkColorActive:       @white;

@dropdownLinkBackgroundActive:  @linkColor;
@dropdownLinkBackgroundHover:   @dropdownLinkBackgroundActive;

// COMPONENT VARIABLES
// --------------------------------------------------

// Z-index master list
// -------------------------
// Used for a bird's eye view of components dependent on the z-axis
// Try to avoid customizing these :)
@zindexDropdown:          1000;
@zindexPopover:           1010;
@zindexTooltip:           1020;
@zindexFixedNavbar:       1030;
@zindexModalBackdrop:     1040;
@zindexModal:             1050;

// Sprite icons path
// -------------------------
@iconSpritePath:          "../img/glyphicons-halflings.png";
@iconWhiteSpritePath:     "../img/glyphicons-halflings-white.png";

// Input placeholder text color
// -------------------------
@placeholderText:         @grayLight;

// Hr border color
// -------------------------
@hrBorder:                @grayLighter;

// Horizontal forms & lists
// -------------------------
@horizontalComponentOffset:       180px;

// Wells
// -------------------------
@wellBackground:                  #f5f5f5;

// Navbar
// -------------------------
@navbarCollapseWidth:             979px;
@navbarCollapseDesktopWidth:      @navbarCollapseWidth + 1;

@navbarHeight:                    50px;
@navbarBackgroundHighlight:       lighten(@navbarBackground, 8%);
@navbarBackground:                @blue;
@navbarBorder:                    darken(@navbarBackground, 8%);

@navbarText:                      @grayLighter;
@navbarLinkColor:                 @white;
@navbarLinkColorHover:            @white;
@navbarLinkColorActive:           @navbarLinkColorHover;
@navbarLinkBackgroundHover:       darken(@navbarBackground, 12%);
@navbarLinkBackgroundActive:      darken(@navbarBackground, 12%);

@navbarBrandColor:                @navbarLinkColor;

// Inverted navbar
@navbarInverseBackground:                @blueDark;
@navbarInverseBackgroundHighlight:       lighten(@navbarInverseBackground, 5%);
@navbarInverseBorder:                    darken(@navbarInverseBackground, 3%);

@navbarInverseText:                      @white;
@navbarInverseLinkColor:                 @white;
@navbarInverseLinkColorHover:            @white;
@navbarInverseLinkColorActive:           @white;
@navbarInverseLinkBackgroundHover:       darken(@navbarInverseBackground, 6%);
@navbarInverseLinkBackgroundActive:      darken(@navbarInverseBackground, 6%);

@navbarInverseSearchBackground:          @white;
@navbarInverseSearchBackgroundFocus:     @white;
@navbarInverseSearchBorder:              @navbarInverseBackground;
@navbarInverseSearchPlaceholderColor:    @grayLight;

@navbarInverseBrandColor:                @navbarInverseLinkColor;

// Pagination
// -------------------------
@paginationBackground:                #fff;
@paginationBorder:                    #ddd;
@paginationActiveBackground:          #f5f5f5;

// Hero unit
// -------------------------
@heroUnitBackground:              @grayLighter;
@heroUnitHeadingColor:            inherit;
@heroUnitLeadColor:               inherit;

// Form states and alerts
// -------------------------
@warningText:             @orange;
@warningBackground:       #F1CEAB;
@warningBorder:           darken(spin(@warningBackground, -10), 3%);

@errorText:               darken(#C45559, 5%);
@errorBackground:         #F2BDB1;
@errorBorder:             darken(spin(@errorBackground, -10), 3%);

@successText:             darken(@green, 5%);
@successBackground:       #D5ECBF;
@successBorder:           darken(spin(@successBackground, -10), 5%);

@infoText:                darken(@blue, 10%);
@infoBackground:          #A7DFF1;
@infoBorder:              darken(spin(@infoBackground, -10), 7%);

// Tooltips and popovers
// -------------------------
@tooltipColor:            #fff;
@tooltipBackground:       #000;
@tooltipArrowWidth:       5px;
@tooltipArrowColor:       @tooltipBackground;

@popoverBackground:       #fff;
@popoverArrowWidth:       10px;
@popoverArrowColor:       #fff;
@popoverTitleBackground:  darken(@popoverBackground, 3%);

// Special enhancement for popovers
@popoverArrowOuterWidth:  @popoverArrowWidth + 1;
@popoverArrowOuterColor:  rgba(0,0,0,.25);

// GRID
// --------------------------------------------------

// Default 940px grid
// -------------------------
@gridColumns:             12;
@gridColumnWidth:         60px;
@gridGutterWidth:         20px;
@gridRowWidth:            (@gridColumns * @gridColumnWidth) + (@gridGutterWidth * (@gridColumns - 1));

// 1200px min
@gridColumnWidth1200:     70px;
@gridGutterWidth1200:     30px;
@gridRowWidth1200:        (@gridColumns * @gridColumnWidth1200) + (@gridGutterWidth1200 * (@gridColumns - 1));

// 768px-979px
@gridColumnWidth768:      42px;
@gridGutterWidth768:      20px;
@gridRowWidth768:         (@gridColumns * @gridColumnWidth768) + (@gridGutterWidth768 * (@gridColumns - 1));

// Fluid grid
// -------------------------
@fluidGridColumnWidth:    percentage(@gridColumnWidth/@gridRowWidth);
@fluidGridGutterWidth:    percentage(@gridGutterWidth/@gridRowWidth);

// 1200px min
@fluidGridColumnWidth1200:     percentage(@gridColumnWidth1200/@gridRowWidth1200);
@fluidGridGutterWidth1200:     percentage(@gridGutterWidth1200/@gridRowWidth1200);

// 768px-979px
@fluidGridColumnWidth768:      percentage(@gridColumnWidth768/@gridRowWidth768);
@fluidGridGutterWidth768:      percentage(@gridGutterWidth768/@gridRowWidth768);


var Centroid = require('./Centroid');
var Offset = require('./Offset');

var CenterOn = function (triangle, x, y, centerFunc)
{
    if (centerFunc === undefined) { centerFunc = Centroid; }

    //  Get the center of the triangle
    var center = centerFunc(triangle);

    //  Difference
    var diffX = x - center.x;
    var diffY = y - center.y;

    return Offset(triangle, diffX, diffY);
};

module.exports = CenterOn;


/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* Dirty! Manager
*
* @class
*/
Phaser.UpdateManager = function (state)
{
    this.state = state;

    this.game = state.game;

    this.list = [];

    // this.i = 1;

    this.running = false;

    this.processed = 0;
};

Phaser.UpdateManager.prototype.constructor = Phaser.UpdateManager;

Phaser.UpdateManager.prototype = {

    stop: function ()
    {
        if (!this.running)
        {
            return;
        }

        // console.log(this.i, 'UpdateManager.stop', this.processed);

        this.list.length = 0;

        // this.i++;
    },

    start: function ()
    {
        if (!this.running)
        {
            return;
        }

        var len = this.list.length;

        if (len === 0)
        {
            return;
        }

        // console.log(this.i, 'UpdateManager.start', len);

        this.processed = 0;

        for (var i = 0; i < len; i++)
        {
            //  Because it may have already been processed (as a child of another Transform that was updated)
            if (this.list[i] && this.list[i]._dirty)
            {
                this.processed++;
                this.list[i].update();
            }
        }
    },

    add: function (transform)
    {
        this.list.push(transform);
    }

};


keep keep keep keep keep replace keep keep keep keep

var ForwardRenderer = function (renderer, lightLayer, interpolationPercentage, camera)
{
    var spriteList = lightLayer.sprites;
    var length = spriteList.length;
    var batch = renderer.spriteBatch;

    if (this.renderMask !== this.renderFlags || length === 0)
    {
        return;
    }

    if (renderer.currentRenderer !== null)
    {
        renderer.currentRenderer.flush();
    }

    batch.bind(lightLayer.passShader);
    batch.indexBufferObject.bind();
    lightLayer.updateLights(renderer, camera, this.passShader);

    for (var index = 0; index < length; ++index)
    {
        var spriteNormalPair = spriteList[index];
        var gameObject = spriteNormalPair.spriteRef;

        /* Inlined function of add sprite modified. */
        {
            var tempMatrix = batch.tempMatrix;
            var frame = gameObject.frame;
            var forceFlipY = (frame.texture.source[frame.sourceIndex].glTexture.isRenderTexture ? true : false);
            var flipX = gameObject.flipX;
            var flipY = gameObject.flipY ^ forceFlipY;
            var vertexDataBuffer = batch.vertexDataBuffer;
            var vertexBufferObjectF32 = vertexDataBuffer.floatView;
            var vertexBufferObjectU32 = vertexDataBuffer.uintView;
            var vertexOffset = 0;
            var uvs = frame.uvs;
            var width = frame.width * (flipX ? -1 : 1);
            var height = frame.height * (flipY ? -1 : 1);
            var translateX = gameObject.x - camera.scrollX * gameObject.scrollFactorX;
            var translateY = gameObject.y - camera.scrollY * gameObject.scrollFactorY;
            var scaleX = gameObject.scaleX;
            var scaleY = gameObject.scaleY;
            var rotation = -gameObject.rotation;
            var tempMatrixMatrix = tempMatrix.matrix;
            var x = -gameObject.displayOriginX + frame.x + ((frame.width) * (flipX ? 1 : 0.0));
            var y = -gameObject.displayOriginY + frame.y + ((frame.height) * (flipY ? 1 : 0.0));
            var xw = x + width;
            var yh = y + height;
            var cameraMatrix = camera.matrix.matrix;
            var mva, mvb, mvc, mvd, mve, mvf, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3;
            var sra, srb, src, srd, sre, srf, cma, cmb, cmc, cmd, cme, cmf;
            var alphaTL = gameObject._alphaTL;
            var alphaTR = gameObject._alphaTR;
            var alphaBL = gameObject._alphaBL;
            var alphaBR = gameObject._alphaBR;
            var tintTL = gameObject._tintTL;
            var tintTR = gameObject._tintTR;
            var tintBL = gameObject._tintBL;
            var tintBR = gameObject._tintBR;

            tempMatrix.applyITRS(translateX, translateY, rotation, scaleX, scaleY);

            sra = tempMatrixMatrix[0];
            srb = tempMatrixMatrix[1];
            src = tempMatrixMatrix[2];
            srd = tempMatrixMatrix[3];
            sre = tempMatrixMatrix[4];
            srf = tempMatrixMatrix[5];

            cma = cameraMatrix[0];
            cmb = cameraMatrix[1];
            cmc = cameraMatrix[2];
            cmd = cameraMatrix[3];
            cme = cameraMatrix[4];
            cmf = cameraMatrix[5];

            mva = sra * cma + srb * cmc;
            mvb = sra * cmb + srb * cmd;
            mvc = src * cma + srd * cmc;
            mvd = src * cmb + srd * cmd;
            mve = sre * cma + srf * cmc + cme;
            mvf = sre * cmb + srf * cmd + cmf;

            tx0 = x * mva + y * mvc + mve;
            ty0 = x * mvb + y * mvd + mvf;
            tx1 = x * mva + yh * mvc + mve;
            ty1 = x * mvb + yh * mvd + mvf;
            tx2 = xw * mva + yh * mvc + mve;
            ty2 = xw * mvb + yh * mvd + mvf;
            tx3 = xw * mva + y * mvc + mve;
            ty3 = xw * mvb + y * mvd + mvf;

            if (renderer.currentTexture[0] !== frame.texture.source[frame.sourceIndex].glTexture ||
                renderer.currentTexture[1] !== spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture)
            {
                batch.flush(lightLayer.passShader, lightLayer.renderTarget);
                renderer.setTexture(frame.texture.source[frame.sourceIndex].glTexture, 0);
                renderer.setTexture(spriteNormalPair.normalTextureRef.source[spriteNormalPair.spriteRef.frame.sourceIndex].glTexture, 1);
            }

            batch.drawIndexed = true;
            batch.drawingMesh = false;
            vertexOffset = vertexDataBuffer.allocate(24);
            batch.elementCount += 6;

            //  Top Left
            vertexBufferObjectF32[vertexOffset++] = tx0;
            vertexBufferObjectF32[vertexOffset++] = ty0;
            vertexBufferObjectF32[vertexOffset++] = uvs.x0;
            vertexBufferObjectF32[vertexOffset++] = uvs.y0;
            vertexBufferObjectU32[vertexOffset++] = tintTL;
            vertexBufferObjectF32[vertexOffset++] = alphaTL;

            //  Bottom Left
            vertexBufferObjectF32[vertexOffset++] = tx1;
            vertexBufferObjectF32[vertexOffset++] = ty1;
            vertexBufferObjectF32[vertexOffset++] = uvs.x1;
            vertexBufferObjectF32[vertexOffset++] = uvs.y1;
            vertexBufferObjectU32[vertexOffset++] = tintBL;
            vertexBufferObjectF32[vertexOffset++] = alphaBL;

            //  Bottom Right
            vertexBufferObjectF32[vertexOffset++] = tx2;
            vertexBufferObjectF32[vertexOffset++] = ty2;
            vertexBufferObjectF32[vertexOffset++] = uvs.x2;
            vertexBufferObjectF32[vertexOffset++] = uvs.y2;
            vertexBufferObjectU32[vertexOffset++] = tintBR;
            vertexBufferObjectF32[vertexOffset++] = alphaBR;

            //  Top Right
            vertexBufferObjectF32[vertexOffset++] = tx3;
            vertexBufferObjectF32[vertexOffset++] = ty3;
            vertexBufferObjectF32[vertexOffset++] = uvs.x3;
            vertexBufferObjectF32[vertexOffset++] = uvs.y3;
            vertexBufferObjectU32[vertexOffset++] = tintTR;
            vertexBufferObjectF32[vertexOffset++] = alphaTR;
        }
    }

    batch.flush(lightLayer.passShader, lightLayer.renderTarget);

};

module.exports = ForwardRenderer;


var Equals = function (ellipse, toCompare)
{
    return (
        ellipse.x === toCompare.x &&
        ellipse.y === toCompare.y &&
        ellipse.width === toCompare.width &&
        ellipse.height === toCompare.height
    );
};

module.exports = Equals;


var MathSmoothStep = require('../math/SmoothStep');

var SmoothStep = function (items, property, min, max, inc)
{
    if (inc === undefined) { inc = false; }

    var step = Math.abs(max - min) / items.length;
    var i;

    if (inc)
    {
        for (i = 0; i < items.length; i++)
        {
            items[i][property] += MathSmoothStep(i * step, min, max);
        }
    }
    else
    {
        for (i = 0; i < items.length; i++)
        {
            items[i][property] = MathSmoothStep(i * step, min, max);
        }
    }

    return items;
};

module.exports = SmoothStep;


/**
* @author       Richard Davey <rich@photonstorm.com>
* @author       Mat Groves (@Doormat23)
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

Phaser.Renderer.Canvas.GameObjects.Container = {

    TYPES: [
        Phaser.GameObject.Container.prototype
    ],

    render: function (renderer, src)
    {
        //  Skip rendering?

        if (src.visible === false || src.alpha === 0)
        {
            return;
        }

        //  Blend Mode

        if (src.blendMode !== renderer.currentBlendMode)
        {
            renderer.currentBlendMode = src.blendMode;
            renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode];
        }

        //  Alpha (World Alpha?)

        if (src.alpha !== renderer.context.globalAlpha)
        {
            renderer.context.globalAlpha = src.alpha;
        }

        //  Smoothing (should this be a Game Object, or Frame / Texture level property?)

        if (src.scaleMode !== renderer.currentScaleMode)
        {
            renderer.currentScaleMode = src.scaleMode;
            renderer.context[renderer.smoothProperty] = (src.scaleMode === Phaser.scaleModes.LINEAR);
        }

        /*
        if (src._cacheAsBitmap)
        {
            return this.renderCachedSprite(renderer, src);
        }

        if (src._mask)
        {
            renderer.pushMask(src._mask);
        }
        */

        for (var i = 0; i < src.children.list.length; i++)
        {
            var child = src.children.list[i];

            child.render(renderer, child);
        }

        /*
        if (src._mask)
        {
            renderer.popMask();
        }
        */

    },

    renderCachedSprite: function (renderer, src)
    {
        //  TODO
        return src;
    }

};