<commit_before>import fs from 'fs-extra';
import path from 'path';
import ZeroCSS from './zerocss';
import _ from 'lodash';

/* Public interface to the ZeroCSS library */

export default class {
  constructor(breakpoints) {
    this.zerocss = new ZeroCSS(breakpoints);
  }

  castUnitlessToPx(value) {
    /*
     * You can use JavaScript numbers for all pixels values but remmeber to quote
     * numbers different than 0 for properties like z-index or opacity.
     * 0 => 0
     * 123 => 123px
     * '0' => 0
     * '123' => 123
     */
    let strValue;
    if (typeof value === 'number') {
      if (value === 0) {
        strValue = '0';
      } else {
        strValue = `${value.toString()}px`;
      }
    } else {
      strValue = value;
    }
    return strValue;
  }

  addSimpleUtil(name, parensContent, property, value, isResponsive = true, pseudoConfig,
    pseudoBase = true) {
    this.zerocss.addSimpleUtil({ name, parensContent, isResponsive, property,
      value: this.castUnitlessToPx(value), pseudoConfig, pseudoBase });
  }

  getSimpleUtil(name, parensContent, property, value, isResponsive = true, pseudoConfig,
    pseudoBase = true) {
    return this.zerocss.getSimpleUtil({ name, parensContent, isResponsive, property,
      value: this.castUnitlessToPx(value), pseudoConfig, pseudoBase });
  }

  addLoopUtils(rawConfig, rawEntries) {
    const config = _.cloneDeep(rawConfig);
    const entries = {};
    config.isResponsive = config.isResponsive === true;
    config.pseudoBase = config.pseudoBase === true;
    _.forOwn(rawEntries, (value, key) => {
      entries[key] = this.castUnitlessToPx(value);
    });
    this.zerocss.addLoopUtils({ config, entries });
  }

  addSpacingHelperUtils(spacingValues, isResponsive = false) {
    for (const s of spacingValues) {
      for (const type of [{ long: 'margin', short: 'm' }, { long: 'padding', short: 'p' }]) {
        this.addSimpleUtil(type.short, `t\,${s}`, `${type.long}-top`, s, isResponsive);
        this.addSimpleUtil(type.short, `b\,${s}`, `${type.long}-bottom`, s, isResponsive);
        this.addSimpleUtil(type.short, `l\,${s}`, `${type.long}-left`, s, isResponsive);
        this.addSimpleUtil(type.short, `r\,${s}`, `${type.long}-right`, s, isResponsive);

        this.addSimpleUtil(type.short, `t\,${s}\%`, `${type.long}-top`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `b\,${s}\%`, `${type.long}-bottom`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `l\,${s}\%`, `${type.long}-left`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `r\,${s}\%`, `${type.long}-right`, `${s}%`, isResponsive);

        this.addSimpleUtil(type.short, `v\,${s}`, `${type.long}-top`, s, isResponsive);
        this.addSimpleUtil(type.short, `v\,${s}`, `${type.long}-bottom`, s, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}`, `${type.long}-left`, s, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}`, `${type.long}-right`, s, isResponsive);

        this.addSimpleUtil(type.short, `v\,${s}\%`, `${type.long}-top`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `v\,${s}\%`, `${type.long}-bottom`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}\%`, `${type.long}-left`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}\%`, `${type.long}-right`, `${s}%`, isResponsive);

        this.addSimpleUtil(type.short, `${s}`, `${type.long}`, s, isResponsive);
        this.addSimpleUtil(type.short, `${s}\%`, `${type.long}`, `${s}%`, isResponsive);
      }
    }
  }

  build(writePath, isVerbose) {
    if (!this.zerocss.output) {
      this.zerocss.assembleAllRules();
    }

    if (writePath) {
      const appDir = path.dirname(require.main.filename);
      fs.outputFileSync(`${appDir}/${writePath}`, this.zerocss.output);
    }

    if (isVerbose) {
      /* eslint-disable no-console */
      console.log(`\n ${this.zerocss.output}`);
      /* eslint-enable no-console */
    }
    return this.zerocss.output;
  }
}
<commit_msg>Fixed default parameter for pseudoBase<commit_after>import fs from 'fs-extra';
import path from 'path';
import ZeroCSS from './zerocss';
import _ from 'lodash';

/* Public interface to the ZeroCSS library */

export default class {
  constructor(breakpoints) {
    this.zerocss = new ZeroCSS(breakpoints);
  }

  castUnitlessToPx(value) {
    /*
     * You can use JavaScript numbers for all pixels values but remmeber to quote
     * numbers different than 0 for properties like z-index or opacity.
     * 0 => 0
     * 123 => 123px
     * '0' => 0
     * '123' => 123
     */
    let strValue;
    if (typeof value === 'number') {
      if (value === 0) {
        strValue = '0';
      } else {
        strValue = `${value.toString()}px`;
      }
    } else {
      strValue = value;
    }
    return strValue;
  }

  addSimpleUtil(name, parensContent, property, value, isResponsive = true, pseudoConfig,
    pseudoBase = true) {
    this.zerocss.addSimpleUtil({ name, parensContent, isResponsive, property,
      value: this.castUnitlessToPx(value), pseudoConfig, pseudoBase });
  }

  getSimpleUtil(name, parensContent, property, value, isResponsive = true, pseudoConfig,
    pseudoBase = true) {
    return this.zerocss.getSimpleUtil({ name, parensContent, isResponsive, property,
      value: this.castUnitlessToPx(value), pseudoConfig, pseudoBase });
  }

  addLoopUtils(rawConfig, rawEntries) {
    const config = _.cloneDeep(rawConfig);
    const entries = {};
    config.isResponsive = config.isResponsive === true; // false is default
    config.pseudoBase = config.pseudoBase !== false; // true is default
    _.forOwn(rawEntries, (value, key) => {
      entries[key] = this.castUnitlessToPx(value);
    });
    this.zerocss.addLoopUtils({ config, entries });
  }

  addSpacingHelperUtils(spacingValues, isResponsive = false) {
    for (const s of spacingValues) {
      for (const type of [{ long: 'margin', short: 'm' }, { long: 'padding', short: 'p' }]) {
        this.addSimpleUtil(type.short, `t\,${s}`, `${type.long}-top`, s, isResponsive);
        this.addSimpleUtil(type.short, `b\,${s}`, `${type.long}-bottom`, s, isResponsive);
        this.addSimpleUtil(type.short, `l\,${s}`, `${type.long}-left`, s, isResponsive);
        this.addSimpleUtil(type.short, `r\,${s}`, `${type.long}-right`, s, isResponsive);

        this.addSimpleUtil(type.short, `t\,${s}\%`, `${type.long}-top`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `b\,${s}\%`, `${type.long}-bottom`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `l\,${s}\%`, `${type.long}-left`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `r\,${s}\%`, `${type.long}-right`, `${s}%`, isResponsive);

        this.addSimpleUtil(type.short, `v\,${s}`, `${type.long}-top`, s, isResponsive);
        this.addSimpleUtil(type.short, `v\,${s}`, `${type.long}-bottom`, s, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}`, `${type.long}-left`, s, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}`, `${type.long}-right`, s, isResponsive);

        this.addSimpleUtil(type.short, `v\,${s}\%`, `${type.long}-top`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `v\,${s}\%`, `${type.long}-bottom`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}\%`, `${type.long}-left`, `${s}%`, isResponsive);
        this.addSimpleUtil(type.short, `h\,${s}\%`, `${type.long}-right`, `${s}%`, isResponsive);

        this.addSimpleUtil(type.short, `${s}`, `${type.long}`, s, isResponsive);
        this.addSimpleUtil(type.short, `${s}\%`, `${type.long}`, `${s}%`, isResponsive);
      }
    }
  }

  build(writePath, isVerbose) {
    if (!this.zerocss.output) {
      this.zerocss.assembleAllRules();
    }

    if (writePath) {
      const appDir = path.dirname(require.main.filename);
      fs.outputFileSync(`${appDir}/${writePath}`, this.zerocss.output);
    }

    if (isVerbose) {
      /* eslint-disable no-console */
      console.log(`\n ${this.zerocss.output}`);
      /* eslint-enable no-console */
    }
    return this.zerocss.output;
  }
}
<|endoftext|>


<commit_before>'use strict';

var tailFile = require ('./monitor/tail-file');
const LogQueue = require( './monitor/log-queue');

var logQueue = new LogQueue();

var options = {
    beginAt: 'end',
    onMove: 'follow',
    endOnError: false
};

tailFile.tailFile( './test.txt', logQueue, options);

<commit_msg>Put in first version of code<commit_after>'use strict';

var tailFile = require ('./monitor/tail-file');
const LogQueue = require( './monitor/log-queue');

var logQueue = new LogQueue();

var options = {
    beginAt: 'end',
    onMove: 'follow',
    endOnError: false
};

tailFile.tailFile( './test.txt', logQueue, options);

//TODO Test this tomorrow and integrate
var processLogs = function(){
    //TODO Replace this tomorrow
    var startTimer = new Date();

    while( starTime > 5000 && !logQueue.isEmpty()){
        console.log( 'Message', logQueue.pop());
    }
}

var interval = setInterval(function() {
    console.log(str1 + " " + str2);
  }, 10000, "Hello.", "How are you?");

<|endoftext|>


<commit_before>export * from './binding-language';
export * from './syntax-interpreter';<commit_msg>feat(binding-language): enable usage as plugin<commit_after>import {BindingLanguage} from 'aurelia-templating';
import {TemplatingBindingLanguage} from './binding-language';
import {SyntaxInterpreter} from './syntax-interpreter';

function install(aurelia){
  var instance,
      getInstance = c => instance || instance = c.invoke(TemplatingBindingLanguage);

  if(aurelia.container.hasHandler(TemplatingBindingLanguage)){
    instance = aurelia.container.get(TemplatingBindingLanguage);
  }else{
    aurelia.container.registerHandler(TemplatingBindingLanguage, getInstance);
  }

  aurelia.container.registerHandler(BindingLanguage, getInstance);
}

export {
  TemplatingBindingLanguage,
  SyntaxInterpreter,
  install
};<|endoftext|>


<commit_before>import * as drawing from './util/drawing.js';

// TEST
export { default as addTool } from './store/addTool.js';
export {
  default as mouseToolEventDispatcher
} from './eventDispatchers/mouseToolEventDispatcher.js';
export {
  default as lengthMouse
} from './new-api-tools-and-examples/lengthMouse.js';
export {
  default as angleMouse
} from './new-api-tools-and-examples/angleMouse.js';
export {
  default as wwwcMouse
} from './new-api-tools-and-examples/wwwcMouse.js';
export {
  setToolActive,
  setToolEnabled,
  setToolDisabled,
  setToolPassive
} from './store/setToolMode.js';
// END TEST

export { drawing };

export { default as external } from './externalModules.js';
export { default as EVENTS } from './events.js';

export { default as referenceLines } from './referenceLines/index.js';
export { default as orientation } from './orientation/index.js';

export {
  default as requestPoolManager
} from './requestPool/requestPoolManager.js';

export {
  default as setContextToDisplayFontSize
} from './util/setContextToDisplayFontSize.js';
export { default as scrollToIndex } from './util/scrollToIndex.js';
export { default as scroll } from './util/scroll.js';
export { default as roundToDecimal } from './util/roundToDecimal.js';
export {
  projectPatientPointToImagePlane,
  imagePointToPatientPoint,
  planePlaneIntersection
} from './util/pointProjector.js';

export {
  default as pointInsideBoundingBox
} from './util/pointInsideBoundingBox.js';
export { default as pointInEllipse } from './util/pointInEllipse.js';
export { default as makeUnselectable } from './util/makeUnselectable.js';
export {
  default as isMouseButtonEnabled
} from './util/isMouseButtonEnabled.js';
export { default as getRGBPixels } from './util/getRGBPixels.js';
export {
  getDefaultSimultaneousRequests,
  getMaxSimultaneousRequests,
  getBrowserInfo,
  isMobileDevice
} from './util/getMaxSimultaneousRequests.js';

export { default as getLuminance } from './util/getLuminance.js';
export { default as drawTextBox } from './util/drawTextBox.js';
export { default as drawEllipse } from './util/drawEllipse.js';
export { default as drawCircle } from './util/drawCircle.js';
export { default as drawArrow } from './util/drawArrow.js';
export { default as copyPoints } from './util/copyPoints.js';
export { default as calculateSUV } from './util/calculateSUV.js';
export {
  default as calculateEllipseStatistics
} from './util/calculateEllipseStatistics.js';

export { default as probeTool4D } from './timeSeriesTools/probeTool4D.js';
export {
  default as incrementTimePoint
} from './timeSeriesTools/incrementTimePoint.js';
export {
  default as timeSeriesPlayer
} from './timeSeriesTools/timeSeriesPlayer.js';
export {
  timeSeriesScroll,
  timeSeriesScrollWheel,
  timeSeriesScrollTouchDrag
} from './timeSeriesTools/timeSeriesScroll.js';

export {
  default as wwwcSynchronizer
} from './synchronization/wwwcSynchronizer.js';
export {
  default as updateImageSynchronizer
} from './synchronization/updateImageSynchronizer.js';
export { default as Synchronizer } from './synchronization/Synchronizer.js';
export {
  default as stackScrollSynchronizer
} from './synchronization/stackScrollSynchronizer.js';
export {
  default as stackImagePositionSynchronizer
} from './synchronization/stackImagePositionSynchronizer.js';
export {
  default as stackImagePositionOffsetSynchronizer
} from './synchronization/stackImagePositionOffsetSynchronizer.js';
export {
  default as stackImageIndexSynchronizer
} from './synchronization/stackImageIndexSynchronizer.js';
export {
  default as panZoomSynchronizer
} from './synchronization/panZoomSynchronizer.js';

export { default as toolStyle } from './stateManagement/toolStyle.js';
export {
  addToolState,
  getToolState,
  removeToolState,
  clearToolState,
  setElementToolStateManager,
  getElementToolStateManager
} from './stateManagement/toolState.js';
export {
  default as toolCoordinates
} from './stateManagement/toolCoordinates.js';
export { default as toolColors } from './stateManagement/toolColors.js';
export {
  addTimeSeriesStateManager,
  newTimeSeriesSpecificToolStateManager
} from './stateManagement/timeSeriesSpecificStateManager.js';
export { default as textStyle } from './stateManagement/textStyle.js';

export {
  stackSpecificStateManager,
  newStackSpecificToolStateManager,
  addStackStateManager
} from './stateManagement/stackSpecificStateManager.js';

export {
  default as loadHandlerManager
} from './stateManagement/loadHandlerManager.js';

export {
  newImageIdSpecificToolStateManager,
  globalImageIdSpecificToolStateManager
} from './stateManagement/imageIdSpecificStateManager.js';

export {
  newFrameOfReferenceSpecificToolStateManager,
  globalFrameOfReferenceSpecificToolStateManager
} from './stateManagement/frameOfReferenceStateManager.js';

export { default as appState } from './stateManagement/appState.js';

export {
  default as stackScrollKeyboard
} from './stackTools/stackScrollKeyboard.js';

export {
  stackScroll,
  stackScrollWheel,
  stackScrollTouchDrag,
  stackScrollMultiTouch
} from './stackTools/stackScroll.js';

export { default as stackPrefetch } from './stackTools/stackPrefetch.js';
export { default as scrollIndicator } from './stackTools/scrollIndicator.js';
export { default as stackRenderers } from './stackTools/stackRenderers.js';
export { playClip, stopClip } from './stackTools/playClip.js';

export {
  default as anyHandlesOutsideImage
} from './manipulators/anyHandlesOutsideImage.js';
export { default as drawHandles } from './manipulators/drawHandles.js';
export {
  default as getHandleNearImagePoint
} from './manipulators/getHandleNearImagePoint.js';
export { default as handleActivator } from './manipulators/handleActivator.js';
export { default as moveAllHandles } from './manipulators/moveAllHandles.js';
export { default as moveHandle } from './manipulators/moveHandle.js';
export { default as moveNewHandle } from './manipulators/moveNewHandle.js';
export {
  default as moveNewHandleTouch
} from './manipulators/moveNewHandleTouch.js';
export {
  default as touchMoveAllHandles
} from './manipulators/touchMoveAllHandles.js';
export { default as touchMoveHandle } from './manipulators/touchMoveHandle.js';

export { default as keyboardInput } from './inputSources/keyboardInput.js';
export { default as mouseInput } from './inputSources/mouseInput.js';
export { default as mouseWheelInput } from './inputSources/mouseWheelInput.js';
export {
  default as preventGhostClick
} from './inputSources/preventGhostClick.js';
export { default as touchInput } from './inputSources/touchInput.js';

export {
  arrowAnnotate,
  arrowAnnotateTouch
} from './imageTools/arrowAnnotate.js';
export { crosshairs, crosshairsTouch } from './imageTools/crosshairs.js';
export { default as displayTool } from './imageTools/displayTool.js';
export { default as doubleTapTool } from './imageTools/doubleTapTool.js';
export { default as doubleTapZoom } from './imageTools/doubleTapZoom.js';
export { dragProbe, dragProbeTouch } from './imageTools/dragProbe.js';

export {
  ellipticalRoi,
  ellipticalRoiTouch
} from './imageTools/ellipticalRoi.js';
export { eraser, eraserTouch } from './imageTools/eraser.js';
export { freehand } from './imageTools/freehand.js';
export { freehandSculpter } from './imageTools/freehandSculpter.js';

export { default as imageStats } from './imageTools/imageStats.js';
export { default as keyboardTool } from './imageTools/keyboardTool.js';
export { length, lengthTouch } from './imageTools/length.js';
export { magnify, magnifyTouchDrag } from './imageTools/magnify.js';

export { default as mouseButtonTool } from './imageTools/mouseButtonTool.js';
export { default as mouseWheelTool } from './imageTools/mouseWheelTool.js';
export {
  default as multiTouchDragTool
} from './imageTools/multiTouchDragTool.js';

export { pan, panTouchDrag } from './imageTools/pan.js';
export { default as panMultiTouch } from './imageTools/panMultiTouch.js';
export { probe, probeTouch } from './imageTools/probe.js';
export { rectangleRoi, rectangleRoiTouch } from './imageTools/rectangleRoi.js';
export { rotate, rotateTouchDrag } from './imageTools/rotate.js';
export { default as rotateTouch } from './imageTools/rotateTouch.js';
export { default as saveAs } from './imageTools/saveAs.js';
export { default as scaleOverlayTool } from './imageTools/scaleOverlayTool.js';
export { simpleAngle, simpleAngleTouch } from './imageTools/simpleAngle.js';
export {
  default as simpleMouseButtonTool
} from './imageTools/simpleMouseButtonTool.js';
export { textMarker, textMarkerTouch } from './imageTools/textMarker.js';

export { default as touchDragTool } from './imageTools/touchDragTool.js';
export { default as touchPinchTool } from './imageTools/touchPinchTool.js';
export { default as touchTool } from './imageTools/touchTool.js';
export { wwwc, wwwcTouchDrag } from './imageTools/wwwc.js';
export { wwwcRegion, wwwcRegionTouch } from './imageTools/wwwcRegion.js';
export {
  zoom,
  zoomWheel,
  zoomTouchPinch,
  zoomTouchDrag
} from './imageTools/zoom.js';
export { brush } from './paintingTools/brush.js';
export { adaptiveBrush } from './paintingTools/adaptiveBrush.js';
export { default as version } from './version.js';

export { setToolOptions, getToolOptions } from './toolOptions.js';
<commit_msg>Update API<commit_after>import * as drawing from './util/drawing.js';

// TEST
export { default as addTool } from './store/addTool.js';
export {
  default as mouseToolEventDispatcher
} from './eventDispatchers/mouseToolEventDispatcher.js';
export {
  default as touchToolEventDispatcher
} from './eventDispatchers/touchToolEventDispatcher.js';
export {
  default as lengthMouse
} from './new-api-tools-and-examples/length/lengthMouse.js';
export {
  default as lengthTouch
} from './new-api-tools-and-examples/length/lengthTouch.js';
export {
  default as angleMouse
} from './new-api-tools-and-examples/angleMouse.js';
export {
  default as wwwcMouse
} from './new-api-tools-and-examples/wwwcMouse.js';
export {
  setToolActive,
  setToolEnabled,
  setToolDisabled,
  setToolPassive
} from './store/setToolMode.js';
// END TEST

export { drawing };

export { default as external } from './externalModules.js';
export { default as EVENTS } from './events.js';

export { default as referenceLines } from './referenceLines/index.js';
export { default as orientation } from './orientation/index.js';

export {
  default as requestPoolManager
} from './requestPool/requestPoolManager.js';

export {
  default as setContextToDisplayFontSize
} from './util/setContextToDisplayFontSize.js';
export { default as scrollToIndex } from './util/scrollToIndex.js';
export { default as scroll } from './util/scroll.js';
export { default as roundToDecimal } from './util/roundToDecimal.js';
export {
  projectPatientPointToImagePlane,
  imagePointToPatientPoint,
  planePlaneIntersection
} from './util/pointProjector.js';

export {
  default as pointInsideBoundingBox
} from './util/pointInsideBoundingBox.js';
export { default as pointInEllipse } from './util/pointInEllipse.js';
export { default as makeUnselectable } from './util/makeUnselectable.js';
export {
  default as isMouseButtonEnabled
} from './util/isMouseButtonEnabled.js';
export { default as getRGBPixels } from './util/getRGBPixels.js';
export {
  getDefaultSimultaneousRequests,
  getMaxSimultaneousRequests,
  getBrowserInfo,
  isMobileDevice
} from './util/getMaxSimultaneousRequests.js';

export { default as getLuminance } from './util/getLuminance.js';
export { default as drawTextBox } from './util/drawTextBox.js';
export { default as drawEllipse } from './util/drawEllipse.js';
export { default as drawCircle } from './util/drawCircle.js';
export { default as drawArrow } from './util/drawArrow.js';
export { default as copyPoints } from './util/copyPoints.js';
export { default as calculateSUV } from './util/calculateSUV.js';
export {
  default as calculateEllipseStatistics
} from './util/calculateEllipseStatistics.js';

export { default as probeTool4D } from './timeSeriesTools/probeTool4D.js';
export {
  default as incrementTimePoint
} from './timeSeriesTools/incrementTimePoint.js';
export {
  default as timeSeriesPlayer
} from './timeSeriesTools/timeSeriesPlayer.js';
export {
  timeSeriesScroll,
  timeSeriesScrollWheel,
  timeSeriesScrollTouchDrag
} from './timeSeriesTools/timeSeriesScroll.js';

export {
  default as wwwcSynchronizer
} from './synchronization/wwwcSynchronizer.js';
export {
  default as updateImageSynchronizer
} from './synchronization/updateImageSynchronizer.js';
export { default as Synchronizer } from './synchronization/Synchronizer.js';
export {
  default as stackScrollSynchronizer
} from './synchronization/stackScrollSynchronizer.js';
export {
  default as stackImagePositionSynchronizer
} from './synchronization/stackImagePositionSynchronizer.js';
export {
  default as stackImagePositionOffsetSynchronizer
} from './synchronization/stackImagePositionOffsetSynchronizer.js';
export {
  default as stackImageIndexSynchronizer
} from './synchronization/stackImageIndexSynchronizer.js';
export {
  default as panZoomSynchronizer
} from './synchronization/panZoomSynchronizer.js';

export { default as toolStyle } from './stateManagement/toolStyle.js';
export {
  addToolState,
  getToolState,
  removeToolState,
  clearToolState,
  setElementToolStateManager,
  getElementToolStateManager
} from './stateManagement/toolState.js';
export {
  default as toolCoordinates
} from './stateManagement/toolCoordinates.js';
export { default as toolColors } from './stateManagement/toolColors.js';
export {
  addTimeSeriesStateManager,
  newTimeSeriesSpecificToolStateManager
} from './stateManagement/timeSeriesSpecificStateManager.js';
export { default as textStyle } from './stateManagement/textStyle.js';

export {
  stackSpecificStateManager,
  newStackSpecificToolStateManager,
  addStackStateManager
} from './stateManagement/stackSpecificStateManager.js';

export {
  default as loadHandlerManager
} from './stateManagement/loadHandlerManager.js';

export {
  newImageIdSpecificToolStateManager,
  globalImageIdSpecificToolStateManager
} from './stateManagement/imageIdSpecificStateManager.js';

export {
  newFrameOfReferenceSpecificToolStateManager,
  globalFrameOfReferenceSpecificToolStateManager
} from './stateManagement/frameOfReferenceStateManager.js';

export { default as appState } from './stateManagement/appState.js';

export {
  default as stackScrollKeyboard
} from './stackTools/stackScrollKeyboard.js';

export {
  stackScroll,
  stackScrollWheel,
  stackScrollTouchDrag,
  stackScrollMultiTouch
} from './stackTools/stackScroll.js';

export { default as stackPrefetch } from './stackTools/stackPrefetch.js';
export { default as scrollIndicator } from './stackTools/scrollIndicator.js';
export { default as stackRenderers } from './stackTools/stackRenderers.js';
export { playClip, stopClip } from './stackTools/playClip.js';

export {
  default as anyHandlesOutsideImage
} from './manipulators/anyHandlesOutsideImage.js';
export { default as drawHandles } from './manipulators/drawHandles.js';
export {
  default as getHandleNearImagePoint
} from './manipulators/getHandleNearImagePoint.js';
export { default as handleActivator } from './manipulators/handleActivator.js';
export { default as moveAllHandles } from './manipulators/moveAllHandles.js';
export { default as moveHandle } from './manipulators/moveHandle.js';
export { default as moveNewHandle } from './manipulators/moveNewHandle.js';
export {
  default as moveNewHandleTouch
} from './manipulators/moveNewHandleTouch.js';
export {
  default as touchMoveAllHandles
} from './manipulators/touchMoveAllHandles.js';
export { default as touchMoveHandle } from './manipulators/touchMoveHandle.js';

export { default as keyboardInput } from './inputSources/keyboardInput.js';
export { default as mouseInput } from './inputSources/mouseInput.js';
export { default as mouseWheelInput } from './inputSources/mouseWheelInput.js';
export {
  default as preventGhostClick
} from './inputSources/preventGhostClick.js';
export { default as touchInput } from './inputSources/touchInput.js';

export {
  arrowAnnotate,
  arrowAnnotateTouch
} from './imageTools/arrowAnnotate.js';
export { crosshairs, crosshairsTouch } from './imageTools/crosshairs.js';
export { default as displayTool } from './imageTools/displayTool.js';
export { default as doubleTapTool } from './imageTools/doubleTapTool.js';
export { default as doubleTapZoom } from './imageTools/doubleTapZoom.js';
export { dragProbe, dragProbeTouch } from './imageTools/dragProbe.js';

export {
  ellipticalRoi,
  ellipticalRoiTouch
} from './imageTools/ellipticalRoi.js';
export { eraser, eraserTouch } from './imageTools/eraser.js';
export { freehand } from './imageTools/freehand.js';
export { freehandSculpter } from './imageTools/freehandSculpter.js';

export { default as imageStats } from './imageTools/imageStats.js';
export { default as keyboardTool } from './imageTools/keyboardTool.js';
export { magnify, magnifyTouchDrag } from './imageTools/magnify.js';

export { default as mouseButtonTool } from './imageTools/mouseButtonTool.js';
export { default as mouseWheelTool } from './imageTools/mouseWheelTool.js';
export {
  default as multiTouchDragTool
} from './imageTools/multiTouchDragTool.js';

export { pan, panTouchDrag } from './imageTools/pan.js';
export { default as panMultiTouch } from './imageTools/panMultiTouch.js';
export { probe, probeTouch } from './imageTools/probe.js';
export { rectangleRoi, rectangleRoiTouch } from './imageTools/rectangleRoi.js';
export { rotate, rotateTouchDrag } from './imageTools/rotate.js';
export { default as rotateTouch } from './imageTools/rotateTouch.js';
export { default as saveAs } from './imageTools/saveAs.js';
export { default as scaleOverlayTool } from './imageTools/scaleOverlayTool.js';
export { simpleAngle, simpleAngleTouch } from './imageTools/simpleAngle.js';
export {
  default as simpleMouseButtonTool
} from './imageTools/simpleMouseButtonTool.js';
export { textMarker, textMarkerTouch } from './imageTools/textMarker.js';

export { default as touchDragTool } from './imageTools/touchDragTool.js';
export { default as touchPinchTool } from './imageTools/touchPinchTool.js';
export { default as touchTool } from './imageTools/touchTool.js';
export { wwwc, wwwcTouchDrag } from './imageTools/wwwc.js';
export { wwwcRegion, wwwcRegionTouch } from './imageTools/wwwcRegion.js';
export {
  zoom,
  zoomWheel,
  zoomTouchPinch,
  zoomTouchDrag
} from './imageTools/zoom.js';
export { brush } from './paintingTools/brush.js';
export { adaptiveBrush } from './paintingTools/adaptiveBrush.js';
export { default as version } from './version.js';

export { setToolOptions, getToolOptions } from './toolOptions.js';
<|endoftext|>


<commit_before>/**
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

import path from 'path';
import prettyBytes from 'pretty-bytes';
import sources from 'webpack-sources';
import { createDocument, serializeDocument } from './dom';
import { parseStylesheet, serializeStylesheet, walkStyleRules } from './css';
import { tap } from './util';

// Used to annotate this plugin's hooks in Tappable invocations
const PLUGIN_NAME = 'critters-webpack-plugin';

/**
 * The mechanism to use for lazy-loading stylesheets.
 * _[JS]_ indicates that a strategy requires JavaScript (falls back to `<noscript>`).
 *
 * - **default:** Move stylesheet links to the end of the document and insert preload meta tags in their place.
 * - **"body":** Move all external stylesheet links to the end of the document.
 * - **"media":** Load stylesheets asynchronously by adding `media="not x"` and removing once loaded. _[JS]_
 * - **"swap":** Convert stylesheet links to preloads that swap to `rel="stylesheet"` once loaded. _[JS]_
 * - **"js":** Inject an asynchronous CSS loader similar to [LoadCSS](https://github.com/filamentgroup/loadCSS) and use it to load stylesheets. _[JS]_
 * - **"js-lazy":** Like `"js"`, but the stylesheet is disabled until fully loaded.
 * @typedef {(default|'body'|'media'|'swap'|'js'|'js-lazy')} PreloadStrategy
 * @public
 */

/**
 * All optional. Pass them to `new Critters({ ... })`.
 * @public
 * @typedef {Object} Options
 * @property {Boolean} external     Inline styles from external stylesheets _(default: `true`)_
 * @property {String} preload       Which {@link PreloadStrategy preload strategy} to use
 * @property {Boolean} noscriptFallback Add `<noscript>` fallback to JS-based strategies
 * @property {Boolean} inlineFonts  Inline critical font-face rules _(default: `false`)_
 * @property {Boolean} preloadFonts Preloads critical fonts _(default: `true`)_
 * @property {Boolean} fonts        Shorthand for setting `inlineFonts`+`preloadFonts`
 *  - values:
 *  - `true` to inline critical font-face rules and preload the fonts
 *  - `false` to don't inline any font-face rules and don't preload fonts
 * @property {Boolean} compress     Compress resulting critical CSS _(default: `true`)_
 */

/**
 * Create a Critters plugin instance with the given options.
 * @public
 * @param {Options} options Options to control how Critters inlines CSS.
 * @example
 * // webpack.config.js
 * module.exports = {
 *   plugins: [
 *     new Critters({
 *       // Outputs: <link rel="preload" onload="this.rel='stylesheet'">
 *       preload: 'swap',
 *
 *       // Don't inline critical font-face rules, but preload the font URLs:
 *       preloadFonts: true
 *     })
 *   ]
 * }
 */
export default class Critters {
  /** @private */
  constructor (options) {
    this.options = options || {};
    this.urlFilter = this.options.filter;
    if (this.urlFilter instanceof RegExp) {
      this.urlFilter = this.urlFilter.test.bind(this.urlFilter);
    }
  }

  /**
   * Invoked by Webpack during plugin initialization
   */
  apply (compiler) {
    // hook into the compiler to get a Compilation instance...
    tap(compiler, 'compilation', PLUGIN_NAME, false, compilation => {
      // ... which is how we get an "after" hook into html-webpack-plugin's HTML generation.
      if (compilation.hooks.htmlWebpackPluginAfterHtmlProcessing) {
        tap(compilation, 'html-webpack-plugin-after-html-processing', PLUGIN_NAME, true, (htmlPluginData, callback) => {
          this.process(compiler, compilation, htmlPluginData.html)
            .then(html => { callback(null, { html }); })
            .catch(callback);
        });
      } else {
        // If html-webpack-plugin isn't used, process the first HTML asset as an optimize step
        tap(compilation, 'optimize-assets', PLUGIN_NAME, true, (assets, callback) => {
          let htmlAssetName;
          for (const name in assets) {
            if (name.match(/\.html$/)) {
              htmlAssetName = name;
              break;
            }
          }
          if (!htmlAssetName) return callback(Error('Could not find HTML asset.'));
          const html = assets[htmlAssetName].source();
          if (!html) return callback(Error('Empty HTML asset.'));

          this.process(compiler, compilation, String(html))
            .then(html => {
              assets[htmlAssetName] = new sources.RawSource(html);
              callback();
            })
            .catch(callback);
        });
      }
    });
  }

  /**
   * Read the contents of a file from Webpack's input filesystem
   */
  readFile (filename, encoding) {
    return new Promise((resolve, reject) => {
      this.fs.readFile(filename, encoding, (err, data) => {
        if (err) reject(err);
        else resolve(data);
      });
    });
  }

  /**
   * Apply critical CSS processing to html-webpack-plugin
   */
  async process (compiler, compilation, html) {
    const outputPath = compiler.options.output.path;

    // Parse the generated HTML in a DOM we can mutate
    const document = createDocument(html);

    // `external:false` skips processing of external sheets
    if (this.options.external !== false) {
      const externalSheets = [].slice.call(document.querySelectorAll('link[rel="stylesheet"]'));
      await Promise.all(externalSheets.map(
        link => this.embedLinkedStylesheet(link, compilation, outputPath)
      ));
    }

    // go through all the style tags in the document and reduce them to only critical CSS
    const styles = [].slice.call(document.querySelectorAll('style'));
    await Promise.all(styles.map(
      style => this.processStyle(style, document)
    ));

    // serialize the document back to HTML and we're done
    return serializeDocument(document);
  }

  /**
   * Inline the target stylesheet referred to by a <link rel="stylesheet"> (assuming it passes `options.filter`)
   */
  async embedLinkedStylesheet (link, compilation, outputPath) {
    const href = link.getAttribute('href');
    const media = link.getAttribute('media');
    const document = link.ownerDocument;

    const preloadMode = this.options.preload;

    // skip filtered resources, or network resources if no filter is provided
    if (this.urlFilter ? this.urlFilter(href) : href.match(/^(https?:)?\/\//)) return Promise.resolve();

    // path on disk
    const filename = path.resolve(outputPath, href.replace(/^\//, ''));

    // try to find a matching asset by filename in webpack's output (not yet written to disk)
    const asset = compilation.assets[path.relative(outputPath, filename).replace(/^\.\//, '')];

    // Attempt to read from assets, falling back to a disk read
    const sheet = asset ? asset.source() : await this.readFile(filename, 'utf8');

    // CSS loader is only injected for the first sheet, then this becomes an empty string
    let cssLoaderPreamble = `function $loadcss(u,m,l){(l=document.createElement('link')).rel='stylesheet';l.href=u;document.head.appendChild(l)}`;
    const lazy = preloadMode === 'js-lazy';
    if (lazy) {
      cssLoaderPreamble = cssLoaderPreamble.replace('l.href', `l.media='only x';l.onload=function(){l.media=m};l.href`);
    }

    // the reduced critical CSS gets injected into a new <style> tag
    const style = document.createElement('style');
    style.appendChild(document.createTextNode(sheet));
    link.parentNode.insertBefore(style, link.nextSibling);

    // drop a reference to the original URL onto the tag (used for reporting to console later)
    style.$$name = href;

    // Allow disabling any mutation of the stylesheet link:
    if (preloadMode === false) return;

    let noscriptFallback = false;

    if (preloadMode === 'body') {
      document.body.appendChild(link);
    } else {
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'style');
      if (preloadMode === 'js' || preloadMode === 'js-lazy') {
        const script = document.createElement('script');
        const js = `${cssLoaderPreamble}$loadcss(${JSON.stringify(href)}${lazy ? (',' + JSON.stringify(media || 'all')) : ''})`;
        script.appendChild(document.createTextNode(js));
        link.parentNode.insertBefore(script, link.nextSibling);
        cssLoaderPreamble = '';
        noscriptFallback = true;
      } else if (preloadMode === 'media') {
        // @see https://github.com/filamentgroup/loadCSS/blob/af1106cfe0bf70147e22185afa7ead96c01dec48/src/loadCSS.js#L26
        link.setAttribute('rel', 'stylesheet');
        link.removeAttribute('as');
        link.setAttribute('media', 'only x');
        link.setAttribute('onload', `this.media='${media || 'all'}'`);
        noscriptFallback = true;
      } else if (preloadMode === 'swap') {
        link.setAttribute('onload', "this.rel='stylesheet'");
        noscriptFallback = true;
      } else {
        const bodyLink = document.createElement('link');
        bodyLink.setAttribute('rel', 'stylesheet');
        if (media) bodyLink.setAttribute('media', media);
        bodyLink.setAttribute('href', href);
        document.body.appendChild(bodyLink);
      }
    }

    if (this.options.noscriptFallback !== false && noscriptFallback) {
      const noscript = document.createElement('noscript');
      const noscriptLink = document.createElement('link');
      noscriptLink.setAttribute('rel', 'stylesheet');
      noscriptLink.setAttribute('href', href);
      if (media) noscriptLink.setAttribute('media', media);
      noscript.appendChild(noscriptLink);
      link.parentNode.insertBefore(noscript, link.nextSibling);
    }
  }

  /**
   * Parse the stylesheet within a <style> element, then reduce it to contain only rules used by the document.
   */
  async processStyle (style) {
    const options = this.options;
    const document = style.ownerDocument;
    const head = document.querySelector('head');

    // basically `.textContent`
    let sheet = style.childNodes.length > 0 && [].map.call(style.childNodes, node => node.nodeValue).join('\n');

    // store a reference to the previous serialized stylesheet for reporting stats
    const before = sheet;

    // Skip empty stylesheets
    if (!sheet) return;

    const ast = parseStylesheet(sheet);

    // a string to search for font names (very loose)
    let criticalFonts = '';

    // Walk all CSS rules, transforming unused rules to comments (which get removed)
    walkStyleRules(ast, rule => {
      if (rule.type === 'rule') {
        // Filter the selector list down to only those matche
        rule.selectors = rule.selectors.filter(sel => {
          // Strip pseudo-elements and pseudo-classes, since we only care that their associated elements exist.
          // This means any selector for a pseudo-element or having a pseudo-class will be inlined if the rest of the selector matches.
          sel = sel.replace(/::?(?:[a-z-]+)([.[#~&^:*]|\s|\n|$)/gi, '$1').trim();
          try {
            return document.querySelector(sel, document) != null;
          } catch (e) {
            return null;
          }
        });
        // If there are no matched selectors, remove the rule:
        if (rule.selectors.length === 0) {
          return false;
        }

        if (rule.declarations) {
          for (let i = 0; i < rule.declarations.length; i++) {
            const decl = rule.declarations[i];
            if (decl.property.match(/\bfont\b/i)) {
              criticalFonts += ' ' + decl.value;
            }
          }
        }
      }

      // keep font rules, they're handled in the second pass:
      if (rule.type === 'font-face') return;

      // If there are no remaining rules, remove the whole rule:
      return !rule.rules || rule.rules.length !== 0;
    });

    const shouldPreloadFonts = options.fonts === true || options.preloadFonts === true;
    const shouldInlineFonts = options.fonts !== false || options.inlineFonts === true;

    const preloadedFonts = [];
    walkStyleRules(ast, rule => {
      // only process @font-face rules in the second pass
      if (rule.type !== 'font-face') return;

      let family, src;
      for (let i = 0; i < rule.declarations.length; i++) {
        const decl = rule.declarations[i];
        if (decl.property === 'src') {
          // @todo parse this properly and generate multiple preloads with type="font/woff2" etc
          src = (decl.value.match(/url\s*\(\s*(['"]?)(.+?)\1\s*\)/) || [])[2];
        } else if (decl.property === 'font-family') {
          family = decl.value;
        }
      }

      if (src && shouldPreloadFonts && preloadedFonts.indexOf(src) === -1) {
        preloadedFonts.push(src);
        const preload = document.createElement('link');
        preload.setAttribute('rel', 'preload');
        preload.setAttribute('as', 'font');
        if (src.match(/:\/\//)) {
          preload.setAttribute('crossorigin', 'anonymous');
        }
        preload.setAttribute('href', src.trim());
        head.appendChild(preload);
      }

      // if we're missing info, if the font is unused, or if critical font inlining is disabled, remove the rule:
      if (!family || !src || criticalFonts.indexOf(family) === -1 || !shouldInlineFonts) return false;
    });

    sheet = serializeStylesheet(ast, { compress: this.options.compress !== false });

    // If all rules were removed, get rid of the style element entirely
    if (sheet.trim().length === 0) {
      if (style.parentNode) {
        style.parentNode.removeChild(style);
      }
    } else {
      // replace the inline stylesheet with its critical'd counterpart
      while (style.lastChild) {
        style.removeChild(style.lastChild);
      }
      style.appendChild(document.createTextNode(sheet));
    }

    // output some stats
    const name = style.$$name ? style.$$name.replace(/^\//, '') : 'inline CSS';
    const percent = sheet.length / before.length * 100 | 0;
    console.log('\u001b[32mCritters: inlined ' + prettyBytes(sheet.length) + ' (' + percent + '% of original ' + prettyBytes(before.length) + ') of ' + name + '.\u001b[39m');
  }
}

<commit_msg>Add logging for failed CSS selectors<commit_after>/**
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

import path from 'path';
import prettyBytes from 'pretty-bytes';
import sources from 'webpack-sources';
import { createDocument, serializeDocument } from './dom';
import { parseStylesheet, serializeStylesheet, walkStyleRules } from './css';
import { tap } from './util';

// Used to annotate this plugin's hooks in Tappable invocations
const PLUGIN_NAME = 'critters-webpack-plugin';

/**
 * The mechanism to use for lazy-loading stylesheets.
 * _[JS]_ indicates that a strategy requires JavaScript (falls back to `<noscript>`).
 *
 * - **default:** Move stylesheet links to the end of the document and insert preload meta tags in their place.
 * - **"body":** Move all external stylesheet links to the end of the document.
 * - **"media":** Load stylesheets asynchronously by adding `media="not x"` and removing once loaded. _[JS]_
 * - **"swap":** Convert stylesheet links to preloads that swap to `rel="stylesheet"` once loaded. _[JS]_
 * - **"js":** Inject an asynchronous CSS loader similar to [LoadCSS](https://github.com/filamentgroup/loadCSS) and use it to load stylesheets. _[JS]_
 * - **"js-lazy":** Like `"js"`, but the stylesheet is disabled until fully loaded.
 * @typedef {(default|'body'|'media'|'swap'|'js'|'js-lazy')} PreloadStrategy
 * @public
 */

/**
 * All optional. Pass them to `new Critters({ ... })`.
 * @public
 * @typedef {Object} Options
 * @property {Boolean} external     Inline styles from external stylesheets _(default: `true`)_
 * @property {String} preload       Which {@link PreloadStrategy preload strategy} to use
 * @property {Boolean} noscriptFallback Add `<noscript>` fallback to JS-based strategies
 * @property {Boolean} inlineFonts  Inline critical font-face rules _(default: `false`)_
 * @property {Boolean} preloadFonts Preloads critical fonts _(default: `true`)_
 * @property {Boolean} fonts        Shorthand for setting `inlineFonts`+`preloadFonts`
 *  - values:
 *  - `true` to inline critical font-face rules and preload the fonts
 *  - `false` to don't inline any font-face rules and don't preload fonts
 * @property {Boolean} compress     Compress resulting critical CSS _(default: `true`)_
 */

/**
 * Create a Critters plugin instance with the given options.
 * @public
 * @param {Options} options Options to control how Critters inlines CSS.
 * @example
 * // webpack.config.js
 * module.exports = {
 *   plugins: [
 *     new Critters({
 *       // Outputs: <link rel="preload" onload="this.rel='stylesheet'">
 *       preload: 'swap',
 *
 *       // Don't inline critical font-face rules, but preload the font URLs:
 *       preloadFonts: true
 *     })
 *   ]
 * }
 */
export default class Critters {
  /** @private */
  constructor (options) {
    this.options = options || {};
    this.urlFilter = this.options.filter;
    if (this.urlFilter instanceof RegExp) {
      this.urlFilter = this.urlFilter.test.bind(this.urlFilter);
    }
  }

  /**
   * Invoked by Webpack during plugin initialization
   */
  apply (compiler) {
    // hook into the compiler to get a Compilation instance...
    tap(compiler, 'compilation', PLUGIN_NAME, false, compilation => {
      // ... which is how we get an "after" hook into html-webpack-plugin's HTML generation.
      if (compilation.hooks.htmlWebpackPluginAfterHtmlProcessing) {
        tap(compilation, 'html-webpack-plugin-after-html-processing', PLUGIN_NAME, true, (htmlPluginData, callback) => {
          this.process(compiler, compilation, htmlPluginData.html)
            .then(html => { callback(null, { html }); })
            .catch(callback);
        });
      } else {
        // If html-webpack-plugin isn't used, process the first HTML asset as an optimize step
        tap(compilation, 'optimize-assets', PLUGIN_NAME, true, (assets, callback) => {
          let htmlAssetName;
          for (const name in assets) {
            if (name.match(/\.html$/)) {
              htmlAssetName = name;
              break;
            }
          }
          if (!htmlAssetName) return callback(Error('Could not find HTML asset.'));
          const html = assets[htmlAssetName].source();
          if (!html) return callback(Error('Empty HTML asset.'));

          this.process(compiler, compilation, String(html))
            .then(html => {
              assets[htmlAssetName] = new sources.RawSource(html);
              callback();
            })
            .catch(callback);
        });
      }
    });
  }

  /**
   * Read the contents of a file from Webpack's input filesystem
   */
  readFile (filename, encoding) {
    return new Promise((resolve, reject) => {
      this.fs.readFile(filename, encoding, (err, data) => {
        if (err) reject(err);
        else resolve(data);
      });
    });
  }

  /**
   * Apply critical CSS processing to html-webpack-plugin
   */
  async process (compiler, compilation, html) {
    const outputPath = compiler.options.output.path;

    // Parse the generated HTML in a DOM we can mutate
    const document = createDocument(html);

    // `external:false` skips processing of external sheets
    if (this.options.external !== false) {
      const externalSheets = [].slice.call(document.querySelectorAll('link[rel="stylesheet"]'));
      await Promise.all(externalSheets.map(
        link => this.embedLinkedStylesheet(link, compilation, outputPath)
      ));
    }

    // go through all the style tags in the document and reduce them to only critical CSS
    const styles = [].slice.call(document.querySelectorAll('style'));
    await Promise.all(styles.map(
      style => this.processStyle(style, document)
    ));

    // serialize the document back to HTML and we're done
    return serializeDocument(document);
  }

  /**
   * Inline the target stylesheet referred to by a <link rel="stylesheet"> (assuming it passes `options.filter`)
   */
  async embedLinkedStylesheet (link, compilation, outputPath) {
    const href = link.getAttribute('href');
    const media = link.getAttribute('media');
    const document = link.ownerDocument;

    const preloadMode = this.options.preload;

    // skip filtered resources, or network resources if no filter is provided
    if (this.urlFilter ? this.urlFilter(href) : href.match(/^(https?:)?\/\//)) return Promise.resolve();

    // path on disk
    const filename = path.resolve(outputPath, href.replace(/^\//, ''));

    // try to find a matching asset by filename in webpack's output (not yet written to disk)
    const asset = compilation.assets[path.relative(outputPath, filename).replace(/^\.\//, '')];

    // Attempt to read from assets, falling back to a disk read
    const sheet = asset ? asset.source() : await this.readFile(filename, 'utf8');

    // CSS loader is only injected for the first sheet, then this becomes an empty string
    let cssLoaderPreamble = `function $loadcss(u,m,l){(l=document.createElement('link')).rel='stylesheet';l.href=u;document.head.appendChild(l)}`;
    const lazy = preloadMode === 'js-lazy';
    if (lazy) {
      cssLoaderPreamble = cssLoaderPreamble.replace('l.href', `l.media='only x';l.onload=function(){l.media=m};l.href`);
    }

    // the reduced critical CSS gets injected into a new <style> tag
    const style = document.createElement('style');
    style.appendChild(document.createTextNode(sheet));
    link.parentNode.insertBefore(style, link.nextSibling);

    // drop a reference to the original URL onto the tag (used for reporting to console later)
    style.$$name = href;

    // Allow disabling any mutation of the stylesheet link:
    if (preloadMode === false) return;

    let noscriptFallback = false;

    if (preloadMode === 'body') {
      document.body.appendChild(link);
    } else {
      link.setAttribute('rel', 'preload');
      link.setAttribute('as', 'style');
      if (preloadMode === 'js' || preloadMode === 'js-lazy') {
        const script = document.createElement('script');
        const js = `${cssLoaderPreamble}$loadcss(${JSON.stringify(href)}${lazy ? (',' + JSON.stringify(media || 'all')) : ''})`;
        script.appendChild(document.createTextNode(js));
        link.parentNode.insertBefore(script, link.nextSibling);
        cssLoaderPreamble = '';
        noscriptFallback = true;
      } else if (preloadMode === 'media') {
        // @see https://github.com/filamentgroup/loadCSS/blob/af1106cfe0bf70147e22185afa7ead96c01dec48/src/loadCSS.js#L26
        link.setAttribute('rel', 'stylesheet');
        link.removeAttribute('as');
        link.setAttribute('media', 'only x');
        link.setAttribute('onload', `this.media='${media || 'all'}'`);
        noscriptFallback = true;
      } else if (preloadMode === 'swap') {
        link.setAttribute('onload', "this.rel='stylesheet'");
        noscriptFallback = true;
      } else {
        const bodyLink = document.createElement('link');
        bodyLink.setAttribute('rel', 'stylesheet');
        if (media) bodyLink.setAttribute('media', media);
        bodyLink.setAttribute('href', href);
        document.body.appendChild(bodyLink);
      }
    }

    if (this.options.noscriptFallback !== false && noscriptFallback) {
      const noscript = document.createElement('noscript');
      const noscriptLink = document.createElement('link');
      noscriptLink.setAttribute('rel', 'stylesheet');
      noscriptLink.setAttribute('href', href);
      if (media) noscriptLink.setAttribute('media', media);
      noscript.appendChild(noscriptLink);
      link.parentNode.insertBefore(noscript, link.nextSibling);
    }
  }

  /**
   * Parse the stylesheet within a <style> element, then reduce it to contain only rules used by the document.
   */
  async processStyle (style) {
    const options = this.options;
    const document = style.ownerDocument;
    const head = document.querySelector('head');

    // basically `.textContent`
    let sheet = style.childNodes.length > 0 && [].map.call(style.childNodes, node => node.nodeValue).join('\n');

    // store a reference to the previous serialized stylesheet for reporting stats
    const before = sheet;

    // Skip empty stylesheets
    if (!sheet) return;

    const ast = parseStylesheet(sheet);

    // a string to search for font names (very loose)
    let criticalFonts = '';

    const failedSelectors = [];

    // Walk all CSS rules, transforming unused rules to comments (which get removed)
    walkStyleRules(ast, rule => {
      if (rule.type === 'rule') {
        // Filter the selector list down to only those matche
        rule.selectors = rule.selectors.filter(sel => {
          // Strip pseudo-elements and pseudo-classes, since we only care that their associated elements exist.
          // This means any selector for a pseudo-element or having a pseudo-class will be inlined if the rest of the selector matches.
          sel = sel.replace(/::?(?:[a-z-]+)([.[#~&^:*]|\s|\n|$)/gi, '$1').trim();
          try {
            return document.querySelector(sel) != null;
          } catch (e) {
            failedSelectors.push(sel + ' -> ' + e.message);
            return null;
          }
        });
        // If there are no matched selectors, remove the rule:
        if (rule.selectors.length === 0) {
          return false;
        }

        if (rule.declarations) {
          for (let i = 0; i < rule.declarations.length; i++) {
            const decl = rule.declarations[i];
            if (decl.property.match(/\bfont\b/i)) {
              criticalFonts += ' ' + decl.value;
            }
          }
        }
      }

      // keep font rules, they're handled in the second pass:
      if (rule.type === 'font-face') return;

      // If there are no remaining rules, remove the whole rule:
      return !rule.rules || rule.rules.length !== 0;
    });

    if (failedSelectors.length !== 0) {
      console.warn(
        `${failedSelectors.length} rules skipped due to selector errors:\n  `+
        failedSelectors.join('\n  ')
      );
    }

    const shouldPreloadFonts = options.fonts === true || options.preloadFonts === true;
    const shouldInlineFonts = options.fonts !== false || options.inlineFonts === true;

    const preloadedFonts = [];
    walkStyleRules(ast, rule => {
      // only process @font-face rules in the second pass
      if (rule.type !== 'font-face') return;

      let family, src;
      for (let i = 0; i < rule.declarations.length; i++) {
        const decl = rule.declarations[i];
        if (decl.property === 'src') {
          // @todo parse this properly and generate multiple preloads with type="font/woff2" etc
          src = (decl.value.match(/url\s*\(\s*(['"]?)(.+?)\1\s*\)/) || [])[2];
        } else if (decl.property === 'font-family') {
          family = decl.value;
        }
      }

      if (src && shouldPreloadFonts && preloadedFonts.indexOf(src) === -1) {
        preloadedFonts.push(src);
        const preload = document.createElement('link');
        preload.setAttribute('rel', 'preload');
        preload.setAttribute('as', 'font');
        if (src.match(/:\/\//)) {
          preload.setAttribute('crossorigin', 'anonymous');
        }
        preload.setAttribute('href', src.trim());
        head.appendChild(preload);
      }

      // if we're missing info, if the font is unused, or if critical font inlining is disabled, remove the rule:
      if (!family || !src || criticalFonts.indexOf(family) === -1 || !shouldInlineFonts) return false;
    });

    sheet = serializeStylesheet(ast, { compress: this.options.compress !== false });

    // If all rules were removed, get rid of the style element entirely
    if (sheet.trim().length === 0) {
      if (style.parentNode) {
        style.parentNode.removeChild(style);
      }
    } else {
      // replace the inline stylesheet with its critical'd counterpart
      while (style.lastChild) {
        style.removeChild(style.lastChild);
      }
      style.appendChild(document.createTextNode(sheet));
    }

    // output some stats
    const name = style.$$name ? style.$$name.replace(/^\//, '') : 'inline CSS';
    const percent = sheet.length / before.length * 100 | 0;
    console.log('\u001b[32mCritters: inlined ' + prettyBytes(sheet.length) + ' (' + percent + '% of original ' + prettyBytes(before.length) + ') of ' + name + '.\u001b[39m');
  }
}

<|endoftext|>


<commit_before>/* global require, module, Buffer */

var through = require('through2');
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var csv = require('./csv');

var readFile = function (filePath, pathCache) {
    'use strict';
    if (_.contains(pathCache, filePath)) {
        return '<!-- CIRCULAR INCLUDE REFERENCE: ' + filePath + ' -->';
    }
    if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
        return '<!-- INCLUDE FILE NOT FOUND: ' + filePath + ' -->';
    }
    pathCache.push(filePath);
    return fs.readFileSync(filePath, 'utf8');
};

var csvTable = function(csvText) {
    var data = '';
    var result = '';
    var headline = true;
    var row;

    data = csv(csvText);

    // cleanup data: ignore empty lines, ignore # comments
    data = _.filter(data, function(row) {
            return row.length > 0 &&
                (row.length > 1 || row[0].trim().length > 0) &&
                row[0].trim()[0] != '#';
    });

    var escapeCellValue = function(value) {
        return value.
            replace('|', '|').
            replace('<', '<').
            replace('>', '>').
            replace('$', '\\$');
    };

    var formatRow = function(rowData, headline) {
        var r = '|';
        var v;
        for (var i = 0; i < rowData.length; i++) {
            v = escapeCellValue(rowData[i]);
            r += ' ' + v + ' |';
        };
        r += '\n';
        if (headline) {
            r += '|';
            for (var i = 0; i < rowData.length; i++) {
                v = escapeCellValue(rowData[i]);
                r += (new Array(v.length + 3).join('-')) + '|';
            }
            r += '\n';
        }
        return r;
    };

    for (var i = 0; i < data.length; i++) {
        row = data[i];
        result += formatRow(row, headline);
        headline = false;
    };

    // append empty row if table is empty
    if (data.length == 1) {
        row = new Array(data[0].length);
        for (var i = 0; i < row.length; i++) {
            row[i] = '';
        };
        result += formatRow(row, false);
    }
    return result;
};

var transformText = function (text, referencePath, pathCache) {
    'use strict';
    pathCache = pathCache || [];
    text = text.replace(
        /<!--\s+#include\s+(.+?)\s+-->/g,
        function (m, filePath) {
            var branchCache = _.clone(pathCache);
            var absPath = path.resolve(referencePath, filePath);
            var includeContent = readFile(absPath, branchCache);
            return transformText(includeContent, path.dirname(absPath), branchCache);
        });
    text = text.replace(
        /<!--\s+#csv\s+(.+?)\s+-->/g,
        function (m, filePath) {
            var absPath = path.resolve(referencePath, filePath);
            var csvContent = readFile(absPath, []);
            return csvTable(csvContent);
        });
    return text;
};

var transformBuffer = function (buffer, referencePath) {
    'use strict';
    return new Buffer(
        transformText(
            buffer.toString('utf8'),
            referencePath),
        'utf8');
};

var transformFile = function (filePath) {
    'use strict';
    if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
        throw 'File not found.';
    }
    var referencePath = path.dirname(filePath);
    var text = fs.readFileSync(filePath, 'utf8');
    return transformText(text, referencePath);
};

var processIncludes = function (fileOrReferencePath, text) {
    'use strict';

    if (fileOrReferencePath) {
        if (typeof(fileOrReferencePath) === 'string') {
            if (text) {
                if (typeof(text) === 'string') {
                    // processIncludes(referencePath, text) -> returns the processed text
                    return transformText(text, fileOrReferencePath);
                } else {
                    throw 'Invalid second argument';
                }
            } else {
                // processIncludes(filePath) -> returns the processed content of the file
                return transformFile(fileOrReferencePath);
            }
        } else {
            throw 'Invalid first argument.';
        }
    }

    // processIncludes() -> gulp transformation step
    return through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            this.push(file);
            cb();
            return;
        }
        if (file.isBuffer()) {
            file.contents = transformBuffer(file.contents, path.dirname(file.path));
            this.push(file);
            cb();
            return;
        }
        if (file.isStream()) {
            throw 'Streams are not supported.';
        }
    });
};

module.exports = processIncludes;
<commit_msg>code cleanup<commit_after>/* global require, module, Buffer */

var through = require('through2');
var _ = require('lodash');
var path = require('path');
var fs = require('fs');
var csv = require('./csv');

var readFile = function (filePath, pathCache) {
    'use strict';
    if (_.contains(pathCache, filePath)) {
        return '<!-- CIRCULAR INCLUDE REFERENCE: ' + filePath + ' -->';
    }
    if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
        return '<!-- INCLUDE FILE NOT FOUND: ' + filePath + ' -->';
    }
    pathCache.push(filePath);
    return fs.readFileSync(filePath, 'utf8');
};

var csvTable = function(csvText) {
    var data = '';
    var result = '';
    var headline = true;
    var row;
    var i;

    data = csv(csvText);

    // cleanup data: ignore empty lines, ignore # comments
    data = _.filter(data, function(row) {
            return row.length > 0 &&
                (row.length > 1 || row[0].trim().length > 0) &&
                row[0].trim()[0] != '#';
    });

    var escapeCellValue = function(value) {
        return value.
            replace('|', '|').
            replace('<', '<').
            replace('>', '>').
            replace('$', '\\$');
    };

    var formatRow = function(rowData, headline) {
        var r = '|';
        var v;
        var i;
        for (i = 0; i < rowData.length; i++) {
            v = escapeCellValue(rowData[i]);
            r += ' ' + v + ' |';
        }
        r += '\n';
        if (headline) {
            r += '|';
            for (i = 0; i < rowData.length; i++) {
                v = escapeCellValue(rowData[i]);
                r += (new Array(v.length + 3).join('-')) + '|';
            }
            r += '\n';
        }
        return r;
    };

    for (i = 0; i < data.length; i++) {
        row = data[i];
        result += formatRow(row, headline);
        headline = false;
    }

    // append empty row if table is empty
    if (data.length == 1) {
        row = new Array(data[0].length);
        for (i = 0; i < row.length; i++) {
            row[i] = '';
        }
        result += formatRow(row, false);
    }
    return result;
};

var transformText = function (text, referencePath, pathCache) {
    'use strict';
    pathCache = pathCache || [];
    text = text.replace(
        /<!--\s+#include\s+(.+?)\s+-->/g,
        function (m, filePath) {
            var branchCache = _.clone(pathCache);
            var absPath = path.resolve(referencePath, filePath);
            var includeContent = readFile(absPath, branchCache);
            return transformText(includeContent, path.dirname(absPath), branchCache);
        });
    text = text.replace(
        /<!--\s+#csv\s+(.+?)\s+-->/g,
        function (m, filePath) {
            var absPath = path.resolve(referencePath, filePath);
            var csvContent = readFile(absPath, []);
            return csvTable(csvContent);
        });
    return text;
};

var transformBuffer = function (buffer, referencePath) {
    'use strict';
    return new Buffer(
        transformText(
            buffer.toString('utf8'),
            referencePath),
        'utf8');
};

var transformFile = function (filePath) {
    'use strict';
    if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) {
        throw 'File not found.';
    }
    var referencePath = path.dirname(filePath);
    var text = fs.readFileSync(filePath, 'utf8');
    return transformText(text, referencePath);
};

var processIncludes = function (fileOrReferencePath, text) {
    'use strict';

    if (fileOrReferencePath) {
        if (typeof(fileOrReferencePath) === 'string') {
            if (text) {
                if (typeof(text) === 'string') {
                    // processIncludes(referencePath, text) -> returns the processed text
                    return transformText(text, fileOrReferencePath);
                } else {
                    throw 'Invalid second argument';
                }
            } else {
                // processIncludes(filePath) -> returns the processed content of the file
                return transformFile(fileOrReferencePath);
            }
        } else {
            throw 'Invalid first argument.';
        }
    }

    // processIncludes() -> gulp transformation step
    return through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            this.push(file);
            cb();
            return;
        }
        if (file.isBuffer()) {
            file.contents = transformBuffer(file.contents, path.dirname(file.path));
            this.push(file);
            cb();
            return;
        }
        if (file.isStream()) {
            throw 'Streams are not supported.';
        }
    });
};

module.exports = processIncludes;
<|endoftext|>


<commit_before>"use strict";
//@ts-check

/** @type {HTMLCanvasElement} */
/*
 * adapted from http://mrdoob.com/lab/javascript/webgl/glsl/02/ by MrDoob
 * adapted from answer for http://stackoverflow.com/questions/4638317
 */

var oldColor;
var newColor;
var colorGradient;

var oldOffsetCenter;
var newOffsetCenter;
var offsetGradient;

var rewardText;
var eventTimeout = Date.now() + 5000;
var emptyTimeout = 0.;

var script = [];
/*
var parameters = {
	time: 0,
	screenWidth: 0,
	screenHeight: 0,
    direction: 1,
    rotation: 1,
    branchCount: 4,
    speedFactor: 1,
    offsetCenter: 0.,
    colors: { fg: {}, bg: {}, dim: {}, pulse: {} },
};
*/

var loopData;

init(getConfigFromURL())
.then(function(data) {
	loopData = data;
	function loopFrame() {
		loopData.parameters.time += TIME_INTERVAL * loopData.parameters.speedFactor;
		loop(loopData.data.currentProgram, loopData.data.locations, loopData.parameters);
		captureCanvas(canvas);
	}
	setInterval(loopFrame, TIME_INTERVAL);
});

function getCanvasData(fileName, canvas, parameters) {
	return Promise.all([
		fetch("shaders/spiral.vs").then(function (r) { return r.text() }),
		fetch("shaders/" + fileName + ".fs").then(function (r) { return r.text() }),
	])
	.then(function (/** @type [string, string] */fetchedText) {
		return {
			data: initWebGL(canvas, fetchedText[0], fetchedText[1]),
			parameters: parameters,
		};
	});
}

function largeInterface() {
    const params = new URLSearchParams(location.search);
	params.delete("canvas");
	window.location.href = "" + location.pathname + "?" + params;
}

function smallInterface() {
    const params = new URLSearchParams(location.search);
	params.set("canvas", "small");
	window.location.href = "" + location.pathname + "?" + params;
}

function fullScreen() {
    const params = new URLSearchParams(location.search);
	params.delete("interface");
	params.set("canvas", "big");
	window.location.href = "" + location.pathname + "?" + params;
}

function init(config) {
	// Spiral parameters to use and/or change.
	var parameters = {
		time: 0,
		screenWidth: 0,
		screenHeight: 0,
		direction: config.direction,
		rotation: config.rotation,
		branchCount: config.branchCount,
		colors: config.colors,
		speedFactor: config.speedFactor,
	};

	// These are the URI parameters.
	createCapturer(config.exportName || undefined);
	// Hide the export button if needed
	if(config.interface) {
		// Shaders
		document.getElementById("shaders-list").innerHTML = shadersList
			.map(function(s) { return "<option" + (s === config.shaderFileName ? " selected=\"selected\"" : "") + ">" + s + "</option>"; })
			.join("");
		document.getElementById("shaders-list").addEventListener("change", function(ev) {
			var shaderName = document.getElementById("shaders-list").value;
			setURLParameter("file", shaderName);
			getCanvasData(shaderName, canvas, parameters)
			.then(function(data) {
				loopData = data;
			});
		});
		// Base Parameters
		document.getElementById("branch-count").value = config.branchCount;
		document.getElementById("branch-count").addEventListener("change", function(ev) {
			var branchCount = +(document.getElementById("branch-count").value);
			if(branchCount > 0) { setURLParameter("branch", branchCount); parameters.branchCount = branchCount; }
		});
		document.getElementById("counterclockwise").checked = config.rotation === -1;
		document.getElementById("counterclockwise").addEventListener("change", function(ev) {
			var checked = document.getElementById("counterclockwise").checked;
			addOrRemoveParameter("counterclockwise", checked);
			parameters.rotation = checked ? -1 : 1;
		});
		document.getElementById("inwards").value = config.direction === -1;
		document.getElementById("inwards").addEventListener("change", function(ev) {
			const checked = document.getElementById("inwards").checked;
			addOrRemoveParameter("inwards", checked);
			parameters.direction = checked ? 1 : -1;
		});
		// Colors parameters
		document.getElementById("bg-color").value = rgbToHex(config.colors.bg);
		document.getElementById("bg-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("bg-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("bg", value); parameters.colors.bg = color; }
		});
		document.getElementById("fg-color").value = rgbToHex(config.colors.fg);
		document.getElementById("fg-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("fg-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("fg", value); parameters.colors.fg = color; }
		});
		document.getElementById("pulse-color").value = rgbToHex(config.colors.pulse);
		document.getElementById("pulse-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("pulse-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("pulse", value); parameters.colors.pulse = color; }
		});
		document.getElementById("dim-color").value = rgbToHex(config.colors.dim);
		document.getElementById("dim-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("dim-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("dim", value); parameters.colors.dim = color; }
		});
		console.log(rgbToHex(config.colors.pulse));
		// Display the area
		document.getElementById("interface").style.display = "";
		document.getElementById("test").classList.add("hasInterface");
	}

	var	canvas = document.getElementById('canvas');

	return getCanvasData(config.shaderFileName, canvas, parameters)
	.then(function(data) {
		// Setup program size.
		onWindowResize(config, canvas, data.parameters);
		window.addEventListener("resize", function(ev) { onWindowResize(config, canvas, data.parameters); }, false);
		return data;
	});
}

function gradient(color1, color2, value) {
	return {
		r: color1.r * (1 - value) + color2.r * (value),
		g: color1.g * (1 - value) + color2.g * (value),
		b: color1.b * (1 - value) + color2.b * (value),
	}
}

function onWindowResize(config, canvas, parameters) {
	if(config.canvasSize === "small") {
		canvas.width = 400;
		canvas.height = 300;
	}
	else if(config.canvasSize === "interface") {
		var width = 2 * window.innerWidth / 3;
		var height = 3 * width / 4;
		if(height > window.innerHeight) {
			width = 4 * window.innerHeight / 3;
			height = window.innerHeight;
		}
		canvas.width = width;
		canvas.height = height;
	}
	else if(config.canvasSize === "screen") {
		canvas.width = 1280;
		canvas.height = 720;
	}
	else if(config.canvasSize === "1080") {
		canvas.width = 1920;
		canvas.height = 1080;
	}
	else if(config.canvasSize === "16-9") {
		canvas.width = 1600;
		canvas.height = 900;
	}
	else {
		canvas.width = window.innerWidth;
		canvas.height = window.innerHeight;
	}
	parameters.screenWidth = canvas.width;
	parameters.screenHeight = canvas.height;
	parameters.aspectX = canvas.width/canvas.height;
	parameters.aspectY = 1.0;
	gl.viewport(0, 0, canvas.width, canvas.height);
}

function loop(/**@type {WebGLProgram}*/program, /**@type {Locations}*/locations, parameters) {
	if (!program) return;
	// Clear the screen. Not necessary so far since we draw everywhere on it.
	// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

	if(colorGradient != null) {
		colorGradient += 0.02;
		parameters.colors.fg = gradient(oldColor, newColor, colorGradient);
		if(colorGradient >= 1.0)
			colorGradient = null;
	}

	if(offsetGradient != null) {
		offsetGradient += 0.01;
		parameters.offsetCenter = oldOffsetCenter * (1 - offsetGradient) + newOffsetCenter * (offsetGradient);
		if(offsetGradient >= 1.0)
			offsetGradient = null;
	}

	if(script[0] && (Date.now() > eventTimeout)) {
		document.getElementById("screenText").style.opacity = 1;
		document.getElementById("screenText").innerHTML = script[0].waitText;
	}
	else if(emptyTimeout && (Date.now() > emptyTimeout)) {
		document.getElementById("screenText").style.opacity = 0;
		emptyTimeout = null;
	}

	renderCanvas(program, locations, parameters);
}
<commit_msg>Fixed mistake with missing parameter.<commit_after>"use strict";
//@ts-check

/** @type {HTMLCanvasElement} */
/*
 * adapted from http://mrdoob.com/lab/javascript/webgl/glsl/02/ by MrDoob
 * adapted from answer for http://stackoverflow.com/questions/4638317
 */

var oldColor;
var newColor;
var colorGradient;

var oldOffsetCenter;
var newOffsetCenter;
var offsetGradient;

var rewardText;
var eventTimeout = Date.now() + 5000;
var emptyTimeout = 0.;

var script = [];
/*
var parameters = {
	time: 0,
	screenWidth: 0,
	screenHeight: 0,
    direction: 1,
    rotation: 1,
    branchCount: 4,
    speedFactor: 1,
    offsetCenter: 0.,
    colors: { fg: {}, bg: {}, dim: {}, pulse: {} },
};
*/

var loopData;

init(getConfigFromURL())
.then(function(data) {
	loopData = data;
	function loopFrame() {
		loopData.parameters.time += TIME_INTERVAL * loopData.parameters.speedFactor;
		loop(loopData.data.currentProgram, loopData.data.locations, loopData.parameters);
		captureCanvas(canvas);
	}
	setInterval(loopFrame, TIME_INTERVAL);
});

function getCanvasData(fileName, canvas, parameters) {
	return Promise.all([
		fetch("shaders/spiral.vs").then(function (r) { return r.text() }),
		fetch("shaders/" + fileName + ".fs").then(function (r) { return r.text() }),
	])
	.then(function (/** @type [string, string] */fetchedText) {
		return {
			data: initWebGL(canvas, fetchedText[0], fetchedText[1]),
			parameters: parameters,
		};
	});
}

function largeInterface() {
    const params = new URLSearchParams(location.search);
	params.delete("canvas");
	window.location.href = "" + location.pathname + "?" + params;
}

function smallInterface() {
    const params = new URLSearchParams(location.search);
	params.set("canvas", "small");
	window.location.href = "" + location.pathname + "?" + params;
}

function fullScreen() {
    const params = new URLSearchParams(location.search);
	params.delete("interface");
	params.set("canvas", "big");
	window.location.href = "" + location.pathname + "?" + params;
}

function init(config) {
	// Spiral parameters to use and/or change.
	var parameters = {
		time: 0,
		screenWidth: 0,
		screenHeight: 0,
		direction: config.direction,
		rotation: config.rotation,
		branchCount: config.branchCount,
		colors: config.colors,
		speedFactor: config.speedFactor,
		offsetCenter: 1.0,
	};

	// These are the URI parameters.
	createCapturer(config.exportName || undefined);
	// Hide the export button if needed
	if(config.interface) {
		// Shaders
		document.getElementById("shaders-list").innerHTML = shadersList
			.map(function(s) { return "<option" + (s === config.shaderFileName ? " selected=\"selected\"" : "") + ">" + s + "</option>"; })
			.join("");
		document.getElementById("shaders-list").addEventListener("change", function(ev) {
			var shaderName = document.getElementById("shaders-list").value;
			setURLParameter("file", shaderName);
			getCanvasData(shaderName, canvas, parameters)
			.then(function(data) {
				loopData = data;
			});
		});
		// Base Parameters
		document.getElementById("branch-count").value = config.branchCount;
		document.getElementById("branch-count").addEventListener("change", function(ev) {
			var branchCount = +(document.getElementById("branch-count").value);
			if(branchCount > 0) { setURLParameter("branch", branchCount); parameters.branchCount = branchCount; }
		});
		document.getElementById("counterclockwise").checked = config.rotation === -1;
		document.getElementById("counterclockwise").addEventListener("change", function(ev) {
			var checked = document.getElementById("counterclockwise").checked;
			addOrRemoveParameter("counterclockwise", checked);
			parameters.rotation = checked ? -1 : 1;
		});
		document.getElementById("inwards").value = config.direction === -1;
		document.getElementById("inwards").addEventListener("change", function(ev) {
			const checked = document.getElementById("inwards").checked;
			addOrRemoveParameter("inwards", checked);
			parameters.direction = checked ? 1 : -1;
		});
		// Colors parameters
		document.getElementById("bg-color").value = rgbToHex(config.colors.bg);
		document.getElementById("bg-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("bg-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("bg", value); parameters.colors.bg = color; }
		});
		document.getElementById("fg-color").value = rgbToHex(config.colors.fg);
		document.getElementById("fg-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("fg-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("fg", value); parameters.colors.fg = color; }
		});
		document.getElementById("pulse-color").value = rgbToHex(config.colors.pulse);
		document.getElementById("pulse-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("pulse-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("pulse", value); parameters.colors.pulse = color; }
		});
		document.getElementById("dim-color").value = rgbToHex(config.colors.dim);
		document.getElementById("dim-color").addEventListener("change", function(ev) {
			var value = (document.getElementById("dim-color").value).substr(1);
			var color = hexToRgb(value);
			if(color) { setURLParameter("dim", value); parameters.colors.dim = color; }
		});
		console.log(rgbToHex(config.colors.pulse));
		// Display the area
		document.getElementById("interface").style.display = "";
		document.getElementById("test").classList.add("hasInterface");
	}

	var	canvas = document.getElementById('canvas');

	return getCanvasData(config.shaderFileName, canvas, parameters)
	.then(function(data) {
		// Setup program size.
		onWindowResize(config, canvas, data.parameters);
		window.addEventListener("resize", function(ev) { onWindowResize(config, canvas, data.parameters); }, false);
		return data;
	});
}

function gradient(color1, color2, value) {
	return {
		r: color1.r * (1 - value) + color2.r * (value),
		g: color1.g * (1 - value) + color2.g * (value),
		b: color1.b * (1 - value) + color2.b * (value),
	}
}

function onWindowResize(config, canvas, parameters) {
	if(config.canvasSize === "small") {
		canvas.width = 400;
		canvas.height = 300;
	}
	else if(config.canvasSize === "interface") {
		var width = 2 * window.innerWidth / 3;
		var height = 3 * width / 4;
		if(height > window.innerHeight) {
			width = 4 * window.innerHeight / 3;
			height = window.innerHeight;
		}
		canvas.width = width;
		canvas.height = height;
	}
	else if(config.canvasSize === "screen") {
		canvas.width = 1280;
		canvas.height = 720;
	}
	else if(config.canvasSize === "1080") {
		canvas.width = 1920;
		canvas.height = 1080;
	}
	else if(config.canvasSize === "16-9") {
		canvas.width = 1600;
		canvas.height = 900;
	}
	else {
		canvas.width = window.innerWidth;
		canvas.height = window.innerHeight;
	}
	parameters.screenWidth = canvas.width;
	parameters.screenHeight = canvas.height;
	parameters.aspectX = canvas.width/canvas.height;
	parameters.aspectY = 1.0;
	gl.viewport(0, 0, canvas.width, canvas.height);
}

function loop(/**@type {WebGLProgram}*/program, /**@type {Locations}*/locations, parameters) {
	if (!program) return;
	// Clear the screen. Not necessary so far since we draw everywhere on it.
	// gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

	if(colorGradient != null) {
		colorGradient += 0.02;
		parameters.colors.fg = gradient(oldColor, newColor, colorGradient);
		if(colorGradient >= 1.0)
			colorGradient = null;
	}

	if(offsetGradient != null) {
		offsetGradient += 0.01;
		parameters.offsetCenter = oldOffsetCenter * (1 - offsetGradient) + newOffsetCenter * (offsetGradient);
		if(offsetGradient >= 1.0)
			offsetGradient = null;
	}

	if(script[0] && (Date.now() > eventTimeout)) {
		document.getElementById("screenText").style.opacity = 1;
		document.getElementById("screenText").innerHTML = script[0].waitText;
	}
	else if(emptyTimeout && (Date.now() > emptyTimeout)) {
		document.getElementById("screenText").style.opacity = 0;
		emptyTimeout = null;
	}

	renderCanvas(program, locations, parameters);
}
<|endoftext|>


<commit_before>function initJournal() {
    var indicator = $('#ajax-progress-indicator');

    $('.day-box input[type="checkbox"]').click(function(event){
        var box = $(this);
        $.ajax(box.data('url'), {
            'type': 'POST',
            'async': true,
            'dataType': 'json',
            'data': {
                'pk': box.data('student-id'),
                'date': box.data('date'),
                'present': box.is(':checked') ? '1' : '',
                'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
            },
            'beforeSend': function(){
                indicator.empty()
                         .append( 'Йде збереження... ' )
                         .show();
            },
            'error': function(xhr, status, error){
                indicator.empty()
                         .removeClass('alert-warning')
                         .addClass('alert-danger')
                         .append( 'Виникла наступна помилка: ' + error );
            },
            'success': function(data, status, xhr){
                indicator.delay( 5800 ).empty().hide();
            }
        });
    });
}

function initGroupSelector() {
    // look uo select element with groups and attach our even handler
    // on field "change" event
    $('#groupe-selector select').change(function(event){
        // get value of currently selected group option
        var group = $(this).val();

        if (group) {
            // set cookie with expiration date 1 year since now;
            // cookie creation function takes period in days
            Cookies.set('current_group', group, {path: '/', expires: 365});
        } else {
            // otherwise we delete the cookie
            Cookies.remove('current_group', {'path': '/'});
        }

        // and reload a page
        location.reload(true);

        return true;
    });
}

function initDateFields() {
    $('input.dateinput').datetimepicker({
        'format': 'YYYY-MM-DD'
    }).on('dp.hide', function(event){
        $(this).blur();
    });
}

function initEditStudentPage() {
    $('a.student-edit-form-link').click(function(event){
        var link = $(this);
        $.ajax({
            'url': link.attr('href'),
            'dataType': 'html',
            'type': 'get',
            'success': function(data, status, xhr){
                // check is we got successfull responce from the server
                if (status != 'success') {
                    alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
                    return false;
                }

                // update modal window with arrived content from the server
                var modal = $('#myModal'),
                    html = $(data), form = html.find('#content-columns form');
                modal.find('.modal-title').html(html.find('#content-columns h2').text());
                modal.find('.modal-body').html(form);

                // init our edit form
                initEditStudentForm(form, modal);

                // setup and show modal window finally
                modal.modal({
                    'keyboard': false,
                    'backdrop': false,
                    'show': true
                });
            },
            'error': function(){
                alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
                return false;
            }
        });

        return false;
    });
}

function initEditStudentForm(form, modal) {
    // attach datepicker
    initDateFields();

    // close modal window on Cancel button click
    form.find('input[name="cancel_button"]').click(function(event){
        modal.modal('hide');
        return false;
    });

    // make form work in AJAX mode
    form.ajaxForm({
        'dataType': 'html',
        'beforeSend': function(){
            var my = $('input.form-control')
            my.attr('disabled', true);
        },
        'error': function(){
            alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
            return false;
        },
        'success': function(data, status, xhr) {
            var html = $(data), newform = html.find('#content-columns form');

            // copy alert to modal window
            modal.find('.modal-body').html(html.find('.alert'));

            // copy form to modal if we found it in server response
            if (newform.length > 0) {
                modal.find('.modal-body').append(newform);

                //initialize form fields and buttons
                initEditStudentForm(newform, modal);
            } else {
                // if no form, it means success and we need to reload page
                // to get update students list;
                // reload after 2 seconds, so tat user can read
                // success message
                setTimeout(function(){location.reload(true);}, 500);
            }
        }
    });
}

function loadFromTab() {
    $('ul.nav-tabs li a').click(function(event){
        var link = $(this);
        $('#content-columns').load(link.attr('href') + ' #content-columns .col-xs-12');
        initEditStudentPage();
    });
}

$(document).ready(function(){
    loadFromTab();
    initJournal();
    initGroupSelector();
    initDateFields();
    initEditStudentPage();
});<commit_msg>make tab active depending of loaded data<commit_after>function initJournal() {
    var indicator = $('#ajax-progress-indicator');

    $('.day-box input[type="checkbox"]').click(function(event){
        var box = $(this);
        $.ajax(box.data('url'), {
            'type': 'POST',
            'async': true,
            'dataType': 'json',
            'data': {
                'pk': box.data('student-id'),
                'date': box.data('date'),
                'present': box.is(':checked') ? '1' : '',
                'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
            },
            'beforeSend': function(){
                indicator.empty()
                         .append( 'Йде збереження... ' )
                         .show();
            },
            'error': function(xhr, status, error){
                indicator.empty()
                         .removeClass('alert-warning')
                         .addClass('alert-danger')
                         .append( 'Виникла наступна помилка: ' + error );
            },
            'success': function(data, status, xhr){
                indicator.delay( 5800 ).empty().hide();
            }
        });
    });
}

function initGroupSelector() {
    // look uo select element with groups and attach our even handler
    // on field "change" event
    $('#groupe-selector select').change(function(event){
        // get value of currently selected group option
        var group = $(this).val();

        if (group) {
            // set cookie with expiration date 1 year since now;
            // cookie creation function takes period in days
            Cookies.set('current_group', group, {path: '/', expires: 365});
        } else {
            // otherwise we delete the cookie
            Cookies.remove('current_group', {'path': '/'});
        }

        // and reload a page
        location.reload(true);

        return true;
    });
}

function initDateFields() {
    $('input.dateinput').datetimepicker({
        'format': 'YYYY-MM-DD'
    }).on('dp.hide', function(event){
        $(this).blur();
    });
}

function initEditStudentPage() {
    $('a.student-edit-form-link').click(function(event){
        var link = $(this);
        $.ajax({
            'url': link.attr('href'),
            'dataType': 'html',
            'type': 'get',
            'success': function(data, status, xhr){
                // check is we got successfull responce from the server
                if (status != 'success') {
                    alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
                    return false;
                }

                // update modal window with arrived content from the server
                var modal = $('#myModal'),
                    html = $(data), form = html.find('#content-columns form');
                modal.find('.modal-title').html(html.find('#content-columns h2').text());
                modal.find('.modal-body').html(form);

                // init our edit form
                initEditStudentForm(form, modal);

                // setup and show modal window finally
                modal.modal({
                    'keyboard': false,
                    'backdrop': false,
                    'show': true
                });
            },
            'error': function(){
                alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
                return false;
            }
        });

        return false;
    });
}

function initEditStudentForm(form, modal) {
    // attach datepicker
    initDateFields();

    // close modal window on Cancel button click
    form.find('input[name="cancel_button"]').click(function(event){
        modal.modal('hide');
        return false;
    });

    // make form work in AJAX mode
    form.ajaxForm({
        'dataType': 'html',
        'beforeSend': function(){
            var my = $('input.form-control')
            my.attr('disabled', true);
        },
        'error': function(){
            alert('Помилка на сервері. Спробуйте, будь ласка, пізніше.');
            return false;
        },
        'success': function(data, status, xhr) {
            var html = $(data), newform = html.find('#content-columns form');

            // copy alert to modal window
            modal.find('.modal-body').html(html.find('.alert'));

            // copy form to modal if we found it in server response
            if (newform.length > 0) {
                modal.find('.modal-body').append(newform);

                //initialize form fields and buttons
                initEditStudentForm(newform, modal);
            } else {
                // if no form, it means success and we need to reload page
                // to get update students list;
                // reload after 2 seconds, so tat user can read
                // success message
                setTimeout(function(){location.reload(true);}, 500);
            }
        }
    });
}

function loadFromTab() {
    $('ul.nav-tabs li a').click( function(event){
        var link = $(this);
        $('#content-columns').load(link.attr('href') + ' #content-columns .col-xs-12');

        $('ul.nav-tabs li').each( function() {
            var that = $(this);
            that[ that.find('a').attr('href') === link.attr('href') ? 'addClass' : 'removeClass' ]('active');
        });

        initEditStudentPage();
    });
}

$(document).ready(function(){
    loadFromTab();
    initJournal();
    initGroupSelector();
    initDateFields();
    initEditStudentPage();
});<|endoftext|>


<commit_before>import {Observable} from 'rxjs'

const XHR2 = typeof XMLHttpRequest !== 'undefined'
  ? XMLHttpRequest
  : require('xhr2')

export const request = function request(options) {
  return Observable.ajax({createXHR: () => new XHR2(), ...options})
}
<commit_msg>Different xhr2 import<commit_after>import {Observable} from 'rxjs'
import xhr2 from 'xhr2'

const XHR2 = typeof XMLHttpRequest !== 'undefined'
  ? XMLHttpRequest
  : xhr2

export const request = function request(options) {
  return Observable.ajax({createXHR: () => new XHR2(), ...options})
}
<|endoftext|>