<commit_before>/*
 * Predator Vision
 * http://idevelop.github.com/predator-vision/
 *
 * Copyright 2013, Andrei Gheorghe (http://github.com/idevelop)
 * Released under the MIT license
 */

(function() {
	var heatmap;
	var heatmapDecay = 250; // higher values => faster decay
	var heatmapPointSize = 6;
	var heatmapPointIntensity = 10 / 255;
	var heatmapCanvas = document.getElementById('heatmap');

	camera.init({
		// downscale video for performance reasons
		width: 320,
		height: 240,

		fps: 60,
		mirror: true,

		onFrame: function(canvas) {
			if (heatmap) {
				// get list of pixels which are different
				movement.fromCanvas(canvas, {
					callback: function(updateList) {
						for (var i = 0; i < updateList.length; i++) {
							heatmap.addPoint(updateList[i].x * 2, updateList[i].y * 2, heatmapPointSize, heatmapPointIntensity);
						}
					}
				});

				// update, decay, etc
	            heatmap.update();
	            heatmap.multiply(1 - heatmapDecay / (100 * 100));
	            heatmap.display();
			}
		},

		onSuccess: function() {
			document.getElementById("info").style.display = "none";

			try {
				heatmap = createWebGLHeatmap({
					canvas: heatmapCanvas
				});
			} catch (exception) {
				// webgl not supported
				camera.stop();
				heatmapCanvas.style.display = "none";
				document.getElementById("webGLNotSupported").style.display = "block";
			}
		},

		onError: console.error,

		onNotSupported: function() {
			document.getElementById("info").style.display = "none";
			heatmapCanvas.style.display = "none";
			document.getElementById("cameraNotSupported").style.display = "block";
		}
	});
})();<commit_msg>Increased heatmap point intensity<commit_after>/*
 * Predator Vision
 * http://idevelop.github.com/predator-vision/
 *
 * Copyright 2013, Andrei Gheorghe (http://github.com/idevelop)
 * Released under the MIT license
 */

(function() {
	var heatmap;
	var heatmapDecay = 250; // higher values => faster decay
	var heatmapPointSize = 6;
	var heatmapPointIntensity = 20 / 255;
	var heatmapCanvas = document.getElementById('heatmap');

	camera.init({
		// downscale video for performance reasons
		width: 320,
		height: 240,

		fps: 60,
		mirror: true,

		onFrame: function(canvas) {
			if (heatmap) {
				// get list of pixels which are different
				movement.fromCanvas(canvas, {
					callback: function(updateList) {
						for (var i = 0; i < updateList.length; i++) {
							heatmap.addPoint(updateList[i].x * 2, updateList[i].y * 2, heatmapPointSize, heatmapPointIntensity);
						}
					}
				});

				// update, decay, etc
	            heatmap.update();
	            heatmap.multiply(1 - heatmapDecay / (100 * 100));
	            heatmap.display();
			}
		},

		onSuccess: function() {
			document.getElementById("info").style.display = "none";

			try {
				heatmap = createWebGLHeatmap({
					canvas: heatmapCanvas
				});
			} catch (exception) {
				// webgl not supported
				camera.stop();
				heatmapCanvas.style.display = "none";
				document.getElementById("webGLNotSupported").style.display = "block";
			}
		},

		onError: console.error,

		onNotSupported: function() {
			document.getElementById("info").style.display = "none";
			heatmapCanvas.style.display = "none";
			document.getElementById("cameraNotSupported").style.display = "block";
		}
	});
})();<|endoftext|>


<commit_before>import { createSelector } from 'reselect';
import { List } from 'immutable';

import * as filterUtils from '../utils/filterUtils';
import getWorldSelector from './World/worldSelectors';
import getWeatherSelector from './Weather/weatherSelectors';
import getIdleDialogStatus from './Idle/idleSelectors';
import * as categorySelectors from './Category/categorySelectors';
import * as catalogueSelectors from './Catalogue/catalogueSelectors';
import * as storeCodeSelectors from './StoreCode/storeCodeSelectors';
import * as storeSelectors from './Store/storeSelectors';
import * as productSelectors from './Product/productSelectors';
import * as filtersSelector from './Filters/filtersSelectors';

export function getWorld(state) {
  return getWorldSelector(state.get('worldReducer'));
}

export function getWeather(state) {
  return getWeatherSelector(state.get('weatherReducer'));
}

export function getStoreCode(state) {
  return storeCodeSelectors.getStoreCode(state.get('storeCodeReducer'));
}

// CATEGORY
export function getCategory(state, categoryCode) {
  return categorySelectors.getCategory(state.get('categoryReducer'), categoryCode);
}

export function getSellingAids(state, categoryCode) {
  return categorySelectors.getSellingAids(state.get('categoryReducer'), categoryCode);
}

export function getFilters(state, categoryCode) {
  return categorySelectors.getFilters(state.get('categoryReducer'), categoryCode);
}

export function getOrderedProducts(state, categoryCode) {
  return categorySelectors.getOrderedProducts(state.get('categoryReducer'), categoryCode);
}

// CATALOGUE
export function getProductList(state) {
  return catalogueSelectors.getProductList(state.get('catalogueReducer'));
}

export const getIdsByAids = createSelector(
  [getSellingAids, getActiveAid],
  (sellingAids, activeAid) => filterUtils.filterProductsByAid(sellingAids, activeAid)
);

export const getIdsByFilters = createSelector(
  [getFilters, getActiveFilters],
  (filterGroups, activeFilters) => filterUtils.filterProducts(filterGroups, activeFilters)
);

export const getIdsByAvailability = createSelector(
  [getOrderedProducts, getActiveAvailability],
  (orderedProducts, activeAvailability) =>
    filterUtils.filterProductsByAvailability(orderedProducts, activeAvailability)
);

export const getIdsByTempFilters = createSelector(
  [getFilters, getTempFilters],
  (filterGroups, tempFilters) => filterUtils.filterProducts(filterGroups, tempFilters)
);

export const getIdsByTempAvailability = createSelector(
  [getOrderedProducts, getTempAvailability],
  (orderedProducts, tempAvailability) =>
    filterUtils.filterProductsByAvailability(orderedProducts, tempAvailability)
);

export const getCatalogueProductsIds = createSelector(
  [getIdsByAids, getIdsByFilters, getIdsByAvailability],
  (idsByAids, idsByFilters, idsByAvailability) =>
    filterUtils.filterCatalogue(idsByAids, idsByFilters, idsByAvailability)
);

export const getCatalogueProducts = () =>
  createSelector([getProductList, getCatalogueProductsIds], (productList, idsToShow) =>
    productList.filter(p => idsToShow.contains(p.get('code'))).toList()
  );

export const getSimilarProducts = () =>
  createSelector([getProductList, getSimilarProductsIds], (productList, idsToShow) =>
    productList.filter(p => idsToShow.contains(p.get('code'))).toList()
  );

export const getItemCount = createSelector(
  [getIdsByAids, getIdsByTempFilters, getIdsByTempAvailability],
  (idsByAids, idsByTempFilters, idsByTempAvailability) =>
    filterUtils.filterCatalogue(idsByAids, idsByTempFilters, idsByTempAvailability).size
);

// FILTERS
export function getFilterMap(state) {
  return filtersSelector.getFilterMap(state.get('filtersReducer'));
}

export function getDialogStatus(state) {
  return filtersSelector.getDialogStatus(state.get('filtersReducer'));
}

export function getActiveAid(state) {
  return filtersSelector.getActiveAid(state.get('filtersReducer'));
}

export function getActiveFilters(state) {
  return filtersSelector.getActiveFilters(state.get('filtersReducer'));
}

export function getActiveAvailability(state) {
  return filtersSelector.getActiveAvailability(state.get('filtersReducer'));
}

export function getTempAid(state) {
  return filtersSelector.getTempAid(state.get('filtersReducer'));
}

export function getTempFilters(state) {
  return filtersSelector.getTempFilters(state.get('filtersReducer'));
}

export function getTempAvailability(state) {
  return filtersSelector.getTempAvailability(state.get('filtersReducer'));
}

// PRODUCT
export function getProduct(state, productCode) {
  return productSelectors.getProduct(state.get('productReducer'), productCode);
}

export const getSimilarProductsIds = createSelector(getProduct, product => {
  if (product) {
    const sim = product.get('similarProducts') || List();
    return sim.reduce((acc, value) => acc.push(value.get('products')), List()).flatten();
  }
  return List();
});

export function getAllStoreStock(state, props) {
  return productSelectors.getAllStoreStock(state.get('productReducer'), props);
}

// STORE
export function getStore(state) {
  return storeSelectors.getStoreInfo(state.get('storeReducer'));
}

export function getStoreName(state) {
  return storeSelectors.getStoreName(state.get('storeReducer'));
}

export function getNearbyStores(state, props) {
  return storeSelectors.getNearbyStores(state.get('storeReducer'), props);
}

export function hasNearbyStores(state) {
  return storeSelectors.hasNearbyStores(state.get('storeReducer'));
}

export const getNearbyStoresWithStock = createSelector(
  [getNearbyStores, getAllStoreStock],
  (nearbyStores, allStoreStock) =>
    nearbyStores.map(s => {
      const currentStore = allStoreStock.find(ns => ns.get('storeCode') === s.get('code'));
      return s
        .set('storeStock', currentStore.get('storeStock'))
        .set('stockStatus', currentStore.get('stockStatus'));
    })
);

export const getNearbyStoresWithProductInStock = createSelector(
  [getNearbyStoresWithStock, getStore],
  (nearbyStoreStock, currentStore) =>
    nearbyStoreStock.filterNot(
      s => s.get('storeStock') <= 0 || s.get('code') === currentStore.get('code')
    )
);

export const getSelectedNearbyStoreInfo = selectedStore =>
  createSelector([getNearbyStoresWithStock], nearbyStoreStock =>
    nearbyStoreStock.find(ns => ns.get('code') === selectedStore)
  );

// Idle

export function getIdleDialog(state) {
  return getIdleDialogStatus(state.get('idleReducer'));
}
<commit_msg>getNearbyStoresWithStock: handle scenario where storeStock is not available<commit_after>import { createSelector } from 'reselect';
import { List } from 'immutable';

import * as filterUtils from '../utils/filterUtils';
import getWorldSelector from './World/worldSelectors';
import getWeatherSelector from './Weather/weatherSelectors';
import getIdleDialogStatus from './Idle/idleSelectors';
import * as categorySelectors from './Category/categorySelectors';
import * as catalogueSelectors from './Catalogue/catalogueSelectors';
import * as storeCodeSelectors from './StoreCode/storeCodeSelectors';
import * as storeSelectors from './Store/storeSelectors';
import * as productSelectors from './Product/productSelectors';
import * as filtersSelector from './Filters/filtersSelectors';

export function getWorld(state) {
  return getWorldSelector(state.get('worldReducer'));
}

export function getWeather(state) {
  return getWeatherSelector(state.get('weatherReducer'));
}

export function getStoreCode(state) {
  return storeCodeSelectors.getStoreCode(state.get('storeCodeReducer'));
}

// CATEGORY
export function getCategory(state, categoryCode) {
  return categorySelectors.getCategory(state.get('categoryReducer'), categoryCode);
}

export function getSellingAids(state, categoryCode) {
  return categorySelectors.getSellingAids(state.get('categoryReducer'), categoryCode);
}

export function getFilters(state, categoryCode) {
  return categorySelectors.getFilters(state.get('categoryReducer'), categoryCode);
}

export function getOrderedProducts(state, categoryCode) {
  return categorySelectors.getOrderedProducts(state.get('categoryReducer'), categoryCode);
}

// CATALOGUE
export function getProductList(state) {
  return catalogueSelectors.getProductList(state.get('catalogueReducer'));
}

export const getIdsByAids = createSelector(
  [getSellingAids, getActiveAid],
  (sellingAids, activeAid) => filterUtils.filterProductsByAid(sellingAids, activeAid)
);

export const getIdsByFilters = createSelector(
  [getFilters, getActiveFilters],
  (filterGroups, activeFilters) => filterUtils.filterProducts(filterGroups, activeFilters)
);

export const getIdsByAvailability = createSelector(
  [getOrderedProducts, getActiveAvailability],
  (orderedProducts, activeAvailability) =>
    filterUtils.filterProductsByAvailability(orderedProducts, activeAvailability)
);

export const getIdsByTempFilters = createSelector(
  [getFilters, getTempFilters],
  (filterGroups, tempFilters) => filterUtils.filterProducts(filterGroups, tempFilters)
);

export const getIdsByTempAvailability = createSelector(
  [getOrderedProducts, getTempAvailability],
  (orderedProducts, tempAvailability) =>
    filterUtils.filterProductsByAvailability(orderedProducts, tempAvailability)
);

export const getCatalogueProductsIds = createSelector(
  [getIdsByAids, getIdsByFilters, getIdsByAvailability],
  (idsByAids, idsByFilters, idsByAvailability) =>
    filterUtils.filterCatalogue(idsByAids, idsByFilters, idsByAvailability)
);

export const getCatalogueProducts = () =>
  createSelector([getProductList, getCatalogueProductsIds], (productList, idsToShow) =>
    productList.filter(p => idsToShow.contains(p.get('code'))).toList()
  );

export const getSimilarProducts = () =>
  createSelector([getProductList, getSimilarProductsIds], (productList, idsToShow) =>
    productList.filter(p => idsToShow.contains(p.get('code'))).toList()
  );

export const getItemCount = createSelector(
  [getIdsByAids, getIdsByTempFilters, getIdsByTempAvailability],
  (idsByAids, idsByTempFilters, idsByTempAvailability) =>
    filterUtils.filterCatalogue(idsByAids, idsByTempFilters, idsByTempAvailability).size
);

// FILTERS
export function getFilterMap(state) {
  return filtersSelector.getFilterMap(state.get('filtersReducer'));
}

export function getDialogStatus(state) {
  return filtersSelector.getDialogStatus(state.get('filtersReducer'));
}

export function getActiveAid(state) {
  return filtersSelector.getActiveAid(state.get('filtersReducer'));
}

export function getActiveFilters(state) {
  return filtersSelector.getActiveFilters(state.get('filtersReducer'));
}

export function getActiveAvailability(state) {
  return filtersSelector.getActiveAvailability(state.get('filtersReducer'));
}

export function getTempAid(state) {
  return filtersSelector.getTempAid(state.get('filtersReducer'));
}

export function getTempFilters(state) {
  return filtersSelector.getTempFilters(state.get('filtersReducer'));
}

export function getTempAvailability(state) {
  return filtersSelector.getTempAvailability(state.get('filtersReducer'));
}

// PRODUCT
export function getProduct(state, productCode) {
  return productSelectors.getProduct(state.get('productReducer'), productCode);
}

export const getSimilarProductsIds = createSelector(getProduct, product => {
  if (product) {
    const sim = product.get('similarProducts') || List();
    return sim.reduce((acc, value) => acc.push(value.get('products')), List()).flatten();
  }
  return List();
});

export function getAllStoreStock(state, props) {
  return productSelectors.getAllStoreStock(state.get('productReducer'), props);
}

// STORE
export function getStore(state) {
  return storeSelectors.getStoreInfo(state.get('storeReducer'));
}

export function getStoreName(state) {
  return storeSelectors.getStoreName(state.get('storeReducer'));
}

export function getNearbyStores(state, props) {
  return storeSelectors.getNearbyStores(state.get('storeReducer'), props);
}

export function hasNearbyStores(state) {
  return storeSelectors.hasNearbyStores(state.get('storeReducer'));
}

export const getNearbyStoresWithStock = createSelector(
  [getNearbyStores, getAllStoreStock],
  (nearbyStores, allStoreStock) =>
    nearbyStores.map(s => {
      const currentStore = allStoreStock.find(ns => ns.get('storeCode') === s.get('code'));
      return s
        .set('storeStock', currentStore ? currentStore.get('storeStock') : 0)
        .set('stockStatus', currentStore ? currentStore.get('stockStatus') : 0);
    })
);

export const getNearbyStoresWithProductInStock = createSelector(
  [getNearbyStoresWithStock, getStore],
  (nearbyStoreStock, currentStore) =>
    nearbyStoreStock.filterNot(
      s => s.get('storeStock') <= 0 || s.get('code') === currentStore.get('code')
    )
);

export const getSelectedNearbyStoreInfo = selectedStore =>
  createSelector([getNearbyStoresWithStock], nearbyStoreStock =>
    nearbyStoreStock.find(ns => ns.get('code') === selectedStore)
  );

// Idle

export function getIdleDialog(state) {
  return getIdleDialogStatus(state.get('idleReducer'));
}
<|endoftext|>


<commit_before>// Node version of the .Net simple compress app

var fs = require('fs');
var zlib = require('zlib');
var path = require('path');
var crypto = require('crypto')

var isWin = /^win/.test(process.platform);

module.exports = {
    cli: cli,
    pack: Pack,
    unpack: Unpack
};

function cli() {
    var args = process.argv.slice(2);
    if (args.length < 3) { return ShowUsageAndExit(); }

    var src = path.resolve(args[1]);
    var dst = path.resolve(args[2]);
    var flags = readFlags(args[3] || "");

    switch (args[0]) {
        case "pack":
            Pack(src, dst, flags);
            break;

        case "unpack":
            Unpack(src, dst, flags);
            break;

        default:
            ShowUsageAndExit();
            break;
    }
}
// end of main program
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function ShowUsageAndExit() {
    console.log(
        ["Simple Compress",
            "    Usage:",
            "        sz pack <src directory> <target file>",
            "        sz unpack <src file> <target directory> [flags]",
            "    Flags:",
            "        h : replace duplicate files with hard links",
        ""].join(require('os').EOL)
    );
}

function readFlags(str){
    return {
        LinkDups : (str.indexOf('h') >= 0)
    };
}

var logStageWaiting = false;
function logStage(str) {
    if (logStageWaiting) {console.timeEnd(' done');}
    process.stdout.write(str);
    console.time(' done');
    logStageWaiting = true;
}

// recursively scan a directory and pack its contents into an archive
function Pack(src, dst, flags) {
    var temp = dst + '.tmp';
    if (fs.existsSync(temp)) {fs.truncateSync(temp, 0);}

    // build dictionary of equal files
    logStage('scanning for duplicates');
    var parts = {};
    var links = {};
    applyToDeepPaths(src, function eachFile (p){
        var hash = fileHashSync(p);
        var key = path.basename(p)+'|'+(hash.toString('base64'));
        var subs = p.replace(src,"");
        if (parts[key]) parts[key].paths.push(subs);
        else parts[key] = {paths : [subs], hash : hash};
    }, function shouldFollowSymlink(srcPath, targetPath){
        if (targetPath.indexOf(src) === 0) {
            // the link targets a path inside our source, write a link and return false (no follow)
            links[srcPath.replace(src,"")] = targetPath.replace(src,"");
            return false;
        } else {
            // the link is outside, we just return true (follow and treat as if it was not a link)
            return true;
        }
    });

    // open pack file
    var cat = fs.openSync(temp, 'w');

    logStage('writing log');
    // write first data of each dict entry to cat file
    Object.keys(parts).forEach(function(key){
        // Write <MD5:16 bytes>
        WriteBuffer(cat, parts[key].hash, 16);
        // Write <length:8 bytes><paths:utf8 str>
        var paths = parts[key].paths;
        var pathBuffer = new Buffer(paths.join('|'), 'utf8');
        WriteLength(cat, pathBuffer.length);
        fs.writeSync(cat, pathBuffer, 0, pathBuffer.length, null);
        // Write <length:8 bytes><data:byte array>
        var srcFile = path.resolve(path.join(src, paths[0]));
        var fileSize = fs.statSync(srcFile).size;
        WriteLength(cat, fileSize);
        WriteFileData(cat, srcFile);
    });
    // write link table
    Object.keys(links).forEach(function(key){
        // Write <zeroes:16 bytes>
        WriteLinkMarker(cat);

        // Write <length:8 bytes><path pair:utf8 str>, path pair is 'src|target'
        var pathBuffer = new Buffer(key+'|'+links[key], 'utf8');
        WriteLength(cat, pathBuffer.length);
        fs.writeSync(cat, pathBuffer, 0, pathBuffer.length, null);

        // Write <length:8 bytes>, always zero (there is not file content in a link)
        WriteLength(cat, 0);
    });

    // close and gzip the cat file
    fs.close(cat);
    if (fs.existsSync(dst)) {fs.truncateSync(dst, 0);}
    var gzip = zlib.createGzip({level:9});
    var inp = fs.createReadStream(temp);
    var out = fs.createWriteStream(dst);

    logStage('compressing');
    var unzip = inp.pipe(gzip).pipe(out); // unpack into catenated file

    // delete cat file
    unzip.on('finish', function unzipCallback(){
        if (inp.end) inp.end();
        if (out.end) out.end();
        fs.unlinkSync(temp);
        logStage('');
    });
}

function WriteLength(fd, length){
    var b = new Buffer(8);
    b.writeIntLE(length, 0, 8);
    fs.writeSync(fd, b, 0, 8, null);
}

function WriteLinkMarker(fd){
    // 16 bytes of zeroes instead of MD5
    var b = new Buffer(16);
    b.fill(0);
    fs.writeSync(fd, b, 0, 16, null);
}

function WriteBuffer(fd, buf, len) {
    fs.writeSync(fd, buf, 0, len, null);
}

function WriteFileData(dst, fileToAdd){
    var src = fs.openSync(fileToAdd, 'r');
    var buf = new Buffer(65536);

    for (;;){
        var rlen = fs.readSync(src, buf, 0, buf.length, null);
        if (rlen < 1) break;
        fs.writeSync(dst, buf, 0, rlen, null);
    }
    fs.close(src);
}

// run a function against every path under a root path
function applyToDeepPaths(root, fileFunction, followSymlinkPredicate){
    var exists = fs.existsSync(root);
    var stats = exists && fs.lstatSync(root);
    if (stats.isSymbolicLink()) {
        var target = fs.readlinkSync(root);
        if (followSymlinkPredicate && !followSymlinkPredicate(root, target)) return;
        stats = fs.statSync(root); // follow the link
    }
    var isDirectory = exists && stats.isDirectory();
    if (isDirectory) {
        fs.readdirSync(root).forEach(function(childItemName) {
            applyToDeepPaths(path.join(root, childItemName), fileFunction, followSymlinkPredicate);
        });
    } else if (exists) {
        fileFunction(root);
    }
}

// sync file hash without loading whole file
function fileHashSync(filename){
    var sum = crypto.createHash('md5');
    var fd = fs.openSync(filename, 'r');
    var buf = new Buffer(65536);

    for (;;){
        var rlen = fs.readSync(fd, buf, 0, buf.length, null);
        if (rlen < 1) break;
        sum.update(buf.slice(0, rlen));
    }
    fs.close(fd);
    return sum.digest();
}

// expand an existing package file into a directory structure
function Unpack(src, dst, flags) {
    var temp = src + '.tmp';
    // Unzip archive to temp location
    if (fs.existsSync(temp)) {fs.truncateSync(temp, 0);}
    var gzip = zlib.createGunzip();
    var inp = fs.createReadStream(src);
    var out = fs.createWriteStream(temp);

    logStage('decompressing');
    var unzip = inp.pipe(gzip).pipe(out); // unpack into catenated file

    unzip.on('finish', function unzipCallback(){
        if (inp.end) inp.end();
        if (out.end) out.end();

        logStage('unpacking files');
        unpackCat(temp, dst, flags);
        logStage('');
    });
}

// then run this over the structure...
// ToDo: see if this can be re-written as a single pipeline

function unpackCat(srcPack, targetPath, flags) {
    var cat = fs.openSync(srcPack, 'r');
    var buf = new Buffer(5000000); // general purpose buffer. Gets overwritten by child functions
    var offset = 0;

    for(;;){
        var hash = ReadHash(cat);
        if (hash == null) {break;}
        var pathLen = ReadLength(cat, buf);
        if (pathLen == 0) {break;}
        var paths = ReadPaths(pathLen, cat, buf);
        var fileLen = ReadLength(cat, buf);

        if (fileLen == 0 && hash.toString('hex') == "00000000000000000000000000000000") { // is a symlink to be restored
            if (paths.length != 2) { throw new Error('Malformed file: symbolic link did not have a single source and target'); }
            fs.symlinkSync(/*target*/path.join(targetPath, paths[1]), /*source*/path.join(targetPath, paths[0]), 'dir');
        } else { // is file data to be written to paths
            // read contents out to all the files at once.
            // The .Net version does a write-then-copy, but Node.js has no OS-level copy.
            ReadToFiles(fileLen, paths, targetPath, cat, buf, hash, flags);
        }
    }
    fs.close(cat);
    fs.unlinkSync(srcPack);
}

function ReadHash(fd) {
    var hbuf = new Buffer(16);
    var rlen = fs.readSync(fd, hbuf, 0, 16, null);
    if (rlen == 0) return null; // no more file
    if (rlen !== 16) throw new Error("Malformed file: truncated file set checksum");
    return hbuf;
}

function ReadPaths(len, fd, buffer){
    // todo: handle paths longer than buffer?
    var rlen = fs.readSync(fd, buffer, 0, len, null);
    if (rlen < 1) throw new Error('Malformed file: Empty path set');
    if (rlen != len) throw new Error("Malformed file: truncated file path list");
    var s = buffer.toString('utf8',0,rlen);
    s = correctFilepath(s);
    return s.split('|');
}

function ReadToFiles(len, paths, target, fd, buffer, expectedHash, flags){
    var remains = len;

    if (paths.length < 1) return;

    // check directories and truncate files
    for (var i = 0; i < paths.length; i++){
        var npath = path.join(target, paths[i]);
        ensureDirectory(path.dirname(npath));
        if (fs.existsSync(npath)) {fs.truncateSync(npath, 0);}
    }

    // read bytes into files. We write one file at a time, rescanning the buffer.
    // it should be less efficient, but it plays nicely with anti-virus junkware
    // and OS expectations, resulting in a faster overall output

    // 1) write first file
    var rlen = 0;
    var masterPath = path.join(target, paths[0]);
    var masterFd = fs.openSync(masterPath, 'w+');
    var sum = crypto.createHash('md5');
    while (remains > 0){
        var next = (remains > buffer.length) ? (buffer.length) : (remains);
        rlen = fs.readSync(fd, buffer, 0, next, null);
        if (rlen < 1) throw new Error('Malformed file: truncated file data');
        remains -= rlen;
        // write the file
        fs.writeSync(masterFd, buffer, 0, rlen);
        // calculate hash
        sum.update(buffer.slice(0, rlen));
    }
    var actualHash = sum.digest();

    // Check the written data against the originam checksum
    // We do this only once, because we're checking for errors in the package transit, not errors of the local system.
    if (actualHash.toString() !== expectedHash.toString()) {
        throw new Error('Damaged archive: File at '+paths[0]+' failed a checksum');
    }

    // copy or link all the subsequent duplicates (files only)
    if (flags.LinkDups) {
        // 2a) link to original for all subsequent files.
        for (var i = 1; i < paths.length; i++){
            var npath = path.join(target, paths[i]);
            fs.linkSync(/*target*/masterPath, /*source*/npath);
        }
    } else {
        // 2b) copy original for all subsequent files.
        for (var i = 1; i < paths.length; i++){
            var npath = path.join(target, paths[i]);
            var dstFd = fs.openSync(npath, 'w');
            var pos = 0;
            while (pos < len) {
                rlen = fs.readSync(masterFd, buffer, 0, buffer.length, pos);
                if (rlen < 1) break;
                pos += rlen;
                fs.writeSync(dstFd, buffer, 0, rlen);
            }
            fs.close(dstFd);
        }
    }
    fs.close(masterFd);
}

function ReadLength(fd, buffer){
    var rlen = fs.readSync(fd, buffer, 0, 8, null);
    if (rlen == 0) return 0; // correct end-of-file
    if (rlen != 8) throw new Error("Malformed file: truncated file segment length");
    return buffer.readIntLE(0, 8); // Node can only support 48 bits of precision, but the file uses 64 bits of data.
}

// node's file system support is shocking
function ensureDirectory (p) {
    p = path.resolve(p);
    try {
        fs.mkdirSync(p);
    } catch (err0) {
        switch (err0.code) {
            case 'ENOENT' :
                ensureDirectory(path.dirname(p));
                ensureDirectory(p);
                break;
            default:
                try {
                    var stat = fs.statSync(p);
                    if (!stat.isDirectory()) throw err0;
                } catch (err1) {
                    throw err0;
                }
                break;
        }
    }
}

/**
 * The compression may have been done on a different os to the current one, so the paths may need to be corrected
 * @param path
 */
function correctFilepath(path) {
    if (isWin) return path.split('/').join('\\');
    else return path.split('\\').join('/');
}

<commit_msg>Sketch of unpack script<commit_after>// Node version of the .Net simple compress app

var fs = require('fs');
var zlib = require('zlib');
var path = require('path');
var crypto = require('crypto')

var isWin = /^win/.test(process.platform);

module.exports = {
    cli: cli,
    pack: Pack,
    unpack: Unpack
};

function cli() {
    var args = process.argv.slice(2);
    if (args.length < 3) { return ShowUsageAndExit(); }

    var src = path.resolve(args[1]);
    var dst = path.resolve(args[2]);
    var flags = readFlags(args[3] || "");

    switch (args[0]) {
        case "pack":
            Pack(src, dst, flags);
            break;

        case "unpack":
            Unpack(src, dst, flags);
            break;

        default:
            ShowUsageAndExit();
            break;
    }
}
// end of main program
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function ShowUsageAndExit() {
    console.log(
        ["Simple Compress",
            "    Usage:",
            "        sz pack <src directory> <target file> [flags]",
            "        sz unpack <src file> <target directory> [flags]",
            "    Flags:",
            "        h : (unpack) replace duplicate files with hard links",
            "        x : (pack) create an expander script for the archive",
        ""].join(require('os').EOL)
    );
}

function readFlags(str){
    return {
        LinkDups : (str.indexOf('h') >= 0),
        Expander : (str.indexOf('x') >= 0)
    };
}

// recursively scan a directory and pack its contents into an archive
function Pack(src, dst, flags) {
    var temp = dst + '.tmp';
    if (fs.existsSync(temp)) {fs.truncateSync(temp, 0);}

    // build dictionary of equal files
    logStage('scanning for duplicates');
    var parts = {};
    var links = {};
    applyToDeepPaths(src, function eachFile (p){
        var hash = fileHashSync(p);
        var key = path.basename(p)+'|'+(hash.toString('base64'));
        var subs = p.replace(src,"");
        if (parts[key]) parts[key].paths.push(subs);
        else parts[key] = {paths : [subs], hash : hash};
    }, function shouldFollowSymlink(srcPath, targetPath){
        if (targetPath.indexOf(src) === 0) {
            // the link targets a path inside our source, write a link and return false (no follow)
            links[srcPath.replace(src,"")] = targetPath.replace(src,"");
            return false;
        } else {
            // the link is outside, we just return true (follow and treat as if it was not a link)
            return true;
        }
    });

    // open pack file
    var cat = fs.openSync(temp, 'w');

    logStage('writing log');
    // write first data of each dict entry to cat file
    Object.keys(parts).forEach(function(key){
        // Write <MD5:16 bytes>
        WriteBuffer(cat, parts[key].hash, 16);
        // Write <length:8 bytes><paths:utf8 str>
        var paths = parts[key].paths;
        var pathBuffer = new Buffer(paths.join('|'), 'utf8');
        WriteLength(cat, pathBuffer.length);
        fs.writeSync(cat, pathBuffer, 0, pathBuffer.length, null);
        // Write <length:8 bytes><data:byte array>
        var srcFile = path.resolve(path.join(src, paths[0]));
        var fileSize = fs.statSync(srcFile).size;
        WriteLength(cat, fileSize);
        WriteFileData(cat, srcFile);
    });
    // write link table
    Object.keys(links).forEach(function(key){
        // Write <zeroes:16 bytes>
        WriteLinkMarker(cat);

        // Write <length:8 bytes><path pair:utf8 str>, path pair is 'src|target'
        var pathBuffer = new Buffer(key+'|'+links[key], 'utf8');
        WriteLength(cat, pathBuffer.length);
        fs.writeSync(cat, pathBuffer, 0, pathBuffer.length, null);

        // Write <length:8 bytes>, always zero (there is not file content in a link)
        WriteLength(cat, 0);
    });

    // close and gzip the cat file
    fs.close(cat);
    if (fs.existsSync(dst)) {fs.truncateSync(dst, 0);}
    var gzip = zlib.createGzip({level:9});
    var inp = fs.createReadStream(temp);
    var out = fs.createWriteStream(dst);

    logStage('compressing');
    var unzip = inp.pipe(gzip).pipe(out); // unpack into catenated file

    // delete cat file
    unzip.on('finish', function unzipCallback(){
        if (inp.end) inp.end();
        if (out.end) out.end();
        fs.unlinkSync(temp);
        if (flags.Expander) WriteExpander(dst);
        logStage('');
    });
}

function WriteLength(fd, length){
    var b = new Buffer(8);
    b.writeIntLE(length, 0, 8);
    fs.writeSync(fd, b, 0, 8, null);
}

function WriteLinkMarker(fd){
    // 16 bytes of zeroes instead of MD5
    var b = new Buffer(16);
    b.fill(0);
    fs.writeSync(fd, b, 0, 16, null);
}

function WriteBuffer(fd, buf, len) {
    fs.writeSync(fd, buf, 0, len, null);
}

function WriteFileData(dst, fileToAdd){
    var src = fs.openSync(fileToAdd, 'r');
    var buf = new Buffer(65536);

    for (;;){
        var rlen = fs.readSync(src, buf, 0, buf.length, null);
        if (rlen < 1) break;
        fs.writeSync(dst, buf, 0, rlen, null);
    }
    fs.close(src);
}

// run a function against every path under a root path
function applyToDeepPaths(root, fileFunction, followSymlinkPredicate){
    var exists = fs.existsSync(root);
    var stats = exists && fs.lstatSync(root);
    if (stats.isSymbolicLink()) {
        var target = fs.readlinkSync(root);
        if (followSymlinkPredicate && !followSymlinkPredicate(root, target)) return;
        stats = fs.statSync(root); // follow the link
    }
    var isDirectory = exists && stats.isDirectory();
    if (isDirectory) {
        fs.readdirSync(root).forEach(function(childItemName) {
            applyToDeepPaths(path.join(root, childItemName), fileFunction, followSymlinkPredicate);
        });
    } else if (exists) {
        fileFunction(root);
    }
}

// sync file hash without loading whole file
function fileHashSync(filename){
    var sum = crypto.createHash('md5');
    var fd = fs.openSync(filename, 'r');
    var buf = new Buffer(65536);

    for (;;){
        var rlen = fs.readSync(fd, buf, 0, buf.length, null);
        if (rlen < 1) break;
        sum.update(buf.slice(0, rlen));
    }
    fs.close(fd);
    return sum.digest();
}

// Copy a subset of this code file into an expander script.
function WriteExpander(packagePath) {
    var relPath = './'+path.basename(packagePath);
    var scriptPath = rewriteName(packagePath, 'expand', 'js');
    var codes = fs.readFileSync(__filename, 'utf8');
    var marker = ('/'.repeat(40)) + " [UNPACK] " + ('/'.repeat(40));
    var idx = codes.indexOf(marker);

    if (idx < 100) {
        console.log("Could not create expander: source code damaged");
        return;
    }

    // cut out packing code, add a call to unpack the named archive
    // this is setup so the expansion is done in the current directory
    // by default, or a named directory with a cmd arg
    codes = codes.substring(idx) + "\nUnpack('"+relPath+"', process.argv[3] || '.', {});";

    fs.writeFileSync(scriptPath, codes, 'utf8');
}

// rewrites 'path/to/old.thing.txt' to 'path/to/pre.old.thing.post'
function rewriteName(old, pre, post) {
    var newName = pre + '.' + path.basename(old, path.extname(old)) + '.' + post;
    return path.join(path.dirname(old), newName);
}

// DO NOT change the line below, or the auto-expander will break:
//////////////////////////////////////// [UNPACK] ////////////////////////////////////////

// expand an existing package file into a directory structure
function Unpack(src, dst, flags) {
    var temp = src + '.tmp';
    // Unzip archive to temp location
    if (fs.existsSync(temp)) {fs.truncateSync(temp, 0);}
    var gzip = zlib.createGunzip();
    var inp = fs.createReadStream(src);
    var out = fs.createWriteStream(temp);

    logStage('decompressing');
    var unzip = inp.pipe(gzip).pipe(out); // unpack into catenated file

    unzip.on('finish', function unzipCallback(){
        if (inp.end) inp.end();
        if (out.end) out.end();

        logStage('unpacking files');
        unpackCat(temp, dst, flags);
        logStage('');
    });
}

// then run this over the structure...
function unpackCat(srcPack, targetPath, flags) {
    var cat = fs.openSync(srcPack, 'r');
    var buf = new Buffer(5000000); // general purpose buffer. Gets overwritten by child functions
    var offset = 0;

    for(;;){
        var hash = ReadHash(cat);
        if (hash == null) {break;}
        var pathLen = ReadLength(cat, buf);
        if (pathLen == 0) {break;}
        var paths = ReadPaths(pathLen, cat, buf);
        var fileLen = ReadLength(cat, buf);

        if (fileLen == 0 && hash.toString('hex') == ("0".repeat(32))) { // is a symlink to be restored
            if (paths.length != 2) { throw new Error('Malformed file: symbolic link did not have a single source and target'); }
            fs.symlinkSync(/*target*/path.join(targetPath, paths[1]), /*source*/path.join(targetPath, paths[0]), 'dir');
        } else { // is file data to be written to paths
            // read contents out to all the files at once.
            // The .Net version does a write-then-copy, but Node.js has no OS-level copy.
            ReadToFiles(fileLen, paths, targetPath, cat, buf, hash, flags);
        }
    }
    fs.close(cat);
    fs.unlinkSync(srcPack);
}

function ReadHash(fd) {
    var hbuf = new Buffer(16);
    var rlen = fs.readSync(fd, hbuf, 0, 16, null);
    if (rlen == 0) return null; // no more file
    if (rlen !== 16) throw new Error("Malformed file: truncated file set checksum");
    return hbuf;
}

function ReadPaths(len, fd, buffer){
    // todo: handle paths longer than buffer?
    var rlen = fs.readSync(fd, buffer, 0, len, null);
    if (rlen < 1) throw new Error('Malformed file: Empty path set');
    if (rlen != len) throw new Error("Malformed file: truncated file path list");
    var s = buffer.toString('utf8',0,rlen);
    s = correctFilepath(s);
    return s.split('|');
}

function ReadToFiles(len, paths, target, fd, buffer, expectedHash, flags){
    var remains = len;

    if (paths.length < 1) return;

    // check directories and truncate files
    for (var i = 0; i < paths.length; i++){
        var npath = path.join(target, paths[i]);
        ensureDirectory(path.dirname(npath));
        if (fs.existsSync(npath)) {fs.truncateSync(npath, 0);}
    }

    // read bytes into files. We write one file at a time, rescanning the buffer.
    // it should be less efficient, but it plays nicely with anti-virus junkware
    // and OS expectations, resulting in a faster overall output

    // 1) write first file
    var rlen = 0;
    var masterPath = path.join(target, paths[0]);
    var masterFd = fs.openSync(masterPath, 'w+');
    var sum = crypto.createHash('md5');
    while (remains > 0){
        var next = (remains > buffer.length) ? (buffer.length) : (remains);
        rlen = fs.readSync(fd, buffer, 0, next, null);
        if (rlen < 1) throw new Error('Malformed file: truncated file data');
        remains -= rlen;
        // write the file
        fs.writeSync(masterFd, buffer, 0, rlen);
        // calculate hash
        sum.update(buffer.slice(0, rlen));
    }
    var actualHash = sum.digest();

    // Check the written data against the originam checksum
    // We do this only once, because we're checking for errors in the package transit, not errors of the local system.
    if (actualHash.toString() !== expectedHash.toString()) {
        throw new Error('Damaged archive: File at '+paths[0]+' failed a checksum');
    }

    // copy or link all the subsequent duplicates (files only)
    if (flags.LinkDups) {
        // 2a) link to original for all subsequent files.
        for (var i = 1; i < paths.length; i++){
            var npath = path.join(target, paths[i]);
            fs.linkSync(/*target*/masterPath, /*source*/npath);
        }
    } else {
        // 2b) copy original for all subsequent files.
        for (var i = 1; i < paths.length; i++){
            var npath = path.join(target, paths[i]);
            var dstFd = fs.openSync(npath, 'w');
            var pos = 0;
            while (pos < len) {
                rlen = fs.readSync(masterFd, buffer, 0, buffer.length, pos);
                if (rlen < 1) break;
                pos += rlen;
                fs.writeSync(dstFd, buffer, 0, rlen);
            }
            fs.close(dstFd);
        }
    }
    fs.close(masterFd);
}

function ReadLength(fd, buffer){
    var rlen = fs.readSync(fd, buffer, 0, 8, null);
    if (rlen == 0) return 0; // correct end-of-file
    if (rlen != 8) throw new Error("Malformed file: truncated file segment length");
    return buffer.readIntLE(0, 8); // Node can only support 48 bits of precision, but the file uses 64 bits of data.
}

// node's file system support is shocking
function ensureDirectory (p) {
    p = path.resolve(p);
    try {
        fs.mkdirSync(p);
    } catch (err0) {
        switch (err0.code) {
            case 'ENOENT' :
                ensureDirectory(path.dirname(p));
                ensureDirectory(p);
                break;
            default:
                try {
                    var stat = fs.statSync(p);
                    if (!stat.isDirectory()) throw err0;
                } catch (err1) {
                    throw err0;
                }
                break;
        }
    }
}

// The compression may have been done on a different os to the current one, so the paths may need to be corrected
function correctFilepath(path) {
    if (isWin) return path.split('/').join('\\');
    else return path.split('\\').join('/');
}

var logStageWaiting = false;
function logStage(str) {
    if (logStageWaiting) {console.timeEnd(' done');}
    process.stdout.write(str);
    console.time(' done');
    logStageWaiting = true;
}

//////////////////////////////////////// [END] ////////////////////////////////////////
<|endoftext|>


<commit_before>const { ipcRenderer, remote } = require('electron');

const domElements = {
  counter: document.getElementById('counter'),
  progress: document.getElementById('progress'),
  info: document.getElementById('info'),
  buttons: {
    pause: document.getElementById('pause'),
    restart: document.getElementById('restart'),
    muteOn: document.getElementById('mute-on'),
    muteOff: document.getElementById('mute-off'),
    exit: document.getElementById('exit'),
  },
};

const beepAudio = new Audio('../assets/audio/beep.wav');

// Object containing strings used in the counter
const text = {
  counter: {
    end: '0',
  },
  info: {
    active: 'Complete your activity',
    coolDown: 'Go to your next station',
    complete: 'Return to your original station',
  },
};

// Object containing values for duration, break duration and number of repeats
let settings = null;

// Flag indicating whether or not the program is currently in a paused state
let paused = false;

// If a single element is given, place it in an array
const ensureArray = arr => (Array.isArray(arr) ? arr : [arr]);

// Executes a function if it is given (bad design is used to make this a one-liner)
const optionalCallback = func => typeof func !== 'function' || func();

const skipTransition = (elements, action) => {
  optionalCallback(action);
  ensureArray(elements).forEach((el) => {
    el.classList.add('skip-transition');
    el.offsetHeight; // Trigger CSS reflow to flush changes
    el.classList.remove('skip-transition');
  });
};

const setProgressBar = () => {
  skipTransition(domElements.progress, () =>
    domElements.progress.classList.remove('expand'));
  domElements.progress.classList.add('expand');
};

const getFormattedTime = (seconds) => {
  // Get units of time (from seconds up to hours)
  let hh = parseInt(seconds / 3600, 10);
  let mm = parseInt((seconds % 3600) / 60, 10);
  let ss = parseInt(seconds % 60, 10);

  // Displaying or hiding units based on length of time (up to hours)
  // Hours
  if (hh > 0) hh = `${hh}:`;
  else hh = '';
  // Minutes
  if (hh === '' && mm <= 0) mm = '';
  else if (hh !== '' && mm < 10) mm = `0${mm}:`;
  else mm = `${mm}:`;
  // Seconds
  if (mm !== '' && ss < 10) ss = `0${ss}`;

  return `${hh}${mm}${ss}`;
};

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

// The pause-wait channel will return a value of false when the pause modal is
// closed, which we can set to the paused flag. When the flag is set, the
// Promise will be resolved.
const pauseWait = () => Promise.resolve(paused = ipcRenderer.sendSync('pause-wait'));

const countdown = (duration, view, onEachSecond) => {
  const action = () => {
    optionalCallback(onEachSecond);
    view.textContent = getFormattedTime(duration--);
  };

  return Promise.resolve((async () => {
    // We'll be decrementing duration each second in action()
    while (duration > 0) {
      if (paused) {
        await pauseWait();
      } else {
        action();
        await sleep(1000);
      }
    }

    // Check for pause before ending countdown
    if (paused) {
      await pauseWait();
    }
  })());
};

domElements.buttons.pause.addEventListener('click', () => {
  paused = true;
  ipcRenderer.send('pause');
});

domElements.buttons.restart.addEventListener('click', () => remote.getCurrentWebContents().send('start-timer', settings));

domElements.buttons.muteOn.addEventListener('click', () => {
  remote.getCurrentWebContents().setAudioMuted(true);
  domElements.buttons.muteOn.parentElement.style.display = 'none';
  domElements.buttons.muteOff.parentElement.style.display = '';
});

domElements.buttons.muteOff.addEventListener('click', () => {
  remote.getCurrentWebContents().setAudioMuted(false);
  domElements.buttons.muteOff.parentElement.style.display = 'none';
  domElements.buttons.muteOn.parentElement.style.display = '';
});

domElements.buttons.exit.addEventListener('click', () => ipcRenderer.send('exit'));

// Since the counter has no pointer events when counting, this will only
// trigger at the end when the end class is added to the counter, which
// enables pointer events.
domElements.counter.addEventListener('click', () => ipcRenderer.send('exit'));

ipcRenderer.on('start-timer', (evt, userSettings) => {
  const { duration, breakDuration, numRepeats } = userSettings;
  settings = userSettings;

  const resetTimer = () => {
    // Reset elements to their intended initial visibility
    domElements.buttons.restart.parentElement.style.display = 'none';
    domElements.buttons.pause.parentElement.style.display = '';
    // Remove all classes from the views
    document.body.classList = '';
    domElements.counter.classList = '';
    domElements.progress.classList = '';
    domElements.info.classList = '';
  };

  const durationCountdown = () => {
    domElements.counter.classList.remove('red');
    domElements.counter.classList.add('primary');
    domElements.progress.classList.remove('red');
    domElements.info.textContent = text.info.active;
    return countdown(duration, domElements.counter, setProgressBar);
  };

  const breakDurationCountdown = () => {
    domElements.counter.classList.remove('primary');
    domElements.counter.classList.add('red');
    domElements.progress.classList.add('red');
    domElements.info.textContent = text.info.coolDown;
    return countdown(breakDuration, domElements.counter, () => {
      beepAudio.play();
      setProgressBar();
    });
  };

  const endTimer = () => {
    // Setting end classes
    domElements.progress.classList.add('remove');
    skipTransition(domElements.counter, () =>
      domElements.counter.classList.remove('red'));
    domElements.counter.classList.add('end');
    // Setting end text to views
    domElements.counter.textContent = text.counter.end;
    domElements.info.textContent = text.info.complete;
    // Setting end visibility for Action Buttons
    domElements.buttons.pause.parentElement.style.display = 'none';
    domElements.buttons.restart.parentElement.style.display = '';
  };

  (async () => {
    resetTimer();
    // Start the timer: repeat for however many stations there are
    for (let i = 0; i < numRepeats; i++) {
      await durationCountdown();
      await breakDurationCountdown();
    }
    endTimer();
  })();
});
<commit_msg>Some more refactoring<commit_after>const { ipcRenderer, remote } = require('electron');

const domElements = {
  counter: document.getElementById('counter'),
  progress: document.getElementById('progress'),
  info: document.getElementById('info'),
  buttons: {
    pause: document.getElementById('pause'),
    restart: document.getElementById('restart'),
    muteOn: document.getElementById('mute-on'),
    muteOff: document.getElementById('mute-off'),
    exit: document.getElementById('exit'),
  },
};

const beepAudio = new Audio('../assets/audio/beep.wav');

// Object containing strings used in the counter
const text = {
  counter: {
    end: '0',
  },
  info: {
    active: 'Complete your activity',
    coolDown: 'Go to your next station',
    complete: 'Return to your original station',
  },
};

// Object containing values for duration, break duration and number of repeats
let settings = null;

// Flag indicating whether or not the program is currently in a paused state
let paused = false;

// If a single element is given, place it in an array
const ensureArray = arr => (Array.isArray(arr) ? arr : [arr]);

// Executes a function if it is given and if not then a noop function is executed
const optionalCallback = func => (typeof func === 'function' ? func : () => {})();

const skipTransition = (elements, action) => {
  optionalCallback(action);
  ensureArray(elements).forEach((el) => {
    el.classList.add('skip-transition');
    (() => el.offsetHeight)(); // Trigger CSS reflow to flush changes
    el.classList.remove('skip-transition');
  });
};

const setProgressBar = () => {
  skipTransition(domElements.progress, () =>
    domElements.progress.classList.remove('expand'));
  domElements.progress.classList.add('expand');
};

const getFormattedTime = (seconds) => {
  // Get units of time (from seconds up to hours)
  let hh = parseInt(seconds / 3600, 10);
  let mm = parseInt((seconds % 3600) / 60, 10);
  let ss = parseInt(seconds % 60, 10);

  // Displaying or hiding units based on length of time (up to hours)
  // Hours
  if (hh > 0) hh = `${hh}:`;
  else hh = '';
  // Minutes
  if (hh === '' && mm <= 0) mm = '';
  else if (hh !== '' && mm < 10) mm = `0${mm}:`;
  else mm = `${mm}:`;
  // Seconds
  if (mm !== '' && ss < 10) ss = `0${ss}`;

  return `${hh}${mm}${ss}`;
};

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

// The pause-wait channel will return a value of false when the pause modal is
// closed, which we can set to the paused flag. When the flag is set, the
// Promise will be resolved.
const pauseWait = () => Promise.resolve(paused = ipcRenderer.sendSync('pause-wait'));

const countdown = (duration, view, onEachSecond) => {
  const action = () => {
    optionalCallback(onEachSecond);
    view.textContent = getFormattedTime(duration--);
  };

  return Promise.resolve((async () => {
    // We'll be decrementing duration each second in action()
    while (duration > 0) {
      if (paused) {
        await pauseWait();
      } else {
        action();
        await sleep(1000);
      }
    }

    // Check for pause before ending countdown
    if (paused) {
      await pauseWait();
    }
  })());
};

domElements.buttons.pause.addEventListener('click', () => {
  paused = true;
  ipcRenderer.send('pause');
});

domElements.buttons.restart.addEventListener('click', () => remote.getCurrentWebContents().send('start-timer', settings));

domElements.buttons.muteOn.addEventListener('click', () => {
  remote.getCurrentWebContents().setAudioMuted(true);
  domElements.buttons.muteOn.parentElement.style.display = 'none';
  domElements.buttons.muteOff.parentElement.style.display = '';
});

domElements.buttons.muteOff.addEventListener('click', () => {
  remote.getCurrentWebContents().setAudioMuted(false);
  domElements.buttons.muteOff.parentElement.style.display = 'none';
  domElements.buttons.muteOn.parentElement.style.display = '';
});

domElements.buttons.exit.addEventListener('click', () => ipcRenderer.send('exit'));

// Since the counter has no pointer events when counting, this will only
// trigger at the end when the end class is added to the counter, which
// enables pointer events.
domElements.counter.addEventListener('click', () => ipcRenderer.send('exit'));

ipcRenderer.on('start-timer', (evt, userSettings) => {
  const { duration, breakDuration, numRepeats } = userSettings;
  settings = userSettings;

  const resetTimer = () => {
    // Reset elements to their intended initial visibility
    domElements.buttons.restart.parentElement.style.display = 'none';
    domElements.buttons.pause.parentElement.style.display = '';
    // Remove all classes from the views
    document.body.classList = '';
    domElements.counter.classList = '';
    domElements.progress.classList = '';
    domElements.info.classList = '';
  };

  const durationCountdown = () => {
    domElements.counter.classList.remove('red');
    domElements.counter.classList.add('primary');
    domElements.progress.classList.remove('red');
    domElements.info.textContent = text.info.active;
    return countdown(duration, domElements.counter, setProgressBar);
  };

  const breakDurationCountdown = () => {
    domElements.counter.classList.remove('primary');
    domElements.counter.classList.add('red');
    domElements.progress.classList.add('red');
    domElements.info.textContent = text.info.coolDown;
    return countdown(breakDuration, domElements.counter, () => {
      beepAudio.play();
      setProgressBar();
    });
  };

  const endTimer = () => {
    // Setting end classes
    domElements.progress.classList.add('remove');
    skipTransition(domElements.counter, () =>
      domElements.counter.classList.remove('red'));
    domElements.counter.classList.add('end');
    // Setting end text to views
    domElements.counter.textContent = text.counter.end;
    domElements.info.textContent = text.info.complete;
    // Setting end visibility for Action Buttons
    domElements.buttons.pause.parentElement.style.display = 'none';
    domElements.buttons.restart.parentElement.style.display = '';
  };

  const countdownAction = () => Promise.resolve(durationCountdown().then(breakDurationCountdown));

  (async () => {
    resetTimer();
    await Promise.all(
      Array(numRepeats).fill(countdownAction)
      .map(action => Promise.resolve().then(action)));
    endTimer();
  })();
});
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');
const util = require('util');

function loadAndParse(filePath) {
  const file = fs.readFileSync(filePath);
  return JSON.parse(file);
}

module.exports = (userBabelConfig, verbose) => {
  const baseBabelConfig = loadAndParse(path.resolve(__dirname, '..', './.babelrc'));
  const babelConfig = userBabelConfig ? Object.assign(baseBabelConfig, loadAndParse(path.resolve(userBabelConfig))) : baseBabelConfig;

  const hmrConfig = [
    'react-transform', {
      transforms: [
        {
          transform: 'react-transform-hmr',
          imports: ['react'],
          locals: ['module']
        },
        {
          transform: 'react-transform-catch-errors',
          imports: ['react', 'redbox-react']
        }
      ]
    }
  ];

  babelConfig.env.development.plugins.unshift(hmrConfig);

  const babelLoader = 'babel-loader?' + JSON.stringify(babelConfig);
  const jsLoaders = [babelLoader];

  // output configuration files if user wants verbosity
  if (verbose) {
    const utilOptions = {
      depth: 10,
      colors: true
    };

    console.log('\nBabel config:');
    console.log(util.inspect(babelConfig, utilOptions));
  }

  return jsLoaders;
};
<commit_msg>use cacheDirectory for faster builds<commit_after>const fs = require('fs');
const path = require('path');
const util = require('util');

function loadAndParse(filePath) {
  const file = fs.readFileSync(filePath);
  return JSON.parse(file);
}

module.exports = (userBabelConfig, verbose) => {
  const baseBabelConfig = loadAndParse(path.resolve(__dirname, '..', './.babelrc'));
  const babelConfig = userBabelConfig ? Object.assign(baseBabelConfig, loadAndParse(path.resolve(userBabelConfig))) : baseBabelConfig;

  const hmrConfig = [
    'react-transform', {
      transforms: [
        {
          transform: 'react-transform-hmr',
          imports: ['react'],
          locals: ['module']
        },
        {
          transform: 'react-transform-catch-errors',
          imports: ['react', 'redbox-react']
        }
      ]
    }
  ];

  babelConfig.env.development.plugins.unshift(hmrConfig);
  babelConfig.cacheDirectory = true;

  const babelLoader = 'babel-loader?' + JSON.stringify(babelConfig);
  const jsLoaders = [babelLoader];

  // output configuration files if user wants verbosity
  if (verbose) {
    const utilOptions = {
      depth: 10,
      colors: true
    };

    console.log('\nBabel config:');
    console.log(util.inspect(babelConfig, utilOptions));
  }

  return jsLoaders;
};
<|endoftext|>


<commit_before>'use strict';

// var config = require('../config');
// var twilio = require('twilio');
// var client = twilio(config.accountSid, config.authToken);
var fallbackCall = require('../lib/fallBackCall');

module.exports = function(app) {
  app.post('/StatusCallBack/:firstName/:lastName/:phoneNumber', function(req, res) {
    if ((req.body.AnsweredBy === 'machine') || (req.body.CallStatus === 'canceled' || req.body.CallStatus ===  'failed' || req.body.CallStatus ===  'no-answer')) {
      console.log('first call failed, time to try again.');
      res.type('text/xml');
      res.render('hangupMachine');
      fallbackCall.makeCall(req.params.firstName, req.params.lastName, req.params.phoneNumber);
    }
    console.log('call status is: ' + req.body.CallStatus);
  });
};
<commit_msg>call status wip<commit_after>'use strict';

// var config = require('../config');
// var twilio = require('twilio');
// var client = twilio(config.accountSid, config.authToken);
var fallbackCall = require('../lib/fallBackCall');

module.exports = function(app) {
  app.post('/StatusCallBack/:firstName/:lastName/:phoneNumber', function(req, res) {
    if (req.body.AnsweredBy === 'machine' || req.body.CallStatus === 'canceled' || req.body.CallStatus ===  'failed' || req.body.CallStatus ===  'no-answer') {
      console.log('first call failed, time to try again.');
      res.type('text/xml');
      res.render('hangupMachine');
      fallbackCall.makeCall(req.params.firstName, req.params.lastName, req.params.phoneNumber);
    }
    console.log('call status is: ' + req.body.CallStatus);
  });
};
<|endoftext|>


<commit_before>var $ = require('jquery');
//var objectFit = require('object-fit');
var video = require('./video.js');

module.exports = {
  init: function () {
    // Dont do anything if we have no media model
    this.Model = window.mediaModel;
    if (this.Model !== undefined && this.Model.length != 0) {
      this.cacheElements();
      this.bindEvents();

      video.init();

      //objectFit.polyfill({
      //  selector: '#media img',
      //  fittype: 'cover'
      //});
    }
  },
  cacheElements: function () {
    this.$thumbs = $('#objectthumbs .thumbnail');
    this.$activeMedia = $('#objectthumbs .thumbnail img.active');

    this.$mediaArea = $('#media');
    this.$mediaHolder = $('.media-holder', this.$mediaArea);
    this.$heroMedia = $('.hero-media', this.$mediaArea);

    this.$heroCaption = $('.caption-text', this.$mediaArea);
    this.$heroCreators = $('.creators', this.$mediaArea);
    this.$heroSources = $('.sources', this.$mediaArea);
    this.$heroCredit = $('.credit', this.$mediaArea);
    this.$heroRights = $('.rights-statement', this.$mediaArea);

    this.$fullscreenButton = $('button.fullscreen');

    this.defaultMaxHeight = $('img', this.$heroMedia).css('max-height');
    this.defaultMaxWidth = this.$mediaHolder.css('max-width');

    this.$previous = $('.previous');
    this.$next = $('.next');

    if (this.Model !== undefined && this.Model.length <= 1)
      this.$next.addClass('inactive');
  },
  bindEvents: function () {
    this.$thumbs.on('click', 'img', this.select.bind(this));
    this.$fullscreenButton.on('click', this.toggleFullscreen.bind(this));
    this.$previous.on('click', { direction: "previous" }, this.moveTo.bind(this));
    this.$next.on('click', { direction: "next" }, this.moveTo.bind(this));
  },
  select: function(e) {
    this.$activeMedia.removeClass('active');
    this.$activeMedia = $(e.target).addClass('active');
    this.switchMedia($(e.target).parent().index());
  },
  toggleFullscreen: function () {
    if (!this.$fullscreenButton.hasClass('disabled')) {
      var media = this.Model[this.$activeMedia.parent().index()];
      var heroMediaImage = $('img', this.$heroMedia);
      var windowWidth = $(window).width();
      var windowHeight = $(window).height();
      var fullscreenImageHeight = windowHeight - $('#media figcaption').height();

      // Default size
      var newHeroMediaImageSrc = media.Medium.Uri;

      if (this.$mediaArea.hasClass('expanded')) {
        // Contract media holder
        this.$mediaHolder.css({
          maxWidth: this.defaultMaxWidth,
          height: ''
        });

        // Reset hero media image max height
        heroMediaImage.css({
          maxHeight: this.defaultMaxHeight,
          height: ''
        });
      } else {
        // Scroll to top
        $('html, body').scrollTop(heroMediaImage.offset().top);

        // Expand media holder
        this.$mediaHolder.css({
          maxWidth: windowWidth,
          height: fullscreenImageHeight
        });

        // Allow hero media image to take up max height available
        heroMediaImage.css({
          maxHeight: fullscreenImageHeight
        });

        // Dont load large media if its even smaller than medium otherwise preload then display large image
        if (!(media.Large.Height < parseInt(this.defaultMaxHeight, 10))) {
          // Enlarge medium image temporarily to provide smooth transition
          heroMediaImage.height(Math.round((windowWidth / media.Large.Width) * media.Large.Height));
          newHeroMediaImageSrc = media.Large.Uri;
        }
      }

      this.preloadImages(newHeroMediaImageSrc).done(function () {
        heroMediaImage.attr('src', newHeroMediaImageSrc);
        heroMediaImage.css('height', '');
      });

      this.$mediaArea.toggleClass('expanded');
    }
  },
  moveTo: function(e) {
    var moveToIndex = this.$activeMedia.parent().index();

    if(e.data.direction == "previous")
      moveToIndex--;
    else if (e.data.direction == "next")
      moveToIndex++;

    if (moveToIndex >= 0 && moveToIndex < this.Model.length) {
      this.$activeMedia.removeClass('active');
      this.$activeMedia = $('img', this.$thumbs.get(moveToIndex)).addClass('active');

      this.switchMedia(moveToIndex);
    }
  },
  switchMedia: function (index) {
    var $this = this;
    this.$next.toggleClass('inactive', index == this.Model.length - 1);
    this.$previous.toggleClass('inactive', index == 0);

    var newMedia = this.Model[index];

    if (newMedia.$type.indexOf('ImageMedia') > 0) {
      // Handle Images
      var newHeroImage = $('<img/>', { 'alt': newMedia.AlternativeText });
      newHeroImage.css({
        maxHeight: $('img', this.$heroMedia).css('max-height')
      });
      if (this.$mediaArea.hasClass('expanded') && !(newMedia.Large.Height < parseInt(this.defaultMaxHeight, 10))) {
        newHeroImage.attr('src', newMedia.Large.Uri);
      } else {
        newHeroImage.attr('src', newMedia.Medium.Uri);
      }

      this.$fullscreenButton.removeClass('disabled');

      this.preloadImages(newHeroImage.attr('src')).done(function () {
        $this.$heroMedia.html(newHeroImage);
        $this.switchCaption(newMedia);
      });
    } else if (newMedia.$type.indexOf('VideoMedia') > 0) {
      // Handle Videos
      if (this.$mediaArea.hasClass('expanded')) {
        this.toggleFullscreen();
      }

      var newHeroVideo = $('<img/>', { 'alt': newMedia.Caption, 'src': newMedia.Medium.Uri, 'class': 'video' });

      this.$fullscreenButton.addClass('disabled');

      this.preloadImages(newHeroVideo.attr('src')).done(function () {
        $this.$heroMedia.html(newHeroVideo);
        $this.switchCaption(newMedia);
        video.init();
      });
    }
  },
  switchCaption: function(media) {
    (media.Caption) ? this.$heroCaption.html(media.Caption) : this.$heroCaption.empty();
    (media.Creators.length > 0) ? this.$heroCreators.text(media.Creators.join(', ')) : this.$heroCreators.empty();
    (media.Sources.length > 0) ? this.$heroSources.text('Source: ' + media.Sources.join(', ')) : this.$heroSources.empty();
    (media.Credit) ? this.$heroCredit.text('Credit: ' + media.Credit) : this.$heroCredit.empty();
    var rightsLabel = (media.$type.indexOf('ImageMedia') > 0) ? 'This image is: ' : 'This video is: ';
    (media.RightsStatement) ? this.$heroRights.text(rightsLabel + media.RightsStatement) : this.$heroRights.empty();
  },
  preloadImages: function(srcs) {
    var dfd = $.Deferred(),
      promises = [],
      img,
      l,
      p;

    if (!$.isArray(srcs)) {
      srcs = [srcs];
    }

    l = srcs.length;

    for (var i = 0; i < l; i++) {
      p = $.Deferred();
      img = $("<img />");

      img.load(p.resolve);
      img.error(p.resolve);

      promises.push(p);

      img.get(0).src = srcs[i];
    }

    $.whenArray(promises).done(dfd.resolve);
    return dfd.promise();
  },
};<commit_msg>small fix to media viewer<commit_after>var $ = require('jquery');
//var objectFit = require('object-fit');
var video = require('./video.js');

module.exports = {
  init: function () {
    // Dont do anything if we have no media model
    this.Model = window.mediaModel;
    if (this.Model !== undefined && this.Model.length != 0) {
      this.cacheElements();
      this.bindEvents();

      video.init();

      //objectFit.polyfill({
      //  selector: '#media img',
      //  fittype: 'cover'
      //});
    }
  },
  cacheElements: function () {
    this.$thumbs = $('#objectthumbs .thumbnail');
    this.$activeMedia = $('#objectthumbs .thumbnail img.active');

    this.$mediaArea = $('#media');
    this.$mediaHolder = $('.media-holder', this.$mediaArea);
    this.$heroMedia = $('.hero-media', this.$mediaArea);

    this.$heroCaption = $('.caption-text', this.$mediaArea);
    this.$heroCreators = $('.creators', this.$mediaArea);
    this.$heroSources = $('.sources', this.$mediaArea);
    this.$heroCredit = $('.credit', this.$mediaArea);
    this.$heroRights = $('.rights-statement', this.$mediaArea);

    this.$fullscreenButton = $('button.fullscreen');

    this.defaultMaxHeight = $('img', this.$heroMedia).css('max-height');
    this.defaultMaxWidth = this.$mediaHolder.css('max-width');

    this.$previous = $('.previous');
    this.$next = $('.next');

    if (this.Model !== undefined && this.Model.length <= 1)
      this.$next.addClass('inactive');
  },
  bindEvents: function () {
    this.$thumbs.on('click', 'img', this.select.bind(this));
    this.$fullscreenButton.on('click', this.toggleFullscreen.bind(this));
    this.$previous.on('click', { direction: "previous" }, this.moveTo.bind(this));
    this.$next.on('click', { direction: "next" }, this.moveTo.bind(this));
  },
  select: function(e) {
    this.$activeMedia.removeClass('active');
    this.$activeMedia = $(e.target).addClass('active');
    this.switchMedia($(e.target).parent().index());
  },
  toggleFullscreen: function () {
    if (!this.$fullscreenButton.hasClass('disabled')) {
      var media = this.Model[this.$activeMedia.parent().index()];
      var heroMediaImage = $('img', this.$heroMedia);
      var windowWidth = $(window).width();
      var windowHeight = $(window).height();
      var fullscreenImageHeight = windowHeight - $('#media figcaption').height();

      // Default size
      var newHeroMediaImageSrc = media.Medium.Uri;

      if (this.$mediaArea.hasClass('expanded')) {
        // Contract media holder
        this.$mediaHolder.css({
          maxWidth: this.defaultMaxWidth,
          height: ''
        });

        // Reset hero media image max height
        heroMediaImage.css({
          maxHeight: this.defaultMaxHeight,
          height: ''
        });
      } else {
        // Scroll to top
        $('html, body').scrollTop(heroMediaImage.offset().top);

        // Expand media holder
        this.$mediaHolder.css({
          maxWidth: windowWidth,
          height: fullscreenImageHeight
        });

        // Allow hero media image to take up max height available
        heroMediaImage.css({
          maxHeight: fullscreenImageHeight
        });

        // Dont load large media if its even smaller than medium otherwise preload then display large image
        if (!(media.Large.Height < parseInt(this.defaultMaxHeight, 10))) {
          // Enlarge medium image temporarily to provide smooth transition
          var heroMediaImageHeight = Math.round((windowWidth / media.Large.Width) * media.Large.Height);

          // Check to make sure we dont enlarge too much
          if (heroMediaImageHeight > media.Large.Height)
            heroMediaImageHeight = media.Large.Height;

          heroMediaImage.height(heroMediaImageHeight);
          newHeroMediaImageSrc = media.Large.Uri;
        }
      }

      this.preloadImages(newHeroMediaImageSrc).done(function () {
        heroMediaImage.attr('src', newHeroMediaImageSrc);
        heroMediaImage.css('height', '');
      });

      this.$mediaArea.toggleClass('expanded');
    }
  },
  moveTo: function(e) {
    var moveToIndex = this.$activeMedia.parent().index();

    if(e.data.direction == "previous")
      moveToIndex--;
    else if (e.data.direction == "next")
      moveToIndex++;

    if (moveToIndex >= 0 && moveToIndex < this.Model.length) {
      this.$activeMedia.removeClass('active');
      this.$activeMedia = $('img', this.$thumbs.get(moveToIndex)).addClass('active');

      this.switchMedia(moveToIndex);
    }
  },
  switchMedia: function (index) {
    var $this = this;
    this.$next.toggleClass('inactive', index == this.Model.length - 1);
    this.$previous.toggleClass('inactive', index == 0);

    var newMedia = this.Model[index];

    if (newMedia.$type.indexOf('ImageMedia') > 0) {
      // Handle Images
      var newHeroImage = $('<img/>', { 'alt': newMedia.AlternativeText });
      newHeroImage.css({
        maxHeight: $('img', this.$heroMedia).css('max-height')
      });
      if (this.$mediaArea.hasClass('expanded') && !(newMedia.Large.Height < parseInt(this.defaultMaxHeight, 10))) {
        newHeroImage.attr('src', newMedia.Large.Uri);
      } else {
        newHeroImage.attr('src', newMedia.Medium.Uri);
      }

      this.$fullscreenButton.removeClass('disabled');

      this.preloadImages(newHeroImage.attr('src')).done(function () {
        $this.$heroMedia.html(newHeroImage);
        $this.switchCaption(newMedia);
      });
    } else if (newMedia.$type.indexOf('VideoMedia') > 0) {
      // Handle Videos
      if (this.$mediaArea.hasClass('expanded')) {
        this.toggleFullscreen();
      }

      var newHeroVideo = $('<img/>', { 'alt': newMedia.Caption, 'src': newMedia.Medium.Uri, 'class': 'video' });

      this.$fullscreenButton.addClass('disabled');

      this.preloadImages(newHeroVideo.attr('src')).done(function () {
        $this.$heroMedia.html(newHeroVideo);
        $this.switchCaption(newMedia);
        video.init();
      });
    }
  },
  switchCaption: function(media) {
    (media.Caption) ? this.$heroCaption.html(media.Caption) : this.$heroCaption.empty();
    (media.Creators.length > 0) ? this.$heroCreators.text(media.Creators.join(', ')) : this.$heroCreators.empty();
    (media.Sources.length > 0) ? this.$heroSources.text('Source: ' + media.Sources.join(', ')) : this.$heroSources.empty();
    (media.Credit) ? this.$heroCredit.text('Credit: ' + media.Credit) : this.$heroCredit.empty();
    var rightsLabel = (media.$type.indexOf('ImageMedia') > 0) ? 'This image is: ' : 'This video is: ';
    (media.RightsStatement) ? this.$heroRights.text(rightsLabel + media.RightsStatement) : this.$heroRights.empty();
  },
  preloadImages: function(srcs) {
    var dfd = $.Deferred(),
      promises = [],
      img,
      l,
      p;

    if (!$.isArray(srcs)) {
      srcs = [srcs];
    }

    l = srcs.length;

    for (var i = 0; i < l; i++) {
      p = $.Deferred();
      img = $("<img />");

      img.load(p.resolve);
      img.error(p.resolve);

      promises.push(p);

      img.get(0).src = srcs[i];
    }

    $.whenArray(promises).done(dfd.resolve);
    return dfd.promise();
  },
};<|endoftext|>


<commit_before>// @flow
import undoable, { includeAction } from 'redux-undo'
import { COMBINE_MEDIA, REMOVE_MEDIA, RESET_MEDIA,
         SELECT_ALL, SELECT_NONE, TOGGLE_CHECKBOX,
         UPDATE_MEDIA, BULK_UPDATE_MEDIA, UPDATE_TEXT
       } from '../actions/media'

export const initialState = {}

function subtitles(state = initialState, action = {}) {
  const newState = {}
  switch (action.type) {
    case BULK_UPDATE_MEDIA:
      const bulkUpdated = {
        ...state
      }
      Object.keys(action.updatedMedia).forEach(key => {
        bulkUpdated[key] = { ...action.updatedMedia[key] }
      })
      return bulkUpdated
    case COMBINE_MEDIA:
      Object.keys(state).forEach(key => {
        const notNew = state[key].index !== action.updatedMedia.index
        const notToRemove = state[key].index !== action.toRemove
        if (notNew && notToRemove) {
          newState[key] = { ...state[key] }
        }
      })
      newState[action.updatedMedia.index] = action.updatedMedia
      return newState
    case REMOVE_MEDIA:
      return Object.keys(state)
        .reduce((remainingMedia, index) => {
          if (parseInt(index) !== action.index) {
            return {
              ...remainingMedia,
              [index]: {
                ...state[index]
              }
            }
          }
          return remainingMedia
        }, {})
    case RESET_MEDIA:
      return {
        ...action.media
      }
    case TOGGLE_CHECKBOX:
      return {
        ...state,
        [action.index]: {
          ...state[action.index],
          selected: !state[action.index].selected
        }
      }
    case SELECT_ALL:
      // Only select subtitle items that match the fitler
      return Object.values(state)
        .reduce((newState, subtitle) => {
          const filter = action.filter.trim()
          const matches = filter ? subtitle.text.toLowerCase().includes(action.filter.toLowerCase()) : true
          if (matches) {
            return {
              ...newState,
              [subtitle.id]: {
                ...subtitle,
                selected: true
              }
            }
          }
          return {
            ...newState
          }
        }, {})
    case SELECT_NONE:
      const noneSelected = {}
      Object.values(state).forEach(subtitle => {
        noneSelected[subtitle.index] = {
          ...subtitle,
          selected: false
        }
      })
      return noneSelected
    case UPDATE_MEDIA:
      return {
        ...state,
        [action.updatedMedia.index]: {
          ...action.updatedMedia
        }
      }
    case UPDATE_TEXT:
      return {
        ...state,
        [action.index]: {
          ...state[action.index],
          text: `${action.text}`
        }
      }
    default:
      return state
  }
}

const undoableSubtitles = undoable(subtitles, {
  filter: includeAction([BULK_UPDATE_MEDIA, COMBINE_MEDIA, UPDATE_MEDIA, REMOVE_MEDIA, UPDATE_TEXT]),
  limit: false
})

export default undoableSubtitles
<commit_msg>Fix select all functionality<commit_after>// @flow
import undoable, { includeAction } from 'redux-undo'
import { COMBINE_MEDIA, REMOVE_MEDIA, RESET_MEDIA,
         SELECT_ALL, SELECT_NONE, TOGGLE_CHECKBOX,
         UPDATE_MEDIA, BULK_UPDATE_MEDIA, UPDATE_TEXT
       } from '../actions/media'

export const initialState = {}

function subtitles(state = initialState, action = {}) {
  const newState = {}
  switch (action.type) {
    case BULK_UPDATE_MEDIA:
      const bulkUpdated = {
        ...state
      }
      Object.keys(action.updatedMedia).forEach(key => {
        bulkUpdated[key] = { ...action.updatedMedia[key] }
      })
      return bulkUpdated
    case COMBINE_MEDIA:
      Object.keys(state).forEach(key => {
        const notNew = state[key].index !== action.updatedMedia.index
        const notToRemove = state[key].index !== action.toRemove
        if (notNew && notToRemove) {
          newState[key] = { ...state[key] }
        }
      })
      newState[action.updatedMedia.index] = action.updatedMedia
      return newState
    case REMOVE_MEDIA:
      return Object.keys(state)
        .reduce((remainingMedia, index) => {
          if (parseInt(index) !== action.index) {
            return {
              ...remainingMedia,
              [index]: {
                ...state[index]
              }
            }
          }
          return remainingMedia
        }, {})
    case RESET_MEDIA:
      return {
        ...action.media
      }
    case TOGGLE_CHECKBOX:
      return {
        ...state,
        [action.index]: {
          ...state[action.index],
          selected: !state[action.index].selected
        }
      }
    case SELECT_ALL:
      // Only select subtitle items that match the fitler
      return Object.values(state)
        .reduce((newState, subtitle) => {
          const filter = action.filter.trim()
          const matches = filter ? subtitle.text.toLowerCase().includes(action.filter.toLowerCase()) : true
          if (matches) {
            return {
              ...newState,
              [subtitle.index]: {
                ...subtitle,
                selected: true
              }
            }
          }
          return {
            ...newState,
            [subtitle.index]: {
              ...subtitle,
              selected: false
            }
          }
        }, {})
    case SELECT_NONE:
      const noneSelected = {}
      Object.values(state).forEach(subtitle => {
        noneSelected[subtitle.index] = {
          ...subtitle,
          selected: false
        }
      })
      return noneSelected
    case UPDATE_MEDIA:
      return {
        ...state,
        [action.updatedMedia.index]: {
          ...action.updatedMedia
        }
      }
    case UPDATE_TEXT:
      return {
        ...state,
        [action.index]: {
          ...state[action.index],
          text: `${action.text}`
        }
      }
    default:
      return state
  }
}

const undoableSubtitles = undoable(subtitles, {
  filter: includeAction([BULK_UPDATE_MEDIA, COMBINE_MEDIA, UPDATE_MEDIA, REMOVE_MEDIA, UPDATE_TEXT]),
  limit: false
})

export default undoableSubtitles
<|endoftext|>


<commit_before>import Ember from 'ember';
import ResetScrollMixin from '../mixins/reset-scroll';
import Analytics from '../mixins/analytics';

export default Ember.Route.extend(Analytics, ResetScrollMixin, {
    queryParams: {
        queryString: {
            replace: true
        }
    },
    model() {
        return this
            .get('store')
            .findAll('preprint-provider', { reload: true })
            .then(result => result.filter(item => item.id !== 'osf'));
    },
});
<commit_msg>clear the url params on link-to transition:<commit_after>import Ember from 'ember';
import ResetScrollMixin from '../mixins/reset-scroll';
import Analytics from '../mixins/analytics';

export default Ember.Route.extend(Analytics, ResetScrollMixin, {
    queryParams: {
        queryString: {
            replace: true
        }
    },
    model() {
        return this
            .get('store')
            .findAll('preprint-provider', { reload: true })
            .then(result => result.filter(item => item.id !== 'osf'));
    },
    actions: {
        willTransition() {
            //Don't want to send clearFIlters action as that will trigger
            //behavior specifc to the clearFIlters button.
            let controller = this.controllerFor('discover');
            controller.set('subjectFilter', '');
            controller.set('providerFilter', '');
        }
    }
});
<|endoftext|>


<commit_before>var Path = require('path');
var Fs = require('fs');
var Ejs = require('ejs');
function readFromFile(filePath, result, callback){
	Fs.readFile(filePath, {encoding: "utf-8"}
		, function(err, data){
			if(err) throw err;
			var output = Ejs.render(data, result);
			output = output.split(/\n/);
			output.forEach(function(s){ s = s.replace(/^\t+/, '');});
			output = output.join('');
			callback(output);
		});
}
module.exports = {
	key: "text/html"
	, execute: function(exists, filePath, represent, result, callback){
		if(!exists){
			return callback(404);
		}
		readFromFile(filePath, result, function(output){
			if(result.request.url.indexOf('.phtml') > -1) return callback(output);
			result.output = output;
			var layout = represent.layoutRoot + result.resource.layout + ".html";
			Fs.exists(layout, function(exists){
				if(!exists) return callback(output);
				readFromFile(layout, result, function(output){
					callback(output);
				});
			});
		});
	}
};

<commit_msg>strip tabs and leading and trailing spaces<commit_after>var Path = require('path');
var Fs = require('fs');
var Ejs = require('ejs');
function readFromFile(filePath, result, callback){
	Fs.readFile(filePath, {encoding: "utf-8"}
		, function(err, data){
			if(err) throw err;
			var output = Ejs.render(data, result);
			output = output.split(/\n/);
			for(var i = 0; i < output.length; i++){
				output[i] = output[i].replace(/^\t+/, '');
				output[i] = output[i].replace(/^\s+/, '');
				output[i] = output[i].replace(/\s+$/, '');
			}
			output = output.join('');
			callback(output);
		});
}
module.exports = {
	key: "text/html"
	, execute: function(exists, filePath, represent, result, callback){
		if(!exists){
			return callback(404);
		}
		readFromFile(filePath, result, function(output){
			if(result.request.url.indexOf('.phtml') > -1) return callback(output);
			result.output = output;
			var layout = represent.layoutRoot + result.resource.layout + ".html";
			Fs.exists(layout, function(exists){
				if(!exists) return callback(output);
				readFromFile(layout, result, function(output){
					callback(output);
				});
			});
		});
	}
};

<|endoftext|>