<commit_before>/*
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * 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.
 */

'use strict';

const CDP = require('chrome-remote-interface');

const LOG_WAITING_FOR_FLAG = 'Rendertron: Waiting for rendering flag';
const LOG_RENDER_COMPLETE = 'Rendertron: Rendering complete';

class Renderer {
  _loadPage(client, url, options, config) {
    /**
     * Finds any meta tags setting the status code.
     * @return {?number} status code
     */
    function getStatusCode() {
      const metaElement = document.querySelector('meta[name="render:status_code"]');
      if (!metaElement) return;
      return parseInt(metaElement.getAttribute('content')) || undefined;
    }

    /**
     * Listens for changes to the 'renderComplete' flag.
     */
    function listenToCompletionFlag() {
      Object.defineProperty(window, 'renderComplete', {
        set: function(value) {
          if (value == false) {
            console.log(LOG_WAITING_FOR_FLAG);
          } else if (value == true) {
            console.log(LOG_RENDER_COMPLETE);
          }
        }
      });
    }

    return new Promise(async(resolve, reject) => {
      const {Page, Runtime, Network, Emulation, Console} = client;

      await Promise.all([
        Page.enable(),
        Runtime.enable(),
        Console.enable(),
        Network.enable(),
      ]);

      // Inject the Shady DOM polyfill if web components v1 is used, so we can
      // serialize the page.
      // TODO(samli): This needs to change to use the non-deprecated API after Chrome 61
      //   addScriptToEvaluateOnNewDocument({source: `ShadyDOM = {force: true}`})
      const shadyFlag = options['wc-inject-shadydom'];
      if (shadyFlag == '' || !!shadyFlag) {
        // Deprecated in Chrome 61.
        Page.addScriptToEvaluateOnLoad({scriptSource: `customElements.forcePolyfill = true`});
        Page.addScriptToEvaluateOnLoad({scriptSource: `ShadyDOM = {force: true}`});
        Page.addScriptToEvaluateOnLoad({scriptSource: `ShadyCSS = {shimcssproperties: true}`});
      }

      // Add hook for completion event.
      Page.addScriptToEvaluateOnLoad({scriptSource: `(${listenToCompletionFlag.toString()})()`});

      if (!!config['debug']) {
        Console.messageAdded((event) => {
          console.log(`[${event.message.level}] ${event.message.text}`);
        });
      }

      Page.navigate({url: url}).catch(reject);

      // Check that all outstanding network requests have finished loading.
      const outstandingRequests = new Map();
      let initialRequestId = undefined;
      let networkIdleTimeout = undefined;

      Network.requestWillBeSent((event) => {
        if (!initialRequestId)
          initialRequestId = event.requestId;
        outstandingRequests.set(event.requestId, event);
        if (networkIdleTimeout) {
          clearTimeout(networkIdleTimeout);
          networkIdleTimeout = undefined;
          networkIdle = false;
        }
      });

      let statusCode = 200;

      Network.responseReceived((event) => {
        if (event.requestId == initialRequestId &&
          event.response.status != 0 &&
          event.response.status != 304)
          statusCode = event.response.status;
      });

      Network.loadingFinished((event) => {
        outstandingRequests.delete(event.requestId);
        if (outstandingRequests.size == 0) {
          networkIdleTimeout = setTimeout(networkNowIdle, 500);
        }
      });

      Network.loadingFailed((event) => {
        if (event.requestId == initialRequestId) {
          reject({message: event.errorText});
        }
        outstandingRequests.delete(event.requestId);
        if (outstandingRequests.size == 0) {
          networkIdleTimeout = setTimeout(networkNowIdle, 500);
        }
      });

      // Ensure the page load event has fired.
      let pageLoadEventFired = false;
      Page.loadEventFired(() => {
        pageLoadEventFired = true;
        triggerTimeBudget();
      });

      // Called when the network has been quiet for 500ms.
      let networkIdle = false;
      let networkNowIdle = () => {
        networkIdle = true;
        triggerTimeBudget();
      };

      // Check if an explicit `renderComplete` flag is being used.
      let waitForFlag = false;
      Console.messageAdded((event) => {
        if (event.message.text === LOG_WAITING_FOR_FLAG) {
          waitForFlag = true;
        } else if (event.message.text === LOG_RENDER_COMPLETE) {
          pageReady();
        }
      });

      let timeBudgetStarted = false;
      let triggerTimeBudget = () => {
        if (timeBudgetStarted || waitForFlag)
          return;

        // Set a virtual time budget of 5 seconds for script/rendering. Once the page is
        // idle, the virtual time budget expires immediately.
        if (networkIdle && pageLoadEventFired) {
          Emulation.setVirtualTimePolicy({policy: 'advance', budget: 5000});
          timeBudgetStarted = true;
        }
      };

      let pageReady = async() => {
        let result = await Runtime.evaluate({expression: `(${getStatusCode.toString()})()`});

        // Original status codes which aren't 200 always return with that status code,
        // regardless of meta tags.
        if (statusCode == 200 && result.result.value)
          statusCode = result.result.value;

        resolve({status: statusCode || 200});
        pageReady = () => {};
        clearTimeout(maxTimeout);
      };

      Emulation.virtualTimeBudgetExpired(pageReady);

      // Set a hard limit of 10 seconds.
      let maxTimeout = setTimeout(() => {
        console.log(`10 second time budget limit reached.
          Attempted rendering: ${url}
          Page load event fired: ${pageLoadEventFired}
          Outstanding network requests: ${outstandingRequests.size}`);
        pageReady();
      }, 10000);
    });
  }

  serialize(url, options, config) {
    /**
     * Executed on the page after the page has loaded. Strips script and
     * import tags to prevent further loading of resources.
     */
    function stripPage() {
      const elements = document.querySelectorAll('script, link[rel=import]');
      elements.forEach((e) => e.remove());
    }

    /**
     * Injects a <base> tag which allows other resources to load. This
     * has no effect on serialised output, but allows it to verify render quality.
     * @param {string} url - Requested URL to set as the base.
     */
    function injectBaseHref(url) {
      const base = document.createElement('base');
      base.setAttribute('href', url);
      document.head.appendChild(base);
    }

    return new Promise(async(resolve, reject) => {
      const tab = await CDP.New({port: config.port});
      const client = await CDP({tab: tab, port: config.port});

      const {Runtime} = client;

      try {
        let renderResult = await this._loadPage(client, url, options, config);

        await Runtime.evaluate({expression: `(${stripPage.toString()})()`});
        await Runtime.evaluate({expression: `(${injectBaseHref.toString()})('${url}')`});

        let result = await Runtime.evaluate({expression: 'document.firstElementChild.outerHTML'});
        CDP.Close({id: client.target.id, port: config.port});
        resolve({
          status: renderResult.status,
          body: result.result.value});
      } catch (error) {
        reject(error);
      }
    });
  }

  captureScreenshot(url, options, config) {
    return new Promise(async(resolve, reject) => {
      const tab = await CDP.New({port: config.port});
      const client = await CDP({tab: tab, port: config.port});

      const {Animation, Page, Emulation} = client;

      // Accelerate global animation timeline so that loading animations
      // are hopefully complete by the time we take the screenshot.
      Animation.setPlaybackRate({playbackRate: 1000});

      const width = Math.min(2000, parseInt(options['width']) || 1000);
      const height = Math.min(2000, parseInt(options['height']) || 1000);
      await Emulation.setDeviceMetricsOverride({width: width, height: height, mobile: true, deviceScaleFactor: 1, fitWindow: false, screenWidth: width, screenHeight: height});
      await Emulation.setVisibleSize({width: width, height: height});

      try {
        await this._loadPage(client, url, options, config);
        let {data} = await Page.captureScreenshot({format: 'jpeg', quality: 60});

        CDP.Close({id: client.target.id, port: config.port});
        resolve(data);
      } catch (error) {
        reject(error);
      }
    });
  }
}

module.exports = new Renderer();
<commit_msg>Synchronously clear timeout & pageReady() (#130)<commit_after>/*
 * Copyright 2017 Google Inc. All rights reserved.
 *
 * 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.
 */

'use strict';

const CDP = require('chrome-remote-interface');

const LOG_WAITING_FOR_FLAG = 'Rendertron: Waiting for rendering flag';
const LOG_RENDER_COMPLETE = 'Rendertron: Rendering complete';

class Renderer {
  _loadPage(client, url, options, config) {
    /**
     * Finds any meta tags setting the status code.
     * @return {?number} status code
     */
    function getStatusCode() {
      const metaElement = document.querySelector('meta[name="render:status_code"]');
      if (!metaElement) return;
      return parseInt(metaElement.getAttribute('content')) || undefined;
    }

    /**
     * Listens for changes to the 'renderComplete' flag.
     */
    function listenToCompletionFlag() {
      Object.defineProperty(window, 'renderComplete', {
        set: function(value) {
          if (value == false) {
            console.log(LOG_WAITING_FOR_FLAG);
          } else if (value == true) {
            console.log(LOG_RENDER_COMPLETE);
          }
        }
      });
    }

    return new Promise(async(resolve, reject) => {
      const {Page, Runtime, Network, Emulation, Console} = client;

      await Promise.all([
        Page.enable(),
        Runtime.enable(),
        Console.enable(),
        Network.enable(),
      ]);

      // Inject the Shady DOM polyfill if web components v1 is used, so we can
      // serialize the page.
      // TODO(samli): This needs to change to use the non-deprecated API after Chrome 61
      //   addScriptToEvaluateOnNewDocument({source: `ShadyDOM = {force: true}`})
      const shadyFlag = options['wc-inject-shadydom'];
      if (shadyFlag == '' || !!shadyFlag) {
        // Deprecated in Chrome 61.
        Page.addScriptToEvaluateOnLoad({scriptSource: `customElements.forcePolyfill = true`});
        Page.addScriptToEvaluateOnLoad({scriptSource: `ShadyDOM = {force: true}`});
        Page.addScriptToEvaluateOnLoad({scriptSource: `ShadyCSS = {shimcssproperties: true}`});
      }

      // Add hook for completion event.
      Page.addScriptToEvaluateOnLoad({scriptSource: `(${listenToCompletionFlag.toString()})()`});

      if (!!config['debug']) {
        Console.messageAdded((event) => {
          console.log(`[${event.message.level}] ${event.message.text}`);
        });
      }

      Page.navigate({url: url}).catch(reject);

      // Check that all outstanding network requests have finished loading.
      const outstandingRequests = new Map();
      let initialRequestId = undefined;
      let networkIdleTimeout = undefined;

      Network.requestWillBeSent((event) => {
        if (!initialRequestId)
          initialRequestId = event.requestId;
        outstandingRequests.set(event.requestId, event);
        if (networkIdleTimeout) {
          clearTimeout(networkIdleTimeout);
          networkIdleTimeout = undefined;
          networkIdle = false;
        }
      });

      let statusCode = 200;

      Network.responseReceived((event) => {
        if (event.requestId == initialRequestId &&
          event.response.status != 0 &&
          event.response.status != 304)
          statusCode = event.response.status;
      });

      Network.loadingFinished((event) => {
        outstandingRequests.delete(event.requestId);
        if (outstandingRequests.size == 0) {
          networkIdleTimeout = setTimeout(networkNowIdle, 500);
        }
      });

      Network.loadingFailed((event) => {
        if (event.requestId == initialRequestId) {
          reject({message: event.errorText});
        }
        outstandingRequests.delete(event.requestId);
        if (outstandingRequests.size == 0) {
          networkIdleTimeout = setTimeout(networkNowIdle, 500);
        }
      });

      // Ensure the page load event has fired.
      let pageLoadEventFired = false;
      Page.loadEventFired(() => {
        pageLoadEventFired = true;
        triggerTimeBudget();
      });

      // Called when the network has been quiet for 500ms.
      let networkIdle = false;
      let networkNowIdle = () => {
        networkIdle = true;
        triggerTimeBudget();
      };

      // Check if an explicit `renderComplete` flag is being used.
      let waitForFlag = false;
      Console.messageAdded((event) => {
        if (event.message.text === LOG_WAITING_FOR_FLAG) {
          waitForFlag = true;
        } else if (event.message.text === LOG_RENDER_COMPLETE) {
          pageReady();
        }
      });

      let timeBudgetStarted = false;
      let triggerTimeBudget = () => {
        if (timeBudgetStarted || waitForFlag)
          return;

        // Set a virtual time budget of 5 seconds for script/rendering. Once the page is
        // idle, the virtual time budget expires immediately.
        if (networkIdle && pageLoadEventFired) {
          Emulation.setVirtualTimePolicy({policy: 'advance', budget: 5000});
          timeBudgetStarted = true;
        }
      };

      let pageReady = async function pageReadyFn() {
        // Synchronously clear timeout & pageReady() to prevent it from
        // being called again.
        pageReady = () => {};
        clearTimeout(maxTimeout);

        let result = await Runtime.evaluate({expression: `(${getStatusCode.toString()})()`});

        // Original status codes which aren't 200 always return with that status code,
        // regardless of meta tags.
        if (statusCode == 200 && result.result.value)
          statusCode = result.result.value;

        resolve({status: statusCode || 200});
      };

      Emulation.virtualTimeBudgetExpired(pageReady);

      // Set a hard limit of 10 seconds.
      let maxTimeout = setTimeout(() => {
        console.log(`10 second time budget limit reached.
          Attempted rendering: ${url}
          Page load event fired: ${pageLoadEventFired}
          Outstanding network requests: ${outstandingRequests.size}`);
        pageReady();
      }, 10000);
    });
  }

  serialize(url, options, config) {
    /**
     * Executed on the page after the page has loaded. Strips script and
     * import tags to prevent further loading of resources.
     */
    function stripPage() {
      const elements = document.querySelectorAll('script, link[rel=import]');
      elements.forEach((e) => e.remove());
    }

    /**
     * Injects a <base> tag which allows other resources to load. This
     * has no effect on serialised output, but allows it to verify render quality.
     * @param {string} url - Requested URL to set as the base.
     */
    function injectBaseHref(url) {
      const base = document.createElement('base');
      base.setAttribute('href', url);
      document.head.appendChild(base);
    }

    return new Promise(async(resolve, reject) => {
      const tab = await CDP.New({port: config.port});
      const client = await CDP({tab: tab, port: config.port});

      const {Runtime} = client;

      try {
        let renderResult = await this._loadPage(client, url, options, config);

        await Runtime.evaluate({expression: `(${stripPage.toString()})()`});
        await Runtime.evaluate({expression: `(${injectBaseHref.toString()})('${url}')`});

        let result = await Runtime.evaluate({expression: 'document.firstElementChild.outerHTML'});
        CDP.Close({id: client.target.id, port: config.port});
        resolve({
          status: renderResult.status,
          body: result.result.value});
      } catch (error) {
        reject(error);
      }
    });
  }

  captureScreenshot(url, options, config) {
    return new Promise(async(resolve, reject) => {
      const tab = await CDP.New({port: config.port});
      const client = await CDP({tab: tab, port: config.port});

      const {Animation, Page, Emulation} = client;

      // Accelerate global animation timeline so that loading animations
      // are hopefully complete by the time we take the screenshot.
      Animation.setPlaybackRate({playbackRate: 1000});

      const width = Math.min(2000, parseInt(options['width']) || 1000);
      const height = Math.min(2000, parseInt(options['height']) || 1000);
      await Emulation.setDeviceMetricsOverride({width: width, height: height, mobile: true, deviceScaleFactor: 1, fitWindow: false, screenWidth: width, screenHeight: height});
      await Emulation.setVisibleSize({width: width, height: height});

      try {
        await this._loadPage(client, url, options, config);
        let {data} = await Page.captureScreenshot({format: 'jpeg', quality: 60});

        CDP.Close({id: client.target.id, port: config.port});
        resolve(data);
      } catch (error) {
        reject(error);
      }
    });
  }
}

module.exports = new Renderer();
<|endoftext|>


<commit_before>(function () {

    $(document).on('evolutionStepFinished', {}, function (event, timeTaken, generation, cellsAlive) {
        $('#id_stats_step_evolution_ms').text(timeTaken.toFixed(0));
        $('#id_stats_generation').text(generation);
        $('#id_stats_cells_alive').text(cellsAlive);
    });

    $(document).on('updateGridFinished', {}, function (event, timeTaken) {
        $('#id_stats_render_grid_ms').text(timeTaken.toFixed(0));
    });

    // field configuration
    var itemSize = 15,
        cellSize = itemSize - 1,
        margin = {
            top: 0,
            left: 0,
            right: 0,
            bottom: 0
        },
        stepTime = 100;
    // balance number of cells / width to fill screen
    var rows = Math.floor(window.height / itemSize);
    var cols = Math.floor(window.width / itemSize);

    function initGrid(torus) {
        /*
        var yAxisScale = d3.scale.linear()
                .range([0, itemSize * (torus.rows - 1)])
                .domain([1, torus.rows]),
            yAxis = d3.svg.axis()
                .orient('left')
                .ticks(torus.rows)
                .scale(yAxisScale);

        var xAxisScale = d3.scale.linear()
                .range([0, itemSize * (torus.cols - 1)])
                .domain([1, torus.cols]),
            xAxis = d3.svg.axis()
                .orient('top')
                .ticks(torus.cols)
                .scale(xAxisScale);
        */

        var width = (torus.cols * itemSize) + margin.right + margin.left;
        var height = (torus.rows * itemSize) + margin.top + margin.bottom;

        var svg = d3.select('[role="fieldmap"]');

        /*
        // Y axis
        svg.append('g')
            .attr('transform',
                'translate(' + (margin.left + 5 ) + ',' + (margin.top + cellSize / 2) + ')')
            .attr('class', 'axis')
            .call(yAxis);

        // X axis
        svg.append('g')
            .attr('transform',
                'translate(' + (margin.left + cellSize / 2 ) + ',' + (margin.top + 5) + ')')
            .attr('class', 'axis')
            .call(xAxis);
        */

        svg.append('rect') // this is background for all cells
            .attr('transform',
                'translate(' + (margin.left - 1) + ',' + (margin.top - 1) + ')')
            .attr('width', width - margin.left - margin.right + 1)
            .attr('height', height - margin.top - margin.bottom + 1)
            .attr('fill', '#ccc');

        var fieldmap = svg
            .attr('width', width)
            .attr('height', height)
            .append('g') // group for all cells
            .attr('role', 'field')
            .attr('transform',
                'translate(' + (margin.left) + ',' + (margin.top) + ')');

        var grid = torus.toArray();

        // add rows
        var rows = fieldmap.selectAll('g')
            .data(grid)
            .enter()
            .append('g')
            // shift each row from top by its number
            .attr('transform', function (d, i) {
                return 'translate(0, ' + itemSize * i + ')';
            });

        // for each row draw cells
        rows.selectAll()
            .data(function (d) {
                return d;
            })
            .enter()
            .append('rect')
            .attr('type', 'cell')
            .attr('width', cellSize)
            .attr('height', cellSize)
            .attr('fill', '#ffffff')
            .attr('data-value', function (d) {
                return d;
            })
            .attr('x', function (d, i) {
                return itemSize * i;
            });

        cells = svg.selectAll('rect[type="cell"]');
        cells.on('click', function (d, i) {
            rect = d3.select(cells[0][i]);
            toggleCell(rect, i);
        });
        cells.on('mouseover', function (d, i) {
            rect = d3.select(cells[0][i]);
            rect.attr('opacity', .35);
        });
        cells.on('mouseout', function (d, i) {
            rect = d3.select(cells[0][i]);
            rect.attr('opacity', 1)
        });

    }

    function toggleCell(rect, i) {
        var row = Math.floor(i / lifeTorus.cols);
        var col = i % lifeTorus.cols;

        var currentValue = lifeTorus.get(row, col);
        var newValue = currentValue == 1 ? 0 : 1;
        rect.attr('fill', newValue == 1 ? '#000' : '#fff');
        lifeTorus.set(row, col, newValue);
    }

    function colorizeGrid() {
        var svg = d3.select('[role="fieldmap"]');
        var items = svg.selectAll('rect[type="cell"]')
            //.transition()
            //.duration(stepTime / 2)
            .attr('fill', function (d) {
                return d == 1 ? '#000' : '#fff';
            });
    }

    function updateGrid(torus) {
        var startTime = performance.now();
        var grid = torus.toArray();
        var gridItems = d3.merge(grid);

        var svg = d3.select('[role="fieldmap"]');
        var items = svg.selectAll('rect[type="cell"]')
            .data(gridItems)
            .attr('data-value', function (d) {
                return d;
            });
        colorizeGrid();

        // metrics
        var endTime = performance.now();
        $(document).trigger('updateGridFinished', [endTime - startTime]);

    }

    function stepEvolution(currentTorus, checkOnly) {
        var cellsSeen = 0;
        var startTime = performance.now();
        var cellsAlive = 0;
        var nextTorus = new TorusArray(currentTorus.rows, currentTorus.cols);

        var nextCheckOnly = [];

        function indexToString(i, j) {
            // oh, shi~
            return i.toString() + "," + j.toString();
        }

        function stringToIndex(stringIndex) {
            // oh, shi~ 2
            return stringIndex.split(",").map(function(n) {
                return parseInt(n, 10);
            })
        }

        function markForCheck(i, j, neighbors) {
//            if(nextCheckOnly.indexOf([i, j]) == -1) {
            var candidates = neighbors.map(function (n) {
                return indexToString(n[0], n[1]);
            });
            candidates.push(indexToString(i, j));
            for(var k = 0; k < candidates.length; k++) {
                if(nextCheckOnly.indexOf(candidates[k]) == -1) {
                    nextCheckOnly.push(candidates[k]);
                }
            }
            //nextCheckOnly.push(indexToString(i, j));

//            }
//            nextCheckOnly.push.apply(nextCheckOnly, neighbors);
//            console.log("after push", nextCheckOnly.length);
//            nextCheckOnly.push.apply(nextCheckOnly,

//            );
//            console.log("marked", neighbors.length + 1);
        }

        function checkCell(i, j) {
            var neighbors = currentTorus.neighbors(i, j);
            var neighborsState = neighbors.map(function (currValue) {
                return currentTorus.get(currValue[0], currValue[1]);
            });

            var population = neighborsState.reduce(function (prevValue, currValue) {
                return prevValue + currValue;
            });

            // new born
            if (currentTorus.get(i, j) == 0 && population == 3) {
                nextTorus.set(i, j, 1);
                markForCheck(i, j, neighbors);
                cellsAlive++;
            }

            if (currentTorus.get(i, j) == 1) {
                // loneliness or overpopulation
                if (population < 2 || population > 3) {
                    nextTorus.set(i, j, 0);
                }
                else {
                    // lucky cell
                    nextTorus.set(i, j, 1);
                    markForCheck(i, j, neighbors);
                    cellsAlive++;
                }
            }
            cellsSeen++;
        }

        if(typeof checkOnly == "undefined") {
            // scan whole field
            for (var i = 0; i < currentTorus.rows; i++) {
                for (var j = 0; j < currentTorus.cols; j++) {
                    checkCell(i, j);
                }
            }
        }
        else {
            // scan only marked
            for(var k = 0; k < checkOnly.length; k++) {
                var cell = stringToIndex(checkOnly[k]);
                checkCell(cell[0], cell[1]);
            }
        }
        console.log("cells seen", cellsSeen, "cellsAlive", cellsAlive, "next round to see", nextCheckOnly.length);

        // metrics
        var endTime = performance.now();
        generation++;
        $(document).trigger('evolutionStepFinished', [
                endTime - startTime,
            generation,
            cellsAlive
        ]);

        console.log("next check only", nextCheckOnly.length);
        return [nextTorus, nextCheckOnly];
    }

    // globals
    TorusArray = modules.TorusArray;

    var generation = 0,
        gameTimer = 0;

    var lifeTorus = new TorusArray(rows, cols);
    var checkOnly;

    initGrid(lifeTorus);
    updateGrid(lifeTorus);

    $('#id_toggle_btn').click(function () {
        var btn = $('#id_toggle_btn');
        var btn_span = $('#id_toggle_btn > span');
        if (btn.hasClass('btn-success')) {
            gameTimer = setInterval(function () {
                var result = stepEvolution(lifeTorus, checkOnly);
                lifeTorus = result[0];
                checkOnly = result[1];
                updateGrid(lifeTorus);
            }, stepTime);

            btn.removeClass('btn-success').addClass('btn-danger');
            btn_span.removeClass("glyphicon-play").addClass("glyphicon-pause");
        }
        else {
            clearTimeout(gameTimer);

            btn.removeClass('btn-danger').addClass('btn-success');
            btn_span.removeClass("glyphicon-pause").addClass("glyphicon-play");
        }
    });

    $(".field-presets-container a").click(function (event) {
        var item = $(event.target);
        var preset = presets[item.attr('data-group')][item.attr('data-key')];
        preset.zfill2d(lifeTorus.rows, lifeTorus.cols);
        lifeTorus.setArray(preset);
        updateGrid(lifeTorus);
    });

    $("#id_preset_empty").click(function (event) {
        lifeTorus.setArray(
            new TorusArray(lifeTorus.rows, lifeTorus.cols).toArray()
        );
        updateGrid(lifeTorus);
    });

})();
<commit_msg>fix to use absolute/relative index<commit_after>(function () {

    $(document).on('evolutionStepFinished', {}, function (event, timeTaken, generation, cellsAlive) {
        $('#id_stats_step_evolution_ms').text(timeTaken.toFixed(0));
        $('#id_stats_generation').text(generation);
        $('#id_stats_cells_alive').text(cellsAlive);
    });

    $(document).on('updateGridFinished', {}, function (event, timeTaken) {
        $('#id_stats_render_grid_ms').text(timeTaken.toFixed(0));
    });

    // field configuration
    var itemSize = 15,
        cellSize = itemSize - 1,
        margin = {
            top: 0,
            left: 0,
            right: 0,
            bottom: 0
        },
        stepTime = 100;
    // balance number of cells / width to fill screen
    var rows = Math.floor(window.height / itemSize);
    var cols = Math.floor(window.width / itemSize);

    function initGrid(torus) {
        /*
        var yAxisScale = d3.scale.linear()
                .range([0, itemSize * (torus.rows - 1)])
                .domain([1, torus.rows]),
            yAxis = d3.svg.axis()
                .orient('left')
                .ticks(torus.rows)
                .scale(yAxisScale);

        var xAxisScale = d3.scale.linear()
                .range([0, itemSize * (torus.cols - 1)])
                .domain([1, torus.cols]),
            xAxis = d3.svg.axis()
                .orient('top')
                .ticks(torus.cols)
                .scale(xAxisScale);
        */

        var width = (torus.cols * itemSize) + margin.right + margin.left;
        var height = (torus.rows * itemSize) + margin.top + margin.bottom;

        var svg = d3.select('[role="fieldmap"]');

        /*
        // Y axis
        svg.append('g')
            .attr('transform',
                'translate(' + (margin.left + 5 ) + ',' + (margin.top + cellSize / 2) + ')')
            .attr('class', 'axis')
            .call(yAxis);

        // X axis
        svg.append('g')
            .attr('transform',
                'translate(' + (margin.left + cellSize / 2 ) + ',' + (margin.top + 5) + ')')
            .attr('class', 'axis')
            .call(xAxis);
        */

        svg.append('rect') // this is background for all cells
            .attr('transform',
                'translate(' + (margin.left - 1) + ',' + (margin.top - 1) + ')')
            .attr('width', width - margin.left - margin.right + 1)
            .attr('height', height - margin.top - margin.bottom + 1)
            .attr('fill', '#ccc');

        var fieldmap = svg
            .attr('width', width)
            .attr('height', height)
            .append('g') // group for all cells
            .attr('role', 'field')
            .attr('transform',
                'translate(' + (margin.left) + ',' + (margin.top) + ')');

        var grid = torus.toArray();

        // add rows
        var rows = fieldmap.selectAll('g')
            .data(grid)
            .enter()
            .append('g')
            // shift each row from top by its number
            .attr('transform', function (d, i) {
                return 'translate(0, ' + itemSize * i + ')';
            });

        // for each row draw cells
        rows.selectAll()
            .data(function (d) {
                return d;
            })
            .enter()
            .append('rect')
            .attr('type', 'cell')
            .attr('width', cellSize)
            .attr('height', cellSize)
            .attr('fill', '#ffffff')
            .attr('data-value', function (d) {
                return d;
            })
            .attr('x', function (d, i) {
                return itemSize * i;
            });

        cells = svg.selectAll('rect[type="cell"]');
        cells.on('click', function (d, i) {
            rect = d3.select(cells[0][i]);
            toggleCell(rect, i);
        });
        cells.on('mouseover', function (d, i) {
            rect = d3.select(cells[0][i]);
            rect.attr('opacity', .35);
        });
        cells.on('mouseout', function (d, i) {
            rect = d3.select(cells[0][i]);
            rect.attr('opacity', 1)
        });

    }

    function toggleCell(rect, i) {
        var row = Math.floor(i / lifeTorus.cols);
        var col = i % lifeTorus.cols;

        var currentValue = lifeTorus.get(row, col);
        var newValue = currentValue == 1 ? 0 : 1;
        rect.attr('fill', newValue == 1 ? '#000' : '#fff');
        lifeTorus.set(row, col, newValue);
    }

    function colorizeGrid() {
        var svg = d3.select('[role="fieldmap"]');
        var items = svg.selectAll('rect[type="cell"]')
            //.transition()
            //.duration(stepTime / 2)
            .attr('fill', function (d) {
                return d == 1 ? '#000' : '#fff';
            });
    }

    function updateGrid(torus) {
        var startTime = performance.now();
        var grid = torus.toArray();
        var gridItems = d3.merge(grid);

        var svg = d3.select('[role="fieldmap"]');
        var items = svg.selectAll('rect[type="cell"]')
            .data(gridItems)
            .attr('data-value', function (d) {
                return d;
            });
        colorizeGrid();

        // metrics
        var endTime = performance.now();
        $(document).trigger('updateGridFinished', [endTime - startTime]);

    }

    function stepEvolution(currentTorus, scanCells) {
        var cellsSeen = 0;
        var startTime = performance.now();
        var cellsAlive = 0;
        var nextTorus = new TorusArray(currentTorus.rows, currentTorus.cols);

        var nextScanCells = [];
        function markForScan(i, j, neighbors) {
            var candidates = neighbors.map(function (n) {
                return currentTorus.absoluteIndex(n[0], n[1]);
            });
            candidates.push(currentTorus.absoluteIndex(i, j));
            for(var k = 0; k < candidates.length; k++) {
                if(nextScanCells.indexOf(candidates[k]) == -1) {
                    nextScanCells.push(candidates[k]);
                }
            }
        }

        function checkCell(i, j) {
            var neighbors = currentTorus.neighbors(i, j);
            var neighborsState = neighbors.map(function (currValue) {
                return currentTorus.get(currValue[0], currValue[1]);
            });

            var population = neighborsState.reduce(function (prevValue, currValue) {
                return prevValue + currValue;
            });

            // new born
            if (currentTorus.get(i, j) == 0 && population == 3) {
                nextTorus.set(i, j, 1);
                markForScan(i, j, neighbors);
                cellsAlive++;
            }

            if (currentTorus.get(i, j) == 1) {
                // loneliness or overpopulation
                if (population < 2 || population > 3) {
                    nextTorus.set(i, j, 0);
                }
                else {
                    // lucky cell
                    nextTorus.set(i, j, 1);
                    markForScan(i, j, neighbors);
                    cellsAlive++;
                }
            }
            cellsSeen++;
        }

        if(typeof scanCells == "undefined") {
            // scan whole field
            for (var i = 0; i < currentTorus.rows; i++) {
                for (var j = 0; j < currentTorus.cols; j++) {
                    checkCell(i, j);
                }
            }
        }
        else {
            // scan only marked
            for(var k = 0; k < scanCells.length; k++) {
                var cell = currentTorus.arrayIndex(scanCells[k]);
                checkCell(cell[0], cell[1]);
            }
        }
        console.log("cells seen", cellsSeen, "cellsAlive", cellsAlive, "next round to see", nextScanCells.length);

        // metrics
        var endTime = performance.now();
        generation++;
        $(document).trigger('evolutionStepFinished', [
                endTime - startTime,
            generation,
            cellsAlive
        ]);

        return [nextTorus, nextScanCells];
    }

    // globals
    TorusArray = modules.TorusArray;

    var generation = 0,
        gameTimer = 0;

    var lifeTorus = new TorusArray(rows, cols);
    var scanCells;

    initGrid(lifeTorus);
    updateGrid(lifeTorus);

    $('#id_toggle_btn').click(function () {
        var btn = $('#id_toggle_btn');
        var btn_span = $('#id_toggle_btn > span');
        if (btn.hasClass('btn-success')) {
            gameTimer = setInterval(function () {
                var result = stepEvolution(lifeTorus, scanCells);
                lifeTorus = result[0];
                scanCells = result[1];
                updateGrid(lifeTorus);
            }, stepTime);

            btn.removeClass('btn-success').addClass('btn-danger');
            btn_span.removeClass("glyphicon-play").addClass("glyphicon-pause");
        }
        else {
            clearTimeout(gameTimer);

            btn.removeClass('btn-danger').addClass('btn-success');
            btn_span.removeClass("glyphicon-pause").addClass("glyphicon-play");
        }
    });

    $(".field-presets-container a").click(function (event) {
        var item = $(event.target);
        var preset = presets[item.attr('data-group')][item.attr('data-key')];
        preset.zfill2d(lifeTorus.rows, lifeTorus.cols);
        lifeTorus.setArray(preset);
        updateGrid(lifeTorus);
    });

    $("#id_preset_empty").click(function (event) {
        lifeTorus.setArray(
            new TorusArray(lifeTorus.rows, lifeTorus.cols).toArray()
        );
        updateGrid(lifeTorus);
    });

})();
<|endoftext|>


<commit_before>import ora from 'ora'

import getRepos from './funcs/getRepos'
import parse2md from './funcs/parse2md'
import save2file from './funcs/save2file'

const spinner = ora({
  text: 'Fetching data for you..',
  color: 'magenta'
})

export default ({ username, path, type, token }) => {
  spinner.start()
  // Ret repos
  getRepos({ username, type, token, spinner })
    .then(repos => {
      const text2save = [
        // Heading
        `# Repos ${type} by @${username}\n\n`,
        // Details
        `> Auto-generated list of repositories using [repos2md](https://github.com/frenchbread/repos2md).\n\n`,
        // Body
        parse2md(repos),
        // Footer
        `> <> with <3`
      ].join('')

      // Save to file
      save2file({ path, text2save })
        .then(res => spinner.succeed(`File saved to ${path}`))
        .catch(err => spinner.fail(err.message))
    })
}
<commit_msg>Add error handling<commit_after>import ora from 'ora'

import getRepos from './funcs/getRepos'
import parse2md from './funcs/parse2md'
import save2file from './funcs/save2file'

const spinner = ora({
  text: 'Fetching data for you..',
  color: 'magenta'
})

export default ({ username, path, type, token }) => {
  spinner.start()
  // Ret repos
  getRepos({ username, type, token, spinner })
    .then(repos => {
      const text2save = [
        // Heading
        `# Repos ${type} by @${username}\n\n`,
        // Details
        `> Auto-generated list of repositories using [repos2md](https://github.com/frenchbread/repos2md).\n\n`,
        // Body
        parse2md(repos),
        // Footer
        `> <> with <3`
      ].join('')

      // Save to file
      save2file({ path, text2save })
        .then(res => spinner.succeed(`File saved to ${path}`))
        .catch(err => spinner.fail(err.message))
    })
    .catch(err => spinner.fail(err.message))
}
<|endoftext|>


<commit_before>import React, {Component} from 'react'
import PropTypes from 'prop-types'
// import SearchInput from 'components/SearchInput'
import {connect} from 'react-redux'
import {fetchUsers, fetchMoreUsers} from 'redux/actions/UserActions'
import ProfileCard from '../ProfileCard'
import LoadMoreButton from 'components/LoadMoreButton'
import Loading from 'components/Loading'
import {addEvent, removeEvent} from 'helpers/listenerHelpers'

@connect(
  state => ({
    chatMode: state.ChatReducer.chatMode,
    userId: state.UserReducer.userId,
    profiles: state.UserReducer.profiles,
    loadMoreButton: state.UserReducer.loadMoreButton
  }),
  {fetchUsers, fetchMoreUsers}
)
export default class People extends Component {
  static propTypes = {
    chatMode: PropTypes.bool,
    fetchUsers: PropTypes.func,
    fetchMoreUsers: PropTypes.func,
    userId: PropTypes.number,
    profiles: PropTypes.array,
    loadMoreButton: PropTypes.bool
  }

  constructor() {
    super()
    this.state = {
      searchText: '',
      loading: false,
      loaded: false
    }
    this.loadMoreProfiles = this.loadMoreProfiles.bind(this)
    this.onPeopleSearch = this.onPeopleSearch.bind(this)
    this.onScroll = this.onScroll.bind(this)
    this.onSelect = this.onSelect.bind(this)
  }

  componentDidMount() {
    const {fetchUsers} = this.props
    addEvent(window, 'scroll', this.onScroll)
    return fetchUsers().then(
      () => this.setState({loaded: true})
    )
  }

  componentWillUnmount() {
    removeEvent(window, 'scroll', this.onScroll)
  }

  render() {
    const {userId, profiles, loadMoreButton} = this.props
    const {loading, loaded} = this.state
    return (

        {/* <SearchInput
          placeholder="Search for users"
          onChange={this.onPeopleSearch}
          value={searchText}
          searchResults={[]}
          renderItemLabel={
            item => item
          }
          onClickOutSide={() => {
            this.setState({searchText: ''})
          }}
          onSelect={this.onSelect}
        /> */}

          {!loaded &&
            <Loading text="Loading Users..." />
          }
          {loaded &&
            profiles.map(
              profile => <ProfileCard expandable key={profile.id} userId={userId} profile={profile} />
            )
          }

        {loadMoreButton && <LoadMoreButton onClick={this.loadMoreProfiles} loading={loading} />}

    )
  }

  loadMoreProfiles() {
    const {fetchMoreUsers, profiles} = this.props
    const {loading} = this.state
    if (!loading) {
      this.setState({loading: true})
      return fetchMoreUsers(profiles.map(profile => profile.id)).then(
        () => this.setState({loading: false})
      )
    }
  }

  onPeopleSearch(event) {
    this.setState({searchText: event.target.value})
  }

  onScroll() {
    let {chatMode, profiles} = this.props
    if (!chatMode && profiles.length > 0) {
      this.setState({scrollPosition: document.body.scrollTop})
      if (this.state.scrollPosition >= (document.body.scrollHeight - window.innerHeight) * 0.7) {
        this.loadMoreProfiles()
      }
    }
  }

  onSelect() {
    console.log('selected')
  }
}
<commit_msg>load more button doesnt show while loading for people section<commit_after>import React, {Component} from 'react'
import PropTypes from 'prop-types'
// import SearchInput from 'components/SearchInput'
import {connect} from 'react-redux'
import {fetchUsers, fetchMoreUsers} from 'redux/actions/UserActions'
import ProfileCard from '../ProfileCard'
import LoadMoreButton from 'components/LoadMoreButton'
import Loading from 'components/Loading'
import {addEvent, removeEvent} from 'helpers/listenerHelpers'

@connect(
  state => ({
    chatMode: state.ChatReducer.chatMode,
    userId: state.UserReducer.userId,
    profiles: state.UserReducer.profiles,
    loadMoreButton: state.UserReducer.loadMoreButton
  }),
  {fetchUsers, fetchMoreUsers}
)
export default class People extends Component {
  static propTypes = {
    chatMode: PropTypes.bool,
    fetchUsers: PropTypes.func,
    fetchMoreUsers: PropTypes.func,
    userId: PropTypes.number,
    profiles: PropTypes.array,
    loadMoreButton: PropTypes.bool
  }

  constructor() {
    super()
    this.state = {
      searchText: '',
      loading: false,
      loaded: false
    }
    this.loadMoreProfiles = this.loadMoreProfiles.bind(this)
    this.onPeopleSearch = this.onPeopleSearch.bind(this)
    this.onScroll = this.onScroll.bind(this)
    this.onSelect = this.onSelect.bind(this)
  }

  componentDidMount() {
    const {fetchUsers} = this.props
    addEvent(window, 'scroll', this.onScroll)
    return fetchUsers().then(
      () => this.setState({loaded: true})
    )
  }

  componentWillUnmount() {
    removeEvent(window, 'scroll', this.onScroll)
  }

  render() {
    const {userId, profiles, loadMoreButton} = this.props
    const {loading, loaded} = this.state
    return (

        {/* <SearchInput
          placeholder="Search for users"
          onChange={this.onPeopleSearch}
          value={searchText}
          searchResults={[]}
          renderItemLabel={
            item => item
          }
          onClickOutSide={() => {
            this.setState({searchText: ''})
          }}
          onSelect={this.onSelect}
        /> */}

          {!loaded &&
            <Loading text="Loading Users..." />
          }
          {loaded &&
            profiles.map(
              profile => <ProfileCard expandable key={profile.id} userId={userId} profile={profile} />
            )
          }

        {loaded && loadMoreButton && <LoadMoreButton onClick={this.loadMoreProfiles} loading={loading} />}

    )
  }

  loadMoreProfiles() {
    const {fetchMoreUsers, profiles} = this.props
    const {loading} = this.state
    if (!loading) {
      this.setState({loading: true})
      return fetchMoreUsers(profiles.map(profile => profile.id)).then(
        () => this.setState({loading: false})
      )
    }
  }

  onPeopleSearch(event) {
    this.setState({searchText: event.target.value})
  }

  onScroll() {
    let {chatMode, profiles} = this.props
    if (!chatMode && profiles.length > 0) {
      this.setState({scrollPosition: document.body.scrollTop})
      if (this.state.scrollPosition >= (document.body.scrollHeight - window.innerHeight) * 0.7) {
        this.loadMoreProfiles()
      }
    }
  }

  onSelect() {
    console.log('selected')
  }
}
<|endoftext|>


<commit_before>// The resolver injects imports and replace aliases with their aliased values

var fs = require('fs'),
	path = require('path'),
	isArray = require('std/isArray'),
	map = require('std/map'),
	curry = require('std/curry'),
	copy = require('std/copy'),
	filter = require('std/filter'),
	blockFunction = require('std/blockFunction'),
	request = require('request'),
	cleanCSS = require('clean-css'),
	exec = require('child_process').exec

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	each = util.each,
	assert = util.assert,
	halt = util.halt

exports.resolve = function(ast, opts, callback) {
	var completion = blockFunction(function(err) {
		if (err) { callback(err, null) }
		else { callback(null, { expressions:expressions, imports:context.imports, headers:context.headers }) }
	}).addBlock()

	var context = { headers:[], imports:{}, names:{}, opts:opts, completion:completion }

	addStylesheet(context, { href:path.join(__dirname, 'runtime/normalize.css') })

	var expressions = util.cleanup(resolve(context, ast))

	completion.removeBlock()
}

/************************
 * Top level statements *
 ************************/
var resolve = function(context, ast) {
	if (isArray(ast)) { return map(ast, curry(resolve, context)) }
	switch (ast.type) {
		// Setup statements
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)        ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)          ;break

		case 'TEXT_LITERAL':         return ast
		case 'NUMBER_LITERAL':       return ast
		case 'LOGIC_LITERAL':        return ast
		case 'NULL_LITERAL':         return ast

		case 'ARGUMENT':             return ast
		case 'VALUE':                return ast
		case 'NULL':                 return ast
		case 'DEBUGGER':             return ast

		case 'DECLARATION':          return resolveVariableDeclaration(context, ast)
		case 'HANDLER':              return resolveInvocable(context, ast)
		case 'TEMPLATE':             return resolveInvocable(context, ast)
		case 'FUNCTION':             return resolveInvocable(context, ast)

		case 'DICTIONARY_LITERAL':   return resolveObjectLiteral(context, ast)
		case 'LIST_LITERAL':         return resolveList(context, ast)

		case 'XML':                  return resolveXML(context, ast)
		case 'SCRIPT_TAG':           return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'SWITCH_STATEMENT':     return resolveSwitchStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)

		case 'INVOCATION':           return resolveInvocation(context, ast)
		case 'REFERENCE':            return lookup(context, ast)
		case 'COMPOSITE':            return resolveCompositeExpression(context, ast)
		case 'UNARY':                return resolveUnaryExpression(context, ast)
		case 'TERNARY':              return resolveTernaryExpression(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'RETURN':               return resolveReturn(context, ast)

		default:                     halt(ast, '_resolveStatement: Unknown AST type "'+ast.type+'"')
	}
}

/****************
 * Declarations *
 ****************/
var resolveVariableDeclaration = function(context, ast) {
	declare(context, ast, ast.name, ast)
	if (ast.initialValue) {
		ast.initialValue = resolve(context, ast.initialValue)
	} else {
		ast.initialValue = { type:'NULL_LITERAL', value:null }
	}
	return ast
}

var declare = function(context, ast, name, value) {
	assert(ast, !context.names.hasOwnProperty(ast.name), ast.name + ' is already declared in this scope')
	context.names[name] = value
}

/************************
 * References & lookups *
 ************************/
var lookup = function(context, ast) {
	assert(ast, context.names[ast.name], 'Couldn\'t find a variable called "'+ast.name+'"')
	return ast
}

/****************************
 * Object and List literals *
 ****************************/
function resolveObjectLiteral(context, ast) {
	var contentByName = {}
	each(ast.content, function(content) {
		contentByName[content.name] = resolve(context, content.value)
	})
	ast.content = contentByName
	return ast
}

function resolveList(context, ast) {
	ast.content = map(ast.content, function(content) {
		return resolve(context, content)
	})
	return ast
}

/************************
 * Composite expressions *
 ************************/
var resolveCompositeExpression = function(context, ast) {
	ast.left = resolve(context, ast.left)
	ast.right = resolve(context, ast.right)
	return ast
}

var resolveUnaryExpression = function(context, ast) {
	ast.value = resolve(context, ast.value)
	return ast
}

var resolveTernaryExpression = function(context, ast) {
	ast.condition = resolve(context, ast.condition)
	ast.ifValue = resolve(context, ast.ifValue)
	ast.elseValue = resolve(context, ast.elseValue)
	return ast
}

/*******
 * XML *
 *******/
var resolveXML = function(context, ast) {
	each(ast.attributes, function(attribute) {
		attribute.value = resolve(context, attribute.value)
	})
	ast.block = ast.block && filter(resolve(context, ast.block))

	var staticLinkAttrs = getStaticLinkAttrs(ast)
	if (staticLinkAttrs && !ast.block.length) {
		addStylesheet(context, staticLinkAttrs)
		return null
	} else {
		return ast
	}
}

function getStaticLinkAttrs(ast) {
	if (ast.tagName != 'link') { return }
	var attributes = ast.attributes,
		staticAttrs = {}
	each(attributes, function(attr) {
		var valueAST = attr.value
		if (valueAST.type != 'TEXT_LITERAL') { return }
		staticAttrs[attr.name] = valueAST.value
	})
	return staticAttrs.rel && staticAttrs.rel.match(/^stylesheet/) && staticAttrs
}

var addStylesheet = function(context, attrs) {
	var linkHref = attrs.href,
		rel = attrs.rel || 'stylesheet/css'

	context.completion.addBlock()
	if (linkHref.match(/^http/)) {
		console.error("Fetching", linkHref)
		request.get(linkHref, function(err, resp, content) {
			if (err) {
				doReportError(err, 'fetch')
			} else if (resp.statusCode != 200) {
				doReportError(new Error('Server returned non-200 status code: '+resp.statusCode), 'fetch')
			} else {
				console.error("Done fetching", linkHref)
				doAddStyle(content)
			}
		})
	} else {
		if (linkHref[0] != '/') {
			linkHref = path.join(context.opts.dirname, linkHref)
		}
		var content = fs.readFile(linkHref, function(err, content) {
			if (err) { doReportError(err, 'read') }
			else { doAddStyle(content.toString()) }
		})
	}
	function doAddStyle(content) {
		// TODO Support e.g. stylus
		var comment = '/* inlined stylesheet: ' + linkHref + ' */\n'
		if (context.opts.minify) { comment = '' }

		cssPreprocessors[rel](linkHref, comment + content, function(err, css) {
			if (err) {
				doReportError(err, 'preprocess')
			} else {
				context.headers.push('<style type="text/css">\n'+css+'\n</style>')
				context.completion.removeBlock()
			}
		})
	}
	function doReportError(err, verb) {
		console.log("Error", verb+'ing', linkHref)
		context.completion.fail(new Error('Could not '+verb+' '+linkHref+'\n'+err.message))
	}
}

var cssPreprocessors = {
	'stylesheet': function(href, css, callback) {
		callback(null, css)
	},
	'stylesheet/css': function(href, css, callback) {
		callback(null, css)
	},
	'stylesheet/less': function(href, lessContent, callback) {
		try { require('less').render(lessContent, callback) }
		catch(err) {
			installNodeModule('less', function(err) {
				if (err) { return callback(err, null) }
				require('less').render(lessContent, callback)
			})
		}
	},
	'stylesheet/stylus': function(href, stylusContent, callback) {
		try { require('stylus').render(stylusContent, { filename:href }, callback) }
		catch(err) {
			installNodeModule('stylus', function(err) {
				if (err) { return callback(err, null) }
				require('stylus').render(stylusContent, { filename:href }, callback)
			})
		}
	}
}

var installNodeModule = function(name, callback) {
	var cwd = process.cwd(),
		command = 'sudo npm install '+name
	console.error("attempting to install node module", name, "...")
	console.error(command)
	process.chdir(__dirname + '/..')
	exec(command, function(err, stdout, stderr) {
		process.chdir(cwd)
		console.error(err ? "error" : "success", "installing node module", name)
		callback(err)
	})
}

/*******************************
 * Imports (imports and files) *
 *******************************/
var handleModuleImport = function(context, ast) {
	var filePath = __dirname + '/Modules/' + ast.name + '.fun'
	_importFile(context, ast, filePath)
}

var handleFileImport = function(context, ast) {
	// TODO resolve files relative to current file path
	var filePath = path.resolve(process.cwd() + '/examples/' + ast.path + '.fun')
	_importFile(context, filePath, ast)
}

var _importFile = function(context, ast, filePath) {
	if (context.imports[filePath]) { return }
	assert(ast, fs.statSync(filePath).isFile(), 'Could not find module '+filePath)
	var tokens = tokenizer.tokenizeFile(filePath),
		newAST = parser.parse(tokens),
		resolvedAST = util.cleanup(resolve(context, newAST))

	context.imports[filePath] = resolvedAST
}

/***************
 * Invocations *
 ***************/
var resolveInvocation = function(context, ast) {
	ast.operand = lookup(context, ast.operand)
	ast.arguments = resolve(context, ast.arguments)
	return ast
}

/**************************************************
 * Invocables - Functions, Handlers and Templates *
 **************************************************/
var resolveInvocable = function(context, ast) {
	if (ast.closure) { return ast } // already resolved
	setNonEnumerableProperty(ast, 'closure', addScope(context))
	each(ast.signature, function(argument) { declare(ast.closure, ast, argument.name, argument) })
	ast.block = filter(resolve(ast.closure, ast.block))
	return ast
}

/*******************************
 * Handler/Function statements *
 *******************************/
var resolveMutation = function(context, ast) {
	ast.arguments = map(ast.arguments, curry(resolve, context))
	ast.operand = resolve(context, ast.operand)
	return ast
}

var resolveReturn = function(context, ast) {
	ast.value = resolve(context, ast.value)
	return ast
}

/*************
 * For loops *
 *************/
var resolveForLoop = function(context, ast) {
	// ast.iterator.operand = ast.iterable
	ast.iterable = resolve(context, ast.iterable)
	ast.context = addScope(context)
	declare(ast.context, ast, ast.iterator.name, ast.iterator)
	ast.block = filter(resolve(ast.context, ast.block))
	return ast
}

/*****************
 * If statements *
 *****************/
var resolveIfStatement = function(context, ast) {
	ast.condition = resolve(context, ast.condition)
	ast.ifContext = addScope(context)
	ast.ifBlock = resolve(ast.ifContext, ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseContext = addScope(context)
		ast.elseBlock = resolve(ast.elseContext, ast.elseBlock)
	}
	return ast
}

/*********************
 * Switch statements *
 *********************/
var resolveSwitchStatement = function(context, ast) {
	ast.controlValue = resolve(context, ast.controlValue)
	each(ast.cases, function(aCase) {
		aCase.values = map(aCase.values, curry(resolve, context))
		each(aCase.values, function(aCase) {
			assert(aCase, aCase.type == 'VALUE_LITERAL', "Switch statements' case values must be numbers or texts (e.g. 2, 3, 'hello')")
		})
		aCase.statements = map(aCase.statements, curry(resolve, context))
	})
	var defaultCases = util.pick(ast.cases, function(aCase) { return aCase.isDefault })
	assert(ast, defaultCases.length < 2, "Found two default cases in switch statement - well, that doesn't make sense")
	return ast
}

/***********
 * Utility *
 ***********/
var setNonEnumerableProperty = function(obj, name, value) {
	// We want to be able to add state to objects that are not seen by the test code's assert.deepEquals.
	// We do this by assigning non-enumerable properties in the environments that support it,
	// most notably in node where the test suite is run.
	Object.defineProperty(obj, name, { value:value, enumerable:false })
	return value
}

var addScope = function(context) {
	// Creates a scope by prototypically inheriting the names dictionary from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// New names written to context.names will shadow names written further up the chain, but won't overwrite them.
	context = util.create(context)
	context.names = copy(context.names) // shouldn't this be create()?
	return context
}
<commit_msg>Fix file imports<commit_after>// The resolver injects imports and replace aliases with their aliased values

var fs = require('fs'),
	path = require('path'),
	isArray = require('std/isArray'),
	map = require('std/map'),
	curry = require('std/curry'),
	copy = require('std/copy'),
	filter = require('std/filter'),
	blockFunction = require('std/blockFunction'),
	request = require('request'),
	cleanCSS = require('clean-css'),
	exec = require('child_process').exec

var tokenizer = require('./tokenizer'),
	parser = require('./parser')

var util = require('./util'),
	each = util.each,
	assert = util.assert,
	halt = util.halt

exports.resolve = function(ast, opts, callback) {
	var completion = blockFunction(function(err) {
		if (err) { callback(err, null) }
		else { callback(null, { expressions:expressions, imports:context.imports, headers:context.headers }) }
	}).addBlock()

	var context = { headers:[], imports:{}, names:{}, opts:opts, completion:completion }

	addStylesheet(context, { href:path.join(__dirname, 'runtime/normalize.css') })

	var expressions = util.cleanup(resolve(context, ast))

	completion.removeBlock()
}

/************************
 * Top level statements *
 ************************/
var resolve = function(context, ast) {
	if (isArray(ast)) { return map(ast, curry(resolve, context)) }
	switch (ast.type) {
		// Setup statements
		case 'IMPORT_MODULE':        handleModuleImport(context, ast)        ;break
		case 'IMPORT_FILE':          handleFileImport(context, ast)          ;break

		case 'TEXT_LITERAL':         return ast
		case 'NUMBER_LITERAL':       return ast
		case 'LOGIC_LITERAL':        return ast
		case 'NULL_LITERAL':         return ast

		case 'ARGUMENT':             return ast
		case 'VALUE':                return ast
		case 'NULL':                 return ast
		case 'DEBUGGER':             return ast

		case 'DECLARATION':          return resolveVariableDeclaration(context, ast)
		case 'HANDLER':              return resolveInvocable(context, ast)
		case 'TEMPLATE':             return resolveInvocable(context, ast)
		case 'FUNCTION':             return resolveInvocable(context, ast)

		case 'DICTIONARY_LITERAL':   return resolveObjectLiteral(context, ast)
		case 'LIST_LITERAL':         return resolveList(context, ast)

		case 'XML':                  return resolveXML(context, ast)
		case 'SCRIPT_TAG':           return resolveXML(context, ast)
		case 'IF_STATEMENT':         return resolveIfStatement(context, ast)
		case 'SWITCH_STATEMENT':     return resolveSwitchStatement(context, ast)
		case 'FOR_LOOP':             return resolveForLoop(context, ast)

		case 'INVOCATION':           return resolveInvocation(context, ast)
		case 'REFERENCE':            return lookup(context, ast)
		case 'COMPOSITE':            return resolveCompositeExpression(context, ast)
		case 'UNARY':                return resolveUnaryExpression(context, ast)
		case 'TERNARY':              return resolveTernaryExpression(context, ast)

		case 'MUTATION':             return resolveMutation(context, ast)
		case 'RETURN':               return resolveReturn(context, ast)

		default:                     halt(ast, '_resolveStatement: Unknown AST type "'+ast.type+'"')
	}
}

/****************
 * Declarations *
 ****************/
var resolveVariableDeclaration = function(context, ast) {
	declare(context, ast, ast.name, ast)
	if (ast.initialValue) {
		ast.initialValue = resolve(context, ast.initialValue)
	} else {
		ast.initialValue = { type:'NULL_LITERAL', value:null }
	}
	return ast
}

var declare = function(context, ast, name, value) {
	assert(ast, !context.names.hasOwnProperty(ast.name), ast.name + ' is already declared in this scope')
	context.names[name] = value
}

/************************
 * References & lookups *
 ************************/
var lookup = function(context, ast) {
	assert(ast, context.names[ast.name], 'Couldn\'t find a variable called "'+ast.name+'"')
	return ast
}

/****************************
 * Object and List literals *
 ****************************/
function resolveObjectLiteral(context, ast) {
	var contentByName = {}
	each(ast.content, function(content) {
		contentByName[content.name] = resolve(context, content.value)
	})
	ast.content = contentByName
	return ast
}

function resolveList(context, ast) {
	ast.content = map(ast.content, function(content) {
		return resolve(context, content)
	})
	return ast
}

/************************
 * Composite expressions *
 ************************/
var resolveCompositeExpression = function(context, ast) {
	ast.left = resolve(context, ast.left)
	ast.right = resolve(context, ast.right)
	return ast
}

var resolveUnaryExpression = function(context, ast) {
	ast.value = resolve(context, ast.value)
	return ast
}

var resolveTernaryExpression = function(context, ast) {
	ast.condition = resolve(context, ast.condition)
	ast.ifValue = resolve(context, ast.ifValue)
	ast.elseValue = resolve(context, ast.elseValue)
	return ast
}

/*******
 * XML *
 *******/
var resolveXML = function(context, ast) {
	each(ast.attributes, function(attribute) {
		attribute.value = resolve(context, attribute.value)
	})
	ast.block = ast.block && filter(resolve(context, ast.block))

	var staticLinkAttrs = getStaticLinkAttrs(ast)
	if (staticLinkAttrs && !ast.block.length) {
		addStylesheet(context, staticLinkAttrs)
		return null
	} else {
		return ast
	}
}

function getStaticLinkAttrs(ast) {
	if (ast.tagName != 'link') { return }
	var attributes = ast.attributes,
		staticAttrs = {}
	each(attributes, function(attr) {
		var valueAST = attr.value
		if (valueAST.type != 'TEXT_LITERAL') { return }
		staticAttrs[attr.name] = valueAST.value
	})
	return staticAttrs.rel && staticAttrs.rel.match(/^stylesheet/) && staticAttrs
}

var addStylesheet = function(context, attrs) {
	var linkHref = attrs.href,
		rel = attrs.rel || 'stylesheet/css'

	context.completion.addBlock()
	if (linkHref.match(/^http/)) {
		console.error("Fetching", linkHref)
		request.get(linkHref, function(err, resp, content) {
			if (err) {
				doReportError(err, 'fetch')
			} else if (resp.statusCode != 200) {
				doReportError(new Error('Server returned non-200 status code: '+resp.statusCode), 'fetch')
			} else {
				console.error("Done fetching", linkHref)
				doAddStyle(content)
			}
		})
	} else {
		if (linkHref[0] != '/') {
			linkHref = path.join(context.opts.dirname, linkHref)
		}
		var content = fs.readFile(linkHref, function(err, content) {
			if (err) { doReportError(err, 'read') }
			else { doAddStyle(content.toString()) }
		})
	}
	function doAddStyle(content) {
		// TODO Support e.g. stylus
		var comment = '/* inlined stylesheet: ' + linkHref + ' */\n'
		if (context.opts.minify) { comment = '' }

		cssPreprocessors[rel](linkHref, comment + content, function(err, css) {
			if (err) {
				doReportError(err, 'preprocess')
			} else {
				context.headers.push('<style type="text/css">\n'+css+'\n</style>')
				context.completion.removeBlock()
			}
		})
	}
	function doReportError(err, verb) {
		console.log("Error", verb+'ing', linkHref)
		context.completion.fail(new Error('Could not '+verb+' '+linkHref+'\n'+err.message))
	}
}

var cssPreprocessors = {
	'stylesheet': function(href, css, callback) {
		callback(null, css)
	},
	'stylesheet/css': function(href, css, callback) {
		callback(null, css)
	},
	'stylesheet/less': function(href, lessContent, callback) {
		try { require('less').render(lessContent, callback) }
		catch(err) {
			installNodeModule('less', function(err) {
				if (err) { return callback(err, null) }
				require('less').render(lessContent, callback)
			})
		}
	},
	'stylesheet/stylus': function(href, stylusContent, callback) {
		try { require('stylus').render(stylusContent, { filename:href }, callback) }
		catch(err) {
			installNodeModule('stylus', function(err) {
				if (err) { return callback(err, null) }
				require('stylus').render(stylusContent, { filename:href }, callback)
			})
		}
	}
}

var installNodeModule = function(name, callback) {
	var cwd = process.cwd(),
		command = 'sudo npm install '+name
	console.error("attempting to install node module", name, "...")
	console.error(command)
	process.chdir(__dirname + '/..')
	exec(command, function(err, stdout, stderr) {
		process.chdir(cwd)
		console.error(err ? "error" : "success", "installing node module", name)
		callback(err)
	})
}

/*******************************
 * Imports (imports and files) *
 *******************************/
var handleModuleImport = function(context, ast) {
	var filePath = __dirname + '/Modules/' + ast.name + '.fun'
	_importFile(context, ast, filePath)
}

var handleFileImport = function(context, ast) {
	var filePath = (ast.path[0] == '.'
		? path.join(context.opts.dirname, ast.path + '.fun')
		: path.normalize(ast.path))
	_importFile(context, ast, filePath)
}

var _importFile = function(context, ast, filePath) {
	if (context.imports[filePath]) { return }
	try { assert(ast, fs.statSync(filePath).isFile(), 'Could not find module '+filePath) }
	catch(e) { halt(ast, e) }
	var tokens = tokenizer.tokenizeFile(filePath),
		newAST = parser.parse(tokens),
		resolvedAST = util.cleanup(resolve(context, newAST))

	context.imports[filePath] = resolvedAST
}

/***************
 * Invocations *
 ***************/
var resolveInvocation = function(context, ast) {
	ast.operand = lookup(context, ast.operand)
	ast.arguments = resolve(context, ast.arguments)
	return ast
}

/**************************************************
 * Invocables - Functions, Handlers and Templates *
 **************************************************/
var resolveInvocable = function(context, ast) {
	if (ast.closure) { return ast } // already resolved
	setNonEnumerableProperty(ast, 'closure', addScope(context))
	each(ast.signature, function(argument) { declare(ast.closure, ast, argument.name, argument) })
	ast.block = filter(resolve(ast.closure, ast.block))
	return ast
}

/*******************************
 * Handler/Function statements *
 *******************************/
var resolveMutation = function(context, ast) {
	ast.arguments = map(ast.arguments, curry(resolve, context))
	ast.operand = resolve(context, ast.operand)
	return ast
}

var resolveReturn = function(context, ast) {
	ast.value = resolve(context, ast.value)
	return ast
}

/*************
 * For loops *
 *************/
var resolveForLoop = function(context, ast) {
	// ast.iterator.operand = ast.iterable
	ast.iterable = resolve(context, ast.iterable)
	ast.context = addScope(context)
	declare(ast.context, ast, ast.iterator.name, ast.iterator)
	ast.block = filter(resolve(ast.context, ast.block))
	return ast
}

/*****************
 * If statements *
 *****************/
var resolveIfStatement = function(context, ast) {
	ast.condition = resolve(context, ast.condition)
	ast.ifContext = addScope(context)
	ast.ifBlock = resolve(ast.ifContext, ast.ifBlock)
	if (ast.elseBlock) {
		ast.elseContext = addScope(context)
		ast.elseBlock = resolve(ast.elseContext, ast.elseBlock)
	}
	return ast
}

/*********************
 * Switch statements *
 *********************/
var resolveSwitchStatement = function(context, ast) {
	ast.controlValue = resolve(context, ast.controlValue)
	each(ast.cases, function(aCase) {
		aCase.values = map(aCase.values, curry(resolve, context))
		each(aCase.values, function(aCase) {
			assert(aCase, aCase.type == 'VALUE_LITERAL', "Switch statements' case values must be numbers or texts (e.g. 2, 3, 'hello')")
		})
		aCase.statements = map(aCase.statements, curry(resolve, context))
	})
	var defaultCases = util.pick(ast.cases, function(aCase) { return aCase.isDefault })
	assert(ast, defaultCases.length < 2, "Found two default cases in switch statement - well, that doesn't make sense")
	return ast
}

/***********
 * Utility *
 ***********/
var setNonEnumerableProperty = function(obj, name, value) {
	// We want to be able to add state to objects that are not seen by the test code's assert.deepEquals.
	// We do this by assigning non-enumerable properties in the environments that support it,
	// most notably in node where the test suite is run.
	Object.defineProperty(obj, name, { value:value, enumerable:false })
	return value
}

var addScope = function(context) {
	// Creates a scope by prototypically inheriting the names dictionary from the current context.
	// Reads will propegate up the prototype chain, while writes won't.
	// New names written to context.names will shadow names written further up the chain, but won't overwrite them.
	context = util.create(context)
	context.names = copy(context.names) // shouldn't this be create()?
	return context
}
<|endoftext|>


<commit_before>import _ from 'lodash'
import roles from './roles'

/**
 * ACL
 * @param {Object} spec
 */
export default function acl(spec) {
  const WILDCARD = '*'
  const ALLOW = 'ALLOW'
  const DENY = 'DENY'
  const {collection} = spec
  let {acl} = spec
  let o = {}

  acl = acl.loadSync()

  /**
   * Get all keys from collection
   * @returns {[String]}
   */
  function keys() {
    return Object.keys(collection.schema.paths)
  }

  /**
   * Get all keys which a possible population
   * @returns {[String]}
   */
  function refs() {
    return collection.refs()
  }

  /**
   * Check path for allowed keys, can also handle wildcard entries
   * @param {[String]} allKeys
   * @param {String} path
   * @param {String} type
   * @param {Role} role
   * @param {String} privilege
   * @returns {[String]}
   */
  function path(allKeys, path, type, role, privilege) {
    if (acl.fields.hasOwnProperty(path)) {
      if (acl.fields[path].hasOwnProperty(type)) {
        if (acl.fields[path][type][role.name]
          && acl.fields[path][type][role.name].indexOf(privilege) !== -1
        ) {
          if (path === WILDCARD) {
            return allKeys
          }
          return [path]
        }
      }
    }
    return []
  }

  /**
   * Get hierarchy for current role
   * @param {Object} role
   * @returns {[Object]}
   */
  function hierarchy(role) {
    if (!role.parent) {
      return [roles.get(role)]
    }

    return [roles.get(role)].concat(hierarchy(role.parent))
  }

  /**
   * Get allowed keys for a specific role
   * @param {Object} resource
   * @param {String|Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {[String]} A list of allowed keys for given collection
   */
  function allowedForRole(resource, role, privilege, asserts) {
    role = roles.get(role)
    const allKeys = keys()
    const allRefs = refs()
    let allowedKeys = []
    let deniedKeys = []

    // return all keys for superuser
    if (role.superuser === true) {
      return allKeys
    }

    // ALLOW statements
    allKeys.forEach(function(key) {
      allowedKeys = allowedKeys.concat(
        path(allKeys, key, ALLOW, role, privilege)
      )
    })

    // asserts
    asserts.forEach(function(assert) {
      allowedKeys = allowedKeys.concat(
        assert(o, resource, role, privilege)
      )
    })

    // DENY statements
    allKeys.forEach(function(key) {
      deniedKeys = deniedKeys.concat(
        path(allKeys, key, DENY, role, privilege)
      )
    })

    // expand populated paths
    allRefs.forEach(function() {
      //const path = collection.schema.path(ref)
      //const modelName = path.options.ref
      //const model = mongoose.models[modelName]
    })

    return _.uniq(
      _.difference(allowedKeys, deniedKeys)
    )
  }

  /**
   * Get allowed keys for given collection
   * @param {String} resource
   * @param {String|Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {[String]} A list of allowed keys for given collection
   */
  function allowed(resource, role, privilege = 'R', asserts = []) {
    role = roles.get(role)
    const roleHierarchy = hierarchy(role).reverse()
    let allowedKeys = []

    if (_.isFunction(asserts)) {
      asserts = [asserts]
    }

    roleHierarchy.forEach(function(r) {
      allowedKeys = allowedKeys.concat(allowedForRole(resource, r, privilege, asserts))
    })

    return allowedKeys
  }

  /**
   * Filters a resource object by ACL
   * @param {Object} resource
   * @param {Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {Object}
   */
  function filter(resource, role, privilege = 'R', asserts = []) {
    return _.pick(
      resource,
      allowed(
        resource,
        role,
        privilege,
        asserts
      )
    )
  }

  o.allowed = allowed
  o.filter = filter

  return Object.freeze(o)
}
<commit_msg>feat: validate options fix: model.refs() is not a function<commit_after>import _ from 'lodash'
import roles from './roles'

/**
 * ACL
 * @param {Object} spec
 */
export default function acl(spec) {
  const WILDCARD = '*'
  const ALLOW = 'ALLOW'
  const DENY = 'DENY'
  const {collection} = spec
  let {acl} = spec
  let o = {}

  if (!collection) {
    throw new Error('You must provide a valid "collection"')
  }

  if (!acl) {
    throw new Error('You must provide a valid "acl"')
  }

  acl = acl.loadSync()

  /**
   * Get all keys from collection
   * @returns {[String]}
   */
  function keys() {
    return Object.keys(collection.schema.paths)
  }

  /**
   * Get all keys which a possible population
   * @returns {[String]}
   */
  function refs() {
    const refs = {}

    if (collection.schema && _.isObject(collection.schema.paths)) {
      _.forEach(collection.schema.paths, function(path, key) {
        if (path.options && path.options.ref) {
          refs[key] = path.options.ref
        }
      })
    }

    return refs
  }

  /**
   * Check path for allowed keys, can also handle wildcard entries
   * @param {[String]} allKeys
   * @param {String} path
   * @param {String} type
   * @param {Role} role
   * @param {String} privilege
   * @returns {[String]}
   */
  function path(allKeys, path, type, role, privilege) {
    if (acl.fields.hasOwnProperty(path)) {
      if (acl.fields[path].hasOwnProperty(type)) {
        if (acl.fields[path][type][role.name]
          && acl.fields[path][type][role.name].indexOf(privilege) !== -1
        ) {
          if (path === WILDCARD) {
            return allKeys
          }
          return [path]
        }
      }
    }
    return []
  }

  /**
   * Get hierarchy for current role
   * @param {Object} role
   * @returns {[Object]}
   */
  function hierarchy(role) {
    if (!role.parent) {
      return [roles.get(role)]
    }

    return [roles.get(role)].concat(hierarchy(role.parent))
  }

  /**
   * Get allowed keys for a specific role
   * @param {Object} resource
   * @param {String|Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {[String]} A list of allowed keys for given collection
   */
  function allowedForRole(resource, role, privilege, asserts) {
    role = roles.get(role)
    const allKeys = keys()
    const allRefs = refs()
    let allowedKeys = []
    let deniedKeys = []

    // return all keys for superuser
    if (role.superuser === true) {
      return allKeys
    }

    // ALLOW statements
    allKeys.forEach(function(key) {
      allowedKeys = allowedKeys.concat(
        path(allKeys, key, ALLOW, role, privilege)
      )
    })

    // asserts
    asserts.forEach(function(assert) {
      allowedKeys = allowedKeys.concat(
        assert(o, resource, role, privilege)
      )
    })

    // DENY statements
    allKeys.forEach(function(key) {
      deniedKeys = deniedKeys.concat(
        path(allKeys, key, DENY, role, privilege)
      )
    })

    // expand populated paths
    _.forEach(allRefs, function(/*ref, path*/) {
      //const path = collection.schema.path(ref)
      //const modelName = path.options.ref
      //const model = mongoose.models[modelName]
    })

    return _.uniq(
      _.difference(allowedKeys, deniedKeys)
    )
  }

  /**
   * Get allowed keys for given collection
   * @param {String} resource
   * @param {String|Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {[String]} A list of allowed keys for given collection
   */
  function allowed(resource, role, privilege = 'R', asserts = []) {
    role = roles.get(role)
    const roleHierarchy = hierarchy(role).reverse()
    let allowedKeys = []

    if (_.isFunction(asserts)) {
      asserts = [asserts]
    }

    roleHierarchy.forEach(function(r) {
      allowedKeys = allowedKeys.concat(allowedForRole(resource, r, privilege, asserts))
    })

    return allowedKeys
  }

  /**
   * Filters a resource object by ACL
   * @param {Object} resource
   * @param {Object} role
   * @param {String} [privilege='R']
   * @param {Array} [asserts=[]]
   * @returns {Object}
   */
  function filter(resource, role, privilege = 'R', asserts = []) {
    return _.pick(
      resource,
      allowed(
        resource,
        role,
        privilege,
        asserts
      )
    )
  }

  o.allowed = allowed
  o.filter = filter

  return Object.freeze(o)
}
<|endoftext|>


<commit_before>;(function(module, undefined) {
'use strict';

module.provider('WebSocket', function() {
    // when forwarding events, prefix the event name
    var _prefix = 'websocket:';
    var _WebSocket;
    var _host;

    this.host = function(setHost) {
      _host = setHost;
      return this;
    };

    this.prefix = function(newPrefix) {
      _prefix = newPrefix;
      return this;
    };

    this.WebSocket = function(url, protocols) {
      protocols = Array.prototype.slice.call(arguments, 1);
      _WebSocket = new WebSocket(url, protocols);
      return this;
    };

    // expose to provider
    this.$get = ['$rootScope', '$timeout', function($rootScope, $timeout) {

      var ws = _WebSocket;

      var asyncAngularify = function (callback) {
        return function(args) {
          args = Array.prototype.slice.call(arguments);
          $timeout(function() {
            callback.apply(ws, args);
          });
        };
      };

      var addListener = function(event) {
        event = event && 'on'+event || 'onmessage';
        return function(eventName, callback) {
          ws[event](eventName, asyncAngularify(callback));
        };
      };

      var wrappedWebSocket = {
        on: addListener('message'),
        onmessage: addListener('message'),
        onclose: addListener('close'),
        onopen: addListener('open'),
        onerror: addListener('error'),
        close: function() {
          ws.close();
        },

        send: function(eventName, data, callback) {
          if (angular.isObject(eventName)) {
            ws.send(eventName);
          } else {
            if (callback) {
              ws.send(eventName, data, asyncAngularify(callback));
            } else {
              ws.send(eventName, data);
            }
          }
        },

        removeListener: function(args) {
          args = Array.prototype.slice.call(arguments);
          return ws.removeListener.apply(ws, args);
        },

        // when ws.on('someEvent', fn (data) { ... }),
        // call scope.$broadcast('someEvent', data)
        forward: function(events, scope) {

          if (events instanceof Array === false) {
            events = [events];
          }

          if (!scope) {
            scope = $rootScope;
          }

          events.forEach(function(eventName) {
            var prefixed = _prefix + eventName;
            var forwardEvent = asyncAngularify(function (data) {
              scope.$broadcast(prefixed, data);
            });
            scope.$on('$destroy', function () {
              ws.removeListener(eventName, forwardEvent);
            });
            ws.onmessage(eventName, forwardEvent);
          });
        }
      };

      return wrappedWebSocket;

    }];

});

}(angular.module('angular-websocket', [])));
<commit_msg>update source<commit_after>;(function(module, undefined) {
'use strict';

module.provider('WebSocket', function() {
    // when forwarding events, prefix the event name
    var _prefix = 'websocket:';
    var _WebSocket;

    this.prefix = function(newPrefix) {
      _prefix = newPrefix;
      return this;
    };

    this.url = function(url, protocols) {
      protocols = Array.prototype.slice.call(arguments, 1);
      _WebSocket = new WebSocket(url, protocols);
      return this;
    };

    // expose to provider
    this.$get = ['$rootScope', '$timeout', function($rootScope, $timeout) {

      var ws = _WebSocket;

      var asyncAngularify = function (callback) {
        return function(args) {
          args = Array.prototype.slice.call(arguments);
          $timeout(function() {
            callback.apply(ws, args);
          });
        };
      };

      var addListener = function(event) {
        event = event && 'on'+event || 'onmessage';
        return function(eventName, callback) {
          ws[event](eventName, asyncAngularify(callback));
        };
      };

      var wrappedWebSocket = {
        on: addListener('message'),
        onmessage: addListener('message'),
        onclose: addListener('close'),
        onopen: addListener('open'),
        onerror: addListener('error'),
        close: function() {
          ws.close();
        },

        send: function(eventName, data, callback) {
          if (angular.isObject(eventName)) {
            ws.send(eventName);
          } else {
            if (callback) {
              ws.send(eventName, data, asyncAngularify(callback));
            } else {
              ws.send(eventName, data);
            }
          }
        },

        removeListener: function(args) {
          args = Array.prototype.slice.call(arguments);
          return ws.removeListener.apply(ws, args);
        },

        // when ws.on('someEvent', fn (data) { ... }),
        // call scope.$broadcast('someEvent', data)
        forward: function(events, scope) {

          if (events instanceof Array === false) {
            events = [events];
          }

          if (!scope) {
            scope = $rootScope;
          }

          events.forEach(function(eventName) {
            var prefixed = _prefix + eventName;
            var forwardEvent = asyncAngularify(function (data) {
              scope.$broadcast(prefixed, data);
            });
            scope.$on('$destroy', function () {
              ws.removeListener(eventName, forwardEvent);
            });
            ws.onmessage(eventName, forwardEvent);
          });
        }
      };

      return wrappedWebSocket;

    }];

});

}(angular.module('angular-websocket', [])));
<|endoftext|>


<commit_before>define([
	"use!underscore"
], function() {
	var Array2d = function () {
		this._columns = {};
	};

	Array2d.prototype.get = function(location) {
		var row = this._columns[location.x];

		if (row)
			return row[location.y];
	};

	Array2d.prototype.set = function (location, value) {
		var row = this._columns[location.x] || (this._columns[location.x] = {});

		if (row[location.y])
			return false;

		row[location.y] = value;

		return true;
	};

	Array2d.prototype.each = function (callback, context, options) {
		var context = context || this;
		var options = options || {};

		var location = {};

		_.each(this._columns, function (data, outerKey) {
			location.x = parseInt(outerKey, 10);

			if (options.left != null && location.x < options.left)
				return;

			if (options.right != null && location.x > options.right)
				return;

			_.each(data, function (data, innerKey) {
				location.y = parseInt(innerKey, 10);

				if (options.top != null && location.y < options.top)
					return;

				if (options.bottom != null && location.y > options.bottom)
					return;

				callback.call(context, location, data);
			});
		});
	};

	Array2d.prototype.clear = function (location) {
		var row = this._columns[location.x];

		if (!row)
			return false;

		var value = row[location.y];

		if (value) {
			delete row[location.y];

			return value;
		}

		return false;
	};

	return Array2d;
});
<commit_msg>Clean up array2d.clear function.<commit_after>define([
	"use!underscore"
], function() {
	var Array2d = function () {
		this._columns = {};
	};

	Array2d.prototype.get = function(location) {
		var row = this._columns[location.x];

		if (row)
			return row[location.y];
	};

	Array2d.prototype.set = function (location, value) {
		var row = this._columns[location.x] || (this._columns[location.x] = {});

		if (row[location.y])
			return false;

		row[location.y] = value;

		return true;
	};

	Array2d.prototype.each = function (callback, context, options) {
		var context = context || this;
		var options = options || {};

		var location = {};

		_.each(this._columns, function (data, outerKey) {
			location.x = parseInt(outerKey, 10);

			if (options.left != null && location.x < options.left)
				return;

			if (options.right != null && location.x > options.right)
				return;

			_.each(data, function (data, innerKey) {
				location.y = parseInt(innerKey, 10);

				if (options.top != null && location.y < options.top)
					return;

				if (options.bottom != null && location.y > options.bottom)
					return;

				callback.call(context, location, data);
			});
		});
	};

	Array2d.prototype.clear = function (location) {
		var row = this._columns[location.x];

		if (!row)
			return false;

		var value = row[location.y];

		if (!value)
			return false;

		delete row[location.y];

		return value;
	};

	return Array2d;
});
<|endoftext|>