<commit_before>var utils = {
  getAllAnimals:  (callback) => {
    // try it with the heroku url
    // or try it with just the /api/animals
    $.ajax({
      type: "GET",
      url: "http://localhost:8080/api/animals",
      contentType: 'application/json',
      success: function success(data) {
        callback(data);
      }
    });
  },

  addOneAnimal: (animal, callback) => {
    var data = JSON.stringify(animal);

    $.ajax({
      type: "POST",
      url: "http://localhost:8080/api/animals",
      contentType: 'application/json',
      data: data,
      success: function success(data) {
        callback();
      }
    });
  },
};

window.utils = utils;<commit_msg>ajax urls updated - heroku<commit_after>var utils = {
  getAllAnimals:  (callback) => {
    // try it with the heroku url
    // or try it with just the /api/animals
    $.ajax({
      type: "GET",
      url: "/api/animals",
      contentType: 'application/json',
      success: function success(data) {
        callback(data);
      }
    });
  },

  addOneAnimal: (animal, callback) => {
    var data = JSON.stringify(animal);

    $.ajax({
      type: "POST",
      url: "/api/animals",
      contentType: 'application/json',
      data: data,
      success: function success(data) {
        callback();
      }
    });
  },
};

window.utils = utils;<|endoftext|>


<commit_before>// @flow

import React from 'react';
import Relay from 'react-relay/classic';

import PusherStore from 'app/stores/PusherStore';
import CentrifugeStore from 'app/stores/CentrifugeStore';

import Button from 'app/components/shared/Button';

const PAGE_SIZE = 5;

type Props = {
  build: {
    id: string,
    annotations: {
      edges: Array<{
        node: {
          id: string,
          style: string,
          body: {
            html: ?string
          }
        }
      }>
    }
  },
  relay: Object
};

type State = {
  loadingMore: boolean
};

class AnnnotationsList extends React.Component<Props> {
  componentDidMount() {
    PusherStore.on("build:annotations_change", this.handleWebsocketEvent);
    CentrifugeStore.on("build:annotations_change", this.handleWebsocketEvent);
  }

  componentWillUnmount() {
    PusherStore.off("build:annotations_change", this.handleWebsocketEvent);
    CentrifugeStore.off("build:annotations_change", this.handleWebsocketEvent);
  }

  render() {
    const annotations = this.props.build.annotations.edges.map((edge) => {
      return this.renderAnnotation(edge.node);
    });

    return (

        {annotations}
        {this.renderShowMore()}

    );
  }

  renderShowMore() {
    if (this.props.build.annotations.pageInfo.hasNextPage) {
      return (
        <Button
          outline={true}
          theme="default"
          onClick={this.handleShowMoreAnnotations}
        >
          Show more annotations
        </Button>
      );
    }
  }

  handleShowMoreAnnotations = () => {
    this.setState({ loadingMore: true });

    this.props.relay.setVariables(
      {
        pageSize: this.props.relay.variables.pageSize + PAGE_SIZE
      },
      (readyState) => {
        if (readyState.done) {
          this.setState({ loadingMore: false });
        }
      }
    );
  };

  handleAnnotationClick = (event: MouseEvent) => {
    // Don't change anything if the user is using any modifier keys
    if ((event.button !== 0) || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
      return;
    }

    // Don't change clicks in things which aren't  elements, or don't have a `href`
    if (!(event.target instanceof HTMLAnchorElement) || !event.target.href) {
      return;
    }

    // Parse the link URL, relative to the current location
    const linkUrl = new URL(event.target.href, window.location.href);

    // Don't do anything to links which go somewhere other than this current page
    if (`${linkUrl.origin}${linkUrl.pathname}` !== `${window.location.origin}${window.location.pathname}`) {
      return;
    }

    const jobId = linkUrl.hash.slice(1);

    // If we can't find a job component for that job ID, don't do anything
    if (!document.getElementById(`job-component-${jobId}`)) {
      return;
    }

    // Finally, if we now know this is a valid URL and job ID,
    // cancel the browser's usual navigation, and emit a "job:focus" event
    event.preventDefault();

    // NOTE: You're supposed to pass a "full" job object here, but
    // only actually consumes "id" and "path" - so here we fake it!
    window.Buildkite.dispatcher.emit(
      "job:focus",
      {
        job: {
          id: jobId,
          path: ((event.target: any): HTMLAnchorElement).href
        }
      }
    );
  }

  renderAnnotation(annotation) {
    let backgroundColor;
    let borderColor;
    let icon;
    let iconColor;

    if (annotation.style === "SUCCESS") {
      backgroundColor = "green";
      borderColor = "green";
      iconColor = "white";
      icon = "check";
    } else if (annotation.style === "ERROR") {
      backgroundColor = "red";
      borderColor = "red";
      iconColor = "white";
      icon = "times";
    } else if (annotation.style === "INFO") {
      backgroundColor = "blue";
      borderColor = "blue";
      iconColor = "white";
      icon = "info";
    } else if (annotation.style === "WARNING") {
      backgroundColor = "orange";
      borderColor = "orange";
      iconColor = "white";
      icon = "warning";
    } else {
      backgroundColor = "gray";
      borderColor = "gray";
      iconColor = "dark-gray";
      icon = "sticky-note-o";
    }

    return (
      <div key={annotation.id} className={`rounded flex items-stretch border-${borderColor} border mb4`}>
        <div className={`bg-${backgroundColor} flex-none flex pt3`} style={{ width: 30 }}>

            <i className={`fa fa-${icon} ${iconColor}`} />

    );
  }

  handleWebsocketEvent = (payload) => {
    if (payload.buildID === this.props.build.id) {
      this.props.relay.forceFetch();
    }
  };
}

export default Relay.createContainer(AnnnotationsList, {
  initialVariables: {
    pageSize: PAGE_SIZE
  },

  fragments: {
    build: () => Relay.QL`
      fragment on Build {
        id
        annotations(first: $pageSize) {
          edges {
            node {
              id
              style
              body {
                html
              }
            }
            cursor
          }
          pageInfo {
            hasNextPage
            hasPreviousPage
          }
        }
      }
    `
  }
});
<commit_msg>Include pageInfo in props<commit_after>// @flow

import React from 'react';
import Relay from 'react-relay/classic';

import PusherStore from 'app/stores/PusherStore';
import CentrifugeStore from 'app/stores/CentrifugeStore';

import Button from 'app/components/shared/Button';

const PAGE_SIZE = 5;

type Props = {
  build: {
    id: string,
    annotations: {
      edges: Array<{
        node: {
          id: string,
          style: string,
          body: {
            html: ?string
          }
        }
      }>,
      pageInfo: {
        hasNextPage: boolean
      }
    }
  },
  relay: Object
};

type State = {
  loadingMore: boolean
};

class AnnnotationsList extends React.Component<Props> {
  componentDidMount() {
    PusherStore.on("build:annotations_change", this.handleWebsocketEvent);
    CentrifugeStore.on("build:annotations_change", this.handleWebsocketEvent);
  }

  componentWillUnmount() {
    PusherStore.off("build:annotations_change", this.handleWebsocketEvent);
    CentrifugeStore.off("build:annotations_change", this.handleWebsocketEvent);
  }

  render() {
    const annotations = this.props.build.annotations.edges.map((edge) => {
      return this.renderAnnotation(edge.node);
    });

    return (

        {annotations}
        {this.renderShowMore()}

    );
  }

  renderShowMore() {
    if (this.props.build.annotations.pageInfo.hasNextPage) {
      return (
        <Button
          outline={true}
          theme="default"
          onClick={this.handleShowMoreAnnotations}
        >
          Show more annotations
        </Button>
      );
    }
  }

  handleShowMoreAnnotations = () => {
    this.setState({ loadingMore: true });

    this.props.relay.setVariables(
      {
        pageSize: this.props.relay.variables.pageSize + PAGE_SIZE
      },
      (readyState) => {
        if (readyState.done) {
          this.setState({ loadingMore: false });
        }
      }
    );
  };

  handleAnnotationClick = (event: MouseEvent) => {
    // Don't change anything if the user is using any modifier keys
    if ((event.button !== 0) || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) {
      return;
    }

    // Don't change clicks in things which aren't  elements, or don't have a `href`
    if (!(event.target instanceof HTMLAnchorElement) || !event.target.href) {
      return;
    }

    // Parse the link URL, relative to the current location
    const linkUrl = new URL(event.target.href, window.location.href);

    // Don't do anything to links which go somewhere other than this current page
    if (`${linkUrl.origin}${linkUrl.pathname}` !== `${window.location.origin}${window.location.pathname}`) {
      return;
    }

    const jobId = linkUrl.hash.slice(1);

    // If we can't find a job component for that job ID, don't do anything
    if (!document.getElementById(`job-component-${jobId}`)) {
      return;
    }

    // Finally, if we now know this is a valid URL and job ID,
    // cancel the browser's usual navigation, and emit a "job:focus" event
    event.preventDefault();

    // NOTE: You're supposed to pass a "full" job object here, but
    // only actually consumes "id" and "path" - so here we fake it!
    window.Buildkite.dispatcher.emit(
      "job:focus",
      {
        job: {
          id: jobId,
          path: ((event.target: any): HTMLAnchorElement).href
        }
      }
    );
  }

  renderAnnotation(annotation) {
    let backgroundColor;
    let borderColor;
    let icon;
    let iconColor;

    if (annotation.style === "SUCCESS") {
      backgroundColor = "green";
      borderColor = "green";
      iconColor = "white";
      icon = "check";
    } else if (annotation.style === "ERROR") {
      backgroundColor = "red";
      borderColor = "red";
      iconColor = "white";
      icon = "times";
    } else if (annotation.style === "INFO") {
      backgroundColor = "blue";
      borderColor = "blue";
      iconColor = "white";
      icon = "info";
    } else if (annotation.style === "WARNING") {
      backgroundColor = "orange";
      borderColor = "orange";
      iconColor = "white";
      icon = "warning";
    } else {
      backgroundColor = "gray";
      borderColor = "gray";
      iconColor = "dark-gray";
      icon = "sticky-note-o";
    }

    return (
      <div key={annotation.id} className={`rounded flex items-stretch border-${borderColor} border mb4`}>
        <div className={`bg-${backgroundColor} flex-none flex pt3`} style={{ width: 30 }}>

            <i className={`fa fa-${icon} ${iconColor}`} />

    );
  }

  handleWebsocketEvent = (payload) => {
    if (payload.buildID === this.props.build.id) {
      this.props.relay.forceFetch();
    }
  };
}

export default Relay.createContainer(AnnnotationsList, {
  initialVariables: {
    pageSize: PAGE_SIZE
  },

  fragments: {
    build: () => Relay.QL`
      fragment on Build {
        id
        annotations(first: $pageSize) {
          edges {
            node {
              id
              style
              body {
                html
              }
            }
            cursor
          }
          pageInfo {
            hasNextPage
            hasPreviousPage
          }
        }
      }
    `
  }
});
<|endoftext|>


<commit_before>var _; // globals

describe("About Applying What We Have Learnt", function() {
  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {
    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(1);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {
    var productsICanEat = [];

    //using _.filter and _.all

    productsICanEat = _.filter(products, function(pizza){
      return _.all(pizza, function(){
        var noMushrooms = true;
        _.each(pizza.ingredients, function(item){
          if (item === "mushrooms"){
            noMushrooms = false;
          }
        })
        return noMushrooms && !pizza.containsNuts;
      })

    })
        //console.log(productsICanEat);

        /* solve using filter() & all() / any() */

    expect(productsICanEat.length).toBe(1);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {
    var sum = 0;

    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(233168);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {
    var sum = 0;    /* try chaining range() and reduce() */
    function range(a, b){
      var arr = [];
      for(var i = a; i < b; i++){
        arr.push(i);
      }
      return arr;
    }

    expect(233168).toBe(range(1, 1000).reduce(function(a, c){
      if(c % 3 === 0 || c % 5 === 0){
        a = a + c;
      }
      return a;
    }, 0));
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(2);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = {};

    Array.prototype.flatten = function flatten(){
      return _.reduce(this, function(a, c){
        return a.concat(c);
      }, [])
    }
// chaining map flatten & reduce
    products.map(function(pizza){
      return pizza.ingredients;
    }).flatten().reduce(function(a, c){
      if(a[c] === undefined){
        a[c] = 1;
      }
      else{
        a[c]++;
      }
      return a;
    }, ingredientCount);

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(2);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */

  it("should find the largest prime factor of a composite number", function () {

    largestPrimeFactor = function(compositeNum){
      //finding the factors of composite number
      var factors = [];
      for(var i = 2; i < compositeNum; i++){
        if(compositeNum % i === 0){
          factors.push(i);
        }
      }
      //filtering the factors for prime number
      var prime = [];
      _.each(factors, function(val){
        var isPrime = true;
        for(var i = 2; i < val; i++){
          if(val % i === 0) {
            isPrime = false;
          }
        }
        if(isPrime){
          prime.push(val);
        }
      })
    // returning the largest prime factor
      return prime.pop();
    }
    expect(largestPrimeFactor(10001)).toBe(137);

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {
    var smallest;
    for(var i = 40; ; i+=10){
      for(var j = 20; j >= 2; j--){
        if(i % j !== 0){
          break;
        }
      }
      if (j < 2) {
        smallest = i;
        break;
      }
    }
  expect(smallest).toBe(232792560);

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  });

  it("should find the 10001st prime", function () {

  });

});
<commit_msg>made changes - smallest num div by each of the num 1-20 (extra credit)<commit_after>var _; // globals

describe("About Applying What We Have Learnt", function() {
  var products;

  beforeEach(function () {
    products = [
       { name: "Sonoma", ingredients: ["artichoke", "sundried tomatoes", "mushrooms"], containsNuts: false },
       { name: "Pizza Primavera", ingredients: ["roma", "sundried tomatoes", "goats cheese", "rosemary"], containsNuts: false },
       { name: "South Of The Border", ingredients: ["black beans", "jalapenos", "mushrooms"], containsNuts: false },
       { name: "Blue Moon", ingredients: ["blue cheese", "garlic", "walnuts"], containsNuts: true },
       { name: "Taste Of Athens", ingredients: ["spinach", "kalamata olives", "sesame seeds"], containsNuts: true }
    ];
  });

  /*********************************************************************************/

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (imperative)", function () {
    var i,j,hasMushrooms, productsICanEat = [];

    for (i = 0; i < products.length; i+=1) {
        if (products[i].containsNuts === false) {
            hasMushrooms = false;
            for (j = 0; j < products[i].ingredients.length; j+=1) {
               if (products[i].ingredients[j] === "mushrooms") {
                  hasMushrooms = true;
               }
            }
            if (!hasMushrooms) productsICanEat.push(products[i]);
        }
    }

    expect(productsICanEat.length).toBe(1);
  });

  it("given I'm allergic to nuts and hate mushrooms, it should find a pizza I can eat (functional)", function () {
    var productsICanEat = [];

    //using _.filter and _.all

    productsICanEat = _.filter(products, function(pizza){
      return _.all(pizza, function(){
        var noMushrooms = true;
        _.each(pizza.ingredients, function(item){
          if (item === "mushrooms"){
            noMushrooms = false;
          }
        })
        return noMushrooms && !pizza.containsNuts;
      })

    })
        //console.log(productsICanEat);

        /* solve using filter() & all() / any() */

    expect(productsICanEat.length).toBe(1);
  });

  /*********************************************************************************/

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (imperative)", function () {
    var sum = 0;

    for(var i=1; i<1000; i+=1) {
      if (i % 3 === 0 || i % 5 === 0) {
        sum += i;
      }
    }

    expect(sum).toBe(233168);
  });

  it("should add all the natural numbers below 1000 that are multiples of 3 or 5 (functional)", function () {
    var sum = 0;    /* try chaining range() and reduce() */
    function range(a, b){
      var arr = [];
      for(var i = a; i < b; i++){
        arr.push(i);
      }
      return arr;
    }

    expect(233168).toBe(range(1, 1000).reduce(function(a, c){
      if(c % 3 === 0 || c % 5 === 0){
        a = a + c;
      }
      return a;
    }, 0));
  });

  /*********************************************************************************/
   it("should count the ingredient occurrence (imperative)", function () {
    var ingredientCount = { "{ingredient name}": 0 };

    for (i = 0; i < products.length; i+=1) {
        for (j = 0; j < products[i].ingredients.length; j+=1) {
            ingredientCount[products[i].ingredients[j]] = (ingredientCount[products[i].ingredients[j]] || 0) + 1;
        }
    }

    expect(ingredientCount['mushrooms']).toBe(2);
  });

  it("should count the ingredient occurrence (functional)", function () {
    var ingredientCount = {};

    Array.prototype.flatten = function flatten(){
      return _.reduce(this, function(a, c){
        return a.concat(c);
      }, [])
    }
// chaining map flatten & reduce
    products.map(function(pizza){
      return pizza.ingredients;
    }).flatten().reduce(function(a, c){
      if(a[c] === undefined){
        a[c] = 1;
      }
      else{
        a[c]++;
      }
      return a;
    }, ingredientCount);

    /* chain() together map(), flatten() and reduce() */

    expect(ingredientCount['mushrooms']).toBe(2);
  });

  /*********************************************************************************/
  /* UNCOMMENT FOR EXTRA CREDIT */

  it("should find the largest prime factor of a composite number", function () {

    largestPrimeFactor = function(compositeNum){
      //finding the factors of composite number
      var factors = [];
      for(var i = 2; i < compositeNum; i++){
        if(compositeNum % i === 0){
          factors.push(i);
        }
      }
      //filtering the factors for prime number
      var prime = [];
      _.each(factors, function(val){
        var isPrime = true;
        for(var i = 2; i < val; i++){
          if(val % i === 0) {
            isPrime = false;
          }
        }
        if(isPrime){
          prime.push(val);
        }
      })
    // returning the largest prime factor
      return prime.pop();
    }
    expect(largestPrimeFactor(10001)).toBe(137);

  });

  it("should find the largest palindrome made from the product of two 3 digit numbers", function () {

  expect(palindrome).toBe();
  });

  it("should find the smallest number divisible by each of the numbers 1 to 20", function () {
    function smallestDivisibleByOneToTwenty(){
      var smallest;
      for(var i = 40; ; i+=10){
        for(var j = 20; j >= 2; j--){
          if(i % j !== 0){
            break;
          }
        }
        if (j < 2) {
          smallest = i;
          return smallest;
        }
      }
    };
  expect(smallestDivisibleByOneToTwenty()).toBe(232792560);

  });

  it("should find the difference between the sum of the squares and the square of the sums", function () {

  expect(difference).toBe();
  });

  it("should find the 10001st prime", function () {

  expect(10001).toBe();
  });

});
<|endoftext|>


<commit_before>import assert from "assert";
import {isString, has, keys, each, without} from "underscore";
import {sha1, readAndWatchFileWithHash} from "../fs/watch.js";
import {matches as archMatches} from "../utils/archinfo.js";
import {findImportedModuleIdentifiers} from "./js-analyze.js";
import buildmessage from "../utils/buildmessage.js";
import {
  pathJoin,
  pathRelative,
  pathNormalize,
  pathDirname,
  pathBasename,
  pathExtname,
  statOrNull,
  convertToPosixPath,
} from "../fs/files.js";

// Default handlers for well-known file extensions.
const extensions = {
  ".js"(data) {
    return data;
  },

  ".json"(data) {
    return "module.exports = " +
      JSON.stringify(JSON.parse(data), null, 2) +
      ";\n";
  }
};

export default class ImportScanner {
  constructor({
    name,
    bundleArch,
    sourceRoot,
    usedPackageNames = {},
    nodeModulesPath,
    watchSet,
  }) {
    assert.ok(isString(sourceRoot));

    this.name = name;
    this.bundleArch = bundleArch;
    this.sourceRoot = sourceRoot;
    this.usedPackageNames = usedPackageNames;
    this.nodeModulesPath = nodeModulesPath;
    this.watchSet = watchSet;
    this.absPathToOutputIndex = {};
    this.outputFiles = [];
  }

  addInputFiles(files) {
    files.forEach(file => {
      const absPath = pathJoin(this.sourceRoot, file.sourcePath);

      // Files that are not eagerly evaluated (lazy) will only be included
      // in the bundle if they are actually imported. Files that are
      // eagerly evaluated are effectively "imported" as entry points.
      file.imported = ! file.lazy;

      file.installPath = this._getInstallPath(absPath);

      if (has(this.absPathToOutputIndex, absPath)) {
        const index = this.absPathToOutputIndex[absPath];
        this.outputFiles[index] = file;
      } else {
        this.absPathToOutputIndex[absPath] =
          this.outputFiles.push(file) - 1;
      }
    });

    this.outputFiles.forEach(file => {
      if (! file.lazy || file.imported) {
        this._scanFile(file);
      }
    });

    return this;
  }

  addNodeModules(identifiers) {
    if (identifiers) {
      if (typeof identifiers === "object" &&
          ! Array.isArray(identifiers)) {
        identifiers = Object.keys(identifiers);
      }

      if (identifiers.length > 0) {
        this._scanFile({
          sourcePath: "fake.js",
          // By specifying the .deps property of this fake file ahead of
          // time, we can avoid calling findImportedModuleIdentifiers in the
          // _scanFile method.
          deps: identifiers,
        });
      }
    }

    return this;
  }

  getOutputFiles(options) {
    return this.outputFiles;
  }

  _scanFile(file) {
    const absPath = pathJoin(this.sourceRoot, file.sourcePath);
    file.deps = file.deps || keys(
      findImportedModuleIdentifiers(file.data.toString("utf8"))
    );

    each(file.deps, id => {
      const absImportedPath = this._tryToResolveImportedPath(file, id);
      if (! absImportedPath) {
        return;
      }

      if (has(this.absPathToOutputIndex, absImportedPath)) {
        // Avoid scanning files that we've scanned before, but mark them
        // as imported so we know to include them in the bundle if they
        // are lazy.
        const index = this.absPathToOutputIndex[absImportedPath];
        const file = this.outputFiles[index];

        // Eager files and files that we have imported before do not need
        // to be scanned again. Lazy files that we have not imported
        // before still need to be scanned, however.
        const alreadyScanned = ! file.lazy || file.imported;

        // Whether the file is eager or lazy, mark it as imported. For
        // lazy files, this makes the difference between being included in
        // or omitted from the bundle. For eager files, this just ensures
        // we won't scan them again.
        file.imported = true;

        if (! alreadyScanned) {
          this._scanFile(file);
        }

        return;
      }

      if (! this._hasKnownExtension(absImportedPath)) {
        // The _readModule method provides hardcoded support for files
        // with known extensions, but any other type of file must be
        // ignored at this point, because it was not in the set of input
        // files and therefore must not have been processed by a compiler
        // plugin for the current architecture (this.bundleArch).
        return;
      }

      const installPath = this._getInstallPath(absImportedPath);
      if (! installPath) {
        // The given path cannot be installed on this architecture.
        return;
      }

      // The result of _readModule will have .data and .hash properties.
      const depFile = this._readModule(absImportedPath);
      depFile.type = "js"; // TODO Is this correct?
      depFile.sourcePath = pathRelative(this.sourceRoot, absImportedPath);
      depFile.installPath = installPath;
      depFile.servePath = installPath;
      depFile.lazy = true;
      depFile.imported = true;

      // Append this file to the output array and record its index.
      this.absPathToOutputIndex[absImportedPath] =
        this.outputFiles.push(depFile) - 1;

      this._scanFile(depFile);
    });
  }

  _readFile(absPath) {
    let { contents, hash } =
      readAndWatchFileWithHash(this.watchSet, absPath);

    return {
      data: contents.toString("utf8"),
      hash,
    };
  }

  _readModule(absPath) {
    const info = this._readFile(absPath);

    // Same logic/comment as stripBOM in node/lib/module.js:
    // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
    // because the buffer-to-string conversion in `fs.readFileSync()`
    // translates it to FEFF, the UTF-16 BOM.
    if (info.data.charCodeAt(0) === 0xfeff) {
      info.data = info.data.slice(1);
    }

    const ext = pathExtname(absPath).toLowerCase();
    info.data = extensions[ext](info.data);

    return info;
  }

  // Returns a relative path indicating where to install the given file
  // via meteorInstall. May return undefined if the file should not be
  // installed on the current architecture.
  _getInstallPath(absPath) {
    let path =
      this._getNodeModulesInstallPath(absPath) ||
      this._getSourceRootInstallPath(absPath);

    if (! path) {
      return;
    }

    if (this.name) {
      // If we're bundling a package, prefix path with
      // node_modules/<package name>/.
      path = pathJoin("node_modules", "meteor", this.name, path);
    }

    return path;
  }

  _getNodeModulesInstallPath(absPath) {
    if (this.nodeModulesPath) {
      const relPathWithinNodeModules =
        pathRelative(this.nodeModulesPath, absPath);

      if (relPathWithinNodeModules.startsWith("..")) {
        // absPath is not a subdirectory of this.nodeModulesPath.
        return;
      }

      if (! this._hasKnownExtension(relPathWithinNodeModules)) {
        // Only accept files within node_modules directories if they
        // have one of the known extensions.
        return;
      }

      // Install the module into the local node_modules directory within
      // this app or package.
      return pathJoin("node_modules", relPathWithinNodeModules);
    }
  }

  _getSourceRootInstallPath(absPath) {
    const installPath = pathRelative(this.sourceRoot, absPath);

    if (installPath.startsWith("..")) {
      // absPath is not a subdirectory of this.sourceRoot.
      return;
    }

    const dirs = this._splitPath(pathDirname(installPath));
    const bundlingClientApp =
      ! this.name && // Indicates we are bundling an app.
      archMatches(this.bundleArch, "web");

    for (let dir of dirs) {
      if (dir.charAt(0) === "." ||
          dir === "packages" ||
          dir === "programs" ||
          dir === "cordova-build-override") {
        // These directories are never loaded as part of an app.
        return;
      }

      if (bundlingClientApp && (dir === "server" ||
                                dir === "private")) {
        // If we're bundling an app for a client architecture, any files
        // contained by a server-only directory that is not contained by
        // a node_modules directory must be ignored.
        return;
      }

      if (dir === "node_modules") {
        if (! this._hasKnownExtension(installPath)) {
          // Reject any files within node_modules directories that do
          // not have one of the known extensions.
          return;
        }

        // Accept any file within a node_modules directory if it has a
        // known file extension.
        return installPath;
      }
    }

    return installPath;
  }

  _hasKnownExtension(path) {
    return has(extensions, pathExtname(path).toLowerCase());
  }

  _splitPath(path) {
    const partsInReverse = [];
    for (let dir; (dir = pathDirname(path)) !== path; path = dir) {
      partsInReverse.push(pathBasename(path));
    }
    return partsInReverse.reverse();
  }

  // TODO This method can probably be consolidated with _getInstallPath.
  _tryToResolveImportedPath(file, id) {
    let resolved =
      this._resolveAbsolute(file, id) ||
      this._resolveRelative(file, id) ||
      this._resolveNodeModule(file, id);

    while (resolved && resolved.stat.isDirectory()) {
      resolved = this._resolvePkgJsonMain(resolved.path) ||
        this._joinAndStat(resolved.path, "index.js");
    }

    return resolved && resolved.path;
  }

  _joinAndStat(...joinArgs) {
    const path = pathNormalize(pathJoin(...joinArgs));
    const exactStat = statOrNull(path);
    const exactResult = exactStat && { path, stat: exactStat };
    if (exactResult && exactStat.isFile()) {
      return exactResult;
    }

    for (let ext in extensions) {
      if (has(extensions, ext)) {
        const pathWithExt = path + ext;
        const stat = statOrNull(pathWithExt);
        if (stat) {
          return { path: pathWithExt, stat };
        }
      }
    }

    if (exactResult && exactStat.isDirectory()) {
      // After trying all available file extensions, fall back to the
      // original result if it was a directory.
      return exactResult;
    }

    return null;
  }

  _resolveAbsolute(file, id) {
    return id.charAt(0) === "/" &&
      this._joinAndStat(this.sourceRoot, id.slice(1));
  }

  _resolveRelative(file, id) {
    if (id.charAt(0) === ".") {
      return this._joinAndStat(
        this.sourceRoot, file.sourcePath, "..", id
      );
    }
  }

  _resolveNodeModule(file, id) {
    let resolved = null;
    let dir = pathJoin(this.sourceRoot, file.sourcePath);

    do {
      dir = pathDirname(dir);
      resolved = this._joinAndStat(dir, "node_modules", id);
    } while (! resolved && dir !== this.sourceRoot);

    if (! resolved && this.nodeModulesPath) {
      // After checking any local node_modules directories, fall back to
      // the package NPM directory, if one was specified.
      resolved = this._joinAndStat(this.nodeModulesPath, id);
    }

    if (! resolved) {
      const parts = id.split("/");
      if (parts[0] !== "meteor") { // Exclude meteor/... packages.
        // If the imported identifier is neither absolute nor relative,
        // but top-level, then it might be satisfied by a package
        // installed in the top-level node_modules directory, and we
        // should record the missing dependency so that we can include it
        // in the app bundle.
        const missing = file.missingNodeModules || Object.create(null);
        missing[id] = true;
        file.missingNodeModules = missing;
      }
    }

    // If the dependency is still not resolved, it might be handled by the
    // fallback function defined in meteor/packages/modules/modules.js, or
    // it might be imported in code that will never run on this platform,
    // so there is always the possibility that its absence is not actually
    // a problem. As much as we might like to issue warnings about missing
    // dependencies here, we just don't have enough information to make
    // that determination until the code actually runs.

    return resolved;
  }

  _resolvePkgJsonMain(dirPath) {
    const pkgJsonPath = pathJoin(dirPath, "package.json");

    let pkg;
    try {
      pkg = JSON.parse(this._readFile(pkgJsonPath).data);
    } catch (e) {
      return null;
    }

    if (pkg && isString(pkg.main)) {
      const resolved = this._joinAndStat(dirPath, pkg.main);
      if (resolved) {
        this._addPkgJsonToOutput(pkgJsonPath, pkg);
        return resolved;
      }
    }

    return null;
  }

  _addPkgJsonToOutput(pkgJsonPath, pkg) {
    if (! has(this.absPathToOutputIndex, pkgJsonPath)) {
      const data = new Buffer(
        // Output a JS module that exports just the "name" and "main"
        // properties defined in the package.json file.
        "exports.name = " + JSON.stringify(pkg.name) + ";\n" +
        "exports.version = " + JSON.stringify(pkg.version) + ";\n" +
        "exports.main = " + JSON.stringify(pkg.main) + ";\n"
      );

      const relPkgJsonPath = pathRelative(this.sourceRoot, pkgJsonPath);

      const pkgFile = {
        type: "js", // We represent the JSON module with JS.
        data,
        deps: [], // Avoid accidentally re-scanning this file.
        sourcePath: relPkgJsonPath,
        installPath: this._getInstallPath(pkgJsonPath),
        servePath: relPkgJsonPath,
        hash: sha1(data),
        lazy: true,
        imported: true,
      };

      this.absPathToOutputIndex[pkgJsonPath] =
        this.outputFiles.push(pkgFile) - 1;
    }
  }
}
<commit_msg>Update comment about package.json files.<commit_after>import assert from "assert";
import {isString, has, keys, each, without} from "underscore";
import {sha1, readAndWatchFileWithHash} from "../fs/watch.js";
import {matches as archMatches} from "../utils/archinfo.js";
import {findImportedModuleIdentifiers} from "./js-analyze.js";
import buildmessage from "../utils/buildmessage.js";
import {
  pathJoin,
  pathRelative,
  pathNormalize,
  pathDirname,
  pathBasename,
  pathExtname,
  statOrNull,
  convertToPosixPath,
} from "../fs/files.js";

// Default handlers for well-known file extensions.
const extensions = {
  ".js"(data) {
    return data;
  },

  ".json"(data) {
    return "module.exports = " +
      JSON.stringify(JSON.parse(data), null, 2) +
      ";\n";
  }
};

export default class ImportScanner {
  constructor({
    name,
    bundleArch,
    sourceRoot,
    usedPackageNames = {},
    nodeModulesPath,
    watchSet,
  }) {
    assert.ok(isString(sourceRoot));

    this.name = name;
    this.bundleArch = bundleArch;
    this.sourceRoot = sourceRoot;
    this.usedPackageNames = usedPackageNames;
    this.nodeModulesPath = nodeModulesPath;
    this.watchSet = watchSet;
    this.absPathToOutputIndex = {};
    this.outputFiles = [];
  }

  addInputFiles(files) {
    files.forEach(file => {
      const absPath = pathJoin(this.sourceRoot, file.sourcePath);

      // Files that are not eagerly evaluated (lazy) will only be included
      // in the bundle if they are actually imported. Files that are
      // eagerly evaluated are effectively "imported" as entry points.
      file.imported = ! file.lazy;

      file.installPath = this._getInstallPath(absPath);

      if (has(this.absPathToOutputIndex, absPath)) {
        const index = this.absPathToOutputIndex[absPath];
        this.outputFiles[index] = file;
      } else {
        this.absPathToOutputIndex[absPath] =
          this.outputFiles.push(file) - 1;
      }
    });

    this.outputFiles.forEach(file => {
      if (! file.lazy || file.imported) {
        this._scanFile(file);
      }
    });

    return this;
  }

  addNodeModules(identifiers) {
    if (identifiers) {
      if (typeof identifiers === "object" &&
          ! Array.isArray(identifiers)) {
        identifiers = Object.keys(identifiers);
      }

      if (identifiers.length > 0) {
        this._scanFile({
          sourcePath: "fake.js",
          // By specifying the .deps property of this fake file ahead of
          // time, we can avoid calling findImportedModuleIdentifiers in the
          // _scanFile method.
          deps: identifiers,
        });
      }
    }

    return this;
  }

  getOutputFiles(options) {
    return this.outputFiles;
  }

  _scanFile(file) {
    const absPath = pathJoin(this.sourceRoot, file.sourcePath);
    file.deps = file.deps || keys(
      findImportedModuleIdentifiers(file.data.toString("utf8"))
    );

    each(file.deps, id => {
      const absImportedPath = this._tryToResolveImportedPath(file, id);
      if (! absImportedPath) {
        return;
      }

      if (has(this.absPathToOutputIndex, absImportedPath)) {
        // Avoid scanning files that we've scanned before, but mark them
        // as imported so we know to include them in the bundle if they
        // are lazy.
        const index = this.absPathToOutputIndex[absImportedPath];
        const file = this.outputFiles[index];

        // Eager files and files that we have imported before do not need
        // to be scanned again. Lazy files that we have not imported
        // before still need to be scanned, however.
        const alreadyScanned = ! file.lazy || file.imported;

        // Whether the file is eager or lazy, mark it as imported. For
        // lazy files, this makes the difference between being included in
        // or omitted from the bundle. For eager files, this just ensures
        // we won't scan them again.
        file.imported = true;

        if (! alreadyScanned) {
          this._scanFile(file);
        }

        return;
      }

      if (! this._hasKnownExtension(absImportedPath)) {
        // The _readModule method provides hardcoded support for files
        // with known extensions, but any other type of file must be
        // ignored at this point, because it was not in the set of input
        // files and therefore must not have been processed by a compiler
        // plugin for the current architecture (this.bundleArch).
        return;
      }

      const installPath = this._getInstallPath(absImportedPath);
      if (! installPath) {
        // The given path cannot be installed on this architecture.
        return;
      }

      // The result of _readModule will have .data and .hash properties.
      const depFile = this._readModule(absImportedPath);
      depFile.type = "js"; // TODO Is this correct?
      depFile.sourcePath = pathRelative(this.sourceRoot, absImportedPath);
      depFile.installPath = installPath;
      depFile.servePath = installPath;
      depFile.lazy = true;
      depFile.imported = true;

      // Append this file to the output array and record its index.
      this.absPathToOutputIndex[absImportedPath] =
        this.outputFiles.push(depFile) - 1;

      this._scanFile(depFile);
    });
  }

  _readFile(absPath) {
    let { contents, hash } =
      readAndWatchFileWithHash(this.watchSet, absPath);

    return {
      data: contents.toString("utf8"),
      hash,
    };
  }

  _readModule(absPath) {
    const info = this._readFile(absPath);

    // Same logic/comment as stripBOM in node/lib/module.js:
    // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
    // because the buffer-to-string conversion in `fs.readFileSync()`
    // translates it to FEFF, the UTF-16 BOM.
    if (info.data.charCodeAt(0) === 0xfeff) {
      info.data = info.data.slice(1);
    }

    const ext = pathExtname(absPath).toLowerCase();
    info.data = extensions[ext](info.data);

    return info;
  }

  // Returns a relative path indicating where to install the given file
  // via meteorInstall. May return undefined if the file should not be
  // installed on the current architecture.
  _getInstallPath(absPath) {
    let path =
      this._getNodeModulesInstallPath(absPath) ||
      this._getSourceRootInstallPath(absPath);

    if (! path) {
      return;
    }

    if (this.name) {
      // If we're bundling a package, prefix path with
      // node_modules/<package name>/.
      path = pathJoin("node_modules", "meteor", this.name, path);
    }

    return path;
  }

  _getNodeModulesInstallPath(absPath) {
    if (this.nodeModulesPath) {
      const relPathWithinNodeModules =
        pathRelative(this.nodeModulesPath, absPath);

      if (relPathWithinNodeModules.startsWith("..")) {
        // absPath is not a subdirectory of this.nodeModulesPath.
        return;
      }

      if (! this._hasKnownExtension(relPathWithinNodeModules)) {
        // Only accept files within node_modules directories if they
        // have one of the known extensions.
        return;
      }

      // Install the module into the local node_modules directory within
      // this app or package.
      return pathJoin("node_modules", relPathWithinNodeModules);
    }
  }

  _getSourceRootInstallPath(absPath) {
    const installPath = pathRelative(this.sourceRoot, absPath);

    if (installPath.startsWith("..")) {
      // absPath is not a subdirectory of this.sourceRoot.
      return;
    }

    const dirs = this._splitPath(pathDirname(installPath));
    const bundlingClientApp =
      ! this.name && // Indicates we are bundling an app.
      archMatches(this.bundleArch, "web");

    for (let dir of dirs) {
      if (dir.charAt(0) === "." ||
          dir === "packages" ||
          dir === "programs" ||
          dir === "cordova-build-override") {
        // These directories are never loaded as part of an app.
        return;
      }

      if (bundlingClientApp && (dir === "server" ||
                                dir === "private")) {
        // If we're bundling an app for a client architecture, any files
        // contained by a server-only directory that is not contained by
        // a node_modules directory must be ignored.
        return;
      }

      if (dir === "node_modules") {
        if (! this._hasKnownExtension(installPath)) {
          // Reject any files within node_modules directories that do
          // not have one of the known extensions.
          return;
        }

        // Accept any file within a node_modules directory if it has a
        // known file extension.
        return installPath;
      }
    }

    return installPath;
  }

  _hasKnownExtension(path) {
    return has(extensions, pathExtname(path).toLowerCase());
  }

  _splitPath(path) {
    const partsInReverse = [];
    for (let dir; (dir = pathDirname(path)) !== path; path = dir) {
      partsInReverse.push(pathBasename(path));
    }
    return partsInReverse.reverse();
  }

  // TODO This method can probably be consolidated with _getInstallPath.
  _tryToResolveImportedPath(file, id) {
    let resolved =
      this._resolveAbsolute(file, id) ||
      this._resolveRelative(file, id) ||
      this._resolveNodeModule(file, id);

    while (resolved && resolved.stat.isDirectory()) {
      resolved = this._resolvePkgJsonMain(resolved.path) ||
        this._joinAndStat(resolved.path, "index.js");
    }

    return resolved && resolved.path;
  }

  _joinAndStat(...joinArgs) {
    const path = pathNormalize(pathJoin(...joinArgs));
    const exactStat = statOrNull(path);
    const exactResult = exactStat && { path, stat: exactStat };
    if (exactResult && exactStat.isFile()) {
      return exactResult;
    }

    for (let ext in extensions) {
      if (has(extensions, ext)) {
        const pathWithExt = path + ext;
        const stat = statOrNull(pathWithExt);
        if (stat) {
          return { path: pathWithExt, stat };
        }
      }
    }

    if (exactResult && exactStat.isDirectory()) {
      // After trying all available file extensions, fall back to the
      // original result if it was a directory.
      return exactResult;
    }

    return null;
  }

  _resolveAbsolute(file, id) {
    return id.charAt(0) === "/" &&
      this._joinAndStat(this.sourceRoot, id.slice(1));
  }

  _resolveRelative(file, id) {
    if (id.charAt(0) === ".") {
      return this._joinAndStat(
        this.sourceRoot, file.sourcePath, "..", id
      );
    }
  }

  _resolveNodeModule(file, id) {
    let resolved = null;
    let dir = pathJoin(this.sourceRoot, file.sourcePath);

    do {
      dir = pathDirname(dir);
      resolved = this._joinAndStat(dir, "node_modules", id);
    } while (! resolved && dir !== this.sourceRoot);

    if (! resolved && this.nodeModulesPath) {
      // After checking any local node_modules directories, fall back to
      // the package NPM directory, if one was specified.
      resolved = this._joinAndStat(this.nodeModulesPath, id);
    }

    if (! resolved) {
      const parts = id.split("/");
      if (parts[0] !== "meteor") { // Exclude meteor/... packages.
        // If the imported identifier is neither absolute nor relative,
        // but top-level, then it might be satisfied by a package
        // installed in the top-level node_modules directory, and we
        // should record the missing dependency so that we can include it
        // in the app bundle.
        const missing = file.missingNodeModules || Object.create(null);
        missing[id] = true;
        file.missingNodeModules = missing;
      }
    }

    // If the dependency is still not resolved, it might be handled by the
    // fallback function defined in meteor/packages/modules/modules.js, or
    // it might be imported in code that will never run on this platform,
    // so there is always the possibility that its absence is not actually
    // a problem. As much as we might like to issue warnings about missing
    // dependencies here, we just don't have enough information to make
    // that determination until the code actually runs.

    return resolved;
  }

  _resolvePkgJsonMain(dirPath) {
    const pkgJsonPath = pathJoin(dirPath, "package.json");

    let pkg;
    try {
      pkg = JSON.parse(this._readFile(pkgJsonPath).data);
    } catch (e) {
      return null;
    }

    if (pkg && isString(pkg.main)) {
      const resolved = this._joinAndStat(dirPath, pkg.main);
      if (resolved) {
        this._addPkgJsonToOutput(pkgJsonPath, pkg);
        return resolved;
      }
    }

    return null;
  }

  _addPkgJsonToOutput(pkgJsonPath, pkg) {
    if (! has(this.absPathToOutputIndex, pkgJsonPath)) {
      const data = new Buffer(
        // Output a JS module that exports just the "name", "version", and
        // "main" properties defined in the package.json file.
        "exports.name = " + JSON.stringify(pkg.name) + ";\n" +
        "exports.version = " + JSON.stringify(pkg.version) + ";\n" +
        "exports.main = " + JSON.stringify(pkg.main) + ";\n"
      );

      const relPkgJsonPath = pathRelative(this.sourceRoot, pkgJsonPath);

      const pkgFile = {
        type: "js", // We represent the JSON module with JS.
        data,
        deps: [], // Avoid accidentally re-scanning this file.
        sourcePath: relPkgJsonPath,
        installPath: this._getInstallPath(pkgJsonPath),
        servePath: relPkgJsonPath,
        hash: sha1(data),
        lazy: true,
        imported: true,
      };

      this.absPathToOutputIndex[pkgJsonPath] =
        this.outputFiles.push(pkgFile) - 1;
    }
  }
}
<|endoftext|>


<commit_before>goog.provide('recoil.ui.widgets.TextAreaWidget');

goog.require('goog.events.InputHandler');
goog.require('goog.ui.Component');
goog.require('goog.ui.Container');
goog.require('goog.ui.Textarea');
goog.require('goog.ui.TextareaRenderer');
goog.require('recoil.frp.Behaviour');
goog.require('recoil.frp.Util');
goog.require('recoil.frp.struct');
goog.require('recoil.ui.ComponentWidgetHelper');
goog.require('recoil.ui.EventHelper');
goog.require('recoil.ui.LabeledWidget');
goog.require('recoil.ui.Widget');
goog.require('recoil.ui.WidgetHelper');
goog.require('recoil.ui.WidgetScope');
goog.require('recoil.ui.widgets.LabelWidget');

/**
 * @implements {recoil.ui.LabeledWidget}
 * @param {!recoil.ui.WidgetScope} scope
 * @constructor
 */
recoil.ui.widgets.TextAreaWidget = function(scope) {
    this.scope_ = scope;
    this.textarea_ = new goog.ui.Textarea('');
    this.container_ = new goog.ui.Container();

    this.label_ = new recoil.ui.widgets.LabelWidget(scope);
    this.changeHelper_ = new recoil.ui.EventHelper(scope, this.textarea_, goog.events.InputHandler.EventType.INPUT);
    this.blurChangeHelper_ = new recoil.ui.EventHelper(scope, this.textarea_, goog.events.EventType.BLUR);
    this.helper_ = new recoil.ui.ComponentWidgetHelper(scope, this.textarea_, this, this.updateState_, this.detach_);
    this.configHelper_ = new recoil.ui.ComponentWidgetHelper(scope, this.textarea_, this, this.updateConfig_);
};

/**
 * if not immediate we need to put data back before we detach
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.detach_ = function() {
    var frp = this.helper_.getFrp();
    var me = this;
    frp.accessTrans(function() {
        if (me.immediateB_.good() && me.valueB_.good() && !me.immediateB_.get()) {
            me.valueB_.set(me.textarea_.getValue());
        }
    }, me.immediateB_, me.valueB_);
};

/**
 * all widgets should not allow themselves to be flatterned
 *
 * @type {!Object}
 */

recoil.ui.widgets.TextAreaWidget.prototype.flatten = recoil.frp.struct.NO_FLATTEN;
/**
 * @return {!goog.ui.Component}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getComponent = function() {
    return this.textarea_;
};

/**
 *
 * @return {goog.ui.Container}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getContainer = function() {
    return this.container_;
};

/**
 *
 * @return {recoil.ui.widgets.LabelWidget}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getLabel = function() {
    return this.label_;
};
/**
 * @param {recoil.frp.Behaviour<string>|string} nameB
 * @param {recoil.frp.Behaviour<string>|string} valueB
 * @param {!recoil.frp.Behaviour<!recoil.ui.BoolWithExplanation>=} opt_enabledB
 */
recoil.ui.widgets.TextAreaWidget.prototype.attach = function(nameB, valueB, opt_enabledB) {
    var frp = this.helper_.getFrp();

    this.attachStruct({'name': nameB, 'value': valueB, 'enabled': opt_enabledB });
};

/**
 * @param {!Object| !recoil.frp.Behaviour<Object>} options
 */
recoil.ui.widgets.TextAreaWidget.prototype.attachStruct = function(options) {
    var frp = this.helper_.getFrp();
    var util = new recoil.frp.Util(frp);

    var structs = recoil.frp.struct;
    var optionsB = structs.flatten(frp, options);

    this.maxLengthB_ = structs.get('maxLength', optionsB, null);
    this.valueB_ = structs.get('value', optionsB);
    this.minHeightB_ = structs.get('minHeight', optionsB, 70);
    this.immediateB_ = structs.get('immediate', optionsB, false);
    this.enabledB_ = structs.get('enabled', optionsB, recoil.ui.BoolWithExplanation.TRUE);
    this.editableB_ = structs.get('editable', optionsB, true);
    this.placeholderB_ = structs.get('placeholder', optionsB, null);
    var readyB = util.isAllGoodExplain(this.valueB_, this.enabledB_);

    this.label_.attach(
          structs.get('name', optionsB),
          recoil.ui.BoolWithExplanation.and(frp, this.enabledB_, readyB));

    this.helper_.attach(this.valueB_, this.immediateB_, this.enabledB_, this.editableB_, this.placeholderB_, this.maxLengthB_);
    this.configHelper_.attach(this.minHeightB_);

    var me = this;
    this.changeHelper_.listen(this.scope_.getFrp().createCallback(function(v) {
        if (me.immediateB_.get()) {
            me.valueB_.set(v.target.value);
        }
    }, this.valueB_, this.immediateB_));

    this.blurChangeHelper_.listen(this.scope_.getFrp().createCallback(
        function(v) {
            if (!me.immediateB_.get()) {
                me.valueB_.set(v.target.value);
            }
        }, this.valueB_, this.immediateB_));

};

/**
 *
 * @param {recoil.ui.WidgetHelper} helper
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.updateState_ = function(helper) {

    var len = this.maxLengthB_.good() && this.maxLengthB_.get() ? this.maxLengthB_.get() : undefined;
    if (!this.textarea_.getElement()) {
        this.textarea_.createDom();
    }
    this.textarea_.getElement().maxLength = len;

    this.textarea_.setPlaceholder(
        this.placeholderB_.good() && this.placeholderB_.get() ? this.placeholderB_.get() : '');
    if (helper.isGood()) {
        this.textarea_.setContent(this.valueB_.get());
        this.textarea_.setEnabled(this.enabledB_.get().val() && this.editableB_.get());
    }
    else {
        this.textarea_.setEnabled(false);
    }

};

/**
 *
 * @param {recoil.ui.WidgetHelper} helper
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.updateConfig_ = function(helper) {

    if (helper.isGood()) {
        this.textarea_.setMinHeight(this.minHeightB_.get());
    }

};

<commit_msg>CT-12387 when maxlength not specified it becomes 0 and no data can be entered<commit_after>goog.provide('recoil.ui.widgets.TextAreaWidget');

goog.require('goog.events.InputHandler');
goog.require('goog.ui.Component');
goog.require('goog.ui.Container');
goog.require('goog.ui.Textarea');
goog.require('goog.ui.TextareaRenderer');
goog.require('recoil.frp.Behaviour');
goog.require('recoil.frp.Util');
goog.require('recoil.frp.struct');
goog.require('recoil.ui.ComponentWidgetHelper');
goog.require('recoil.ui.EventHelper');
goog.require('recoil.ui.LabeledWidget');
goog.require('recoil.ui.Widget');
goog.require('recoil.ui.WidgetHelper');
goog.require('recoil.ui.WidgetScope');
goog.require('recoil.ui.widgets.LabelWidget');

/**
 * @implements {recoil.ui.LabeledWidget}
 * @param {!recoil.ui.WidgetScope} scope
 * @constructor
 */
recoil.ui.widgets.TextAreaWidget = function(scope) {
    this.scope_ = scope;
    this.textarea_ = new goog.ui.Textarea('');
    this.container_ = new goog.ui.Container();

    this.label_ = new recoil.ui.widgets.LabelWidget(scope);
    this.changeHelper_ = new recoil.ui.EventHelper(scope, this.textarea_, goog.events.InputHandler.EventType.INPUT);
    this.blurChangeHelper_ = new recoil.ui.EventHelper(scope, this.textarea_, goog.events.EventType.BLUR);
    this.helper_ = new recoil.ui.ComponentWidgetHelper(scope, this.textarea_, this, this.updateState_, this.detach_);
    this.configHelper_ = new recoil.ui.ComponentWidgetHelper(scope, this.textarea_, this, this.updateConfig_);
};

/**
 * if not immediate we need to put data back before we detach
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.detach_ = function() {
    var frp = this.helper_.getFrp();
    var me = this;
    frp.accessTrans(function() {
        if (me.immediateB_.good() && me.valueB_.good() && !me.immediateB_.get()) {
            me.valueB_.set(me.textarea_.getValue());
        }
    }, me.immediateB_, me.valueB_);
};

/**
 * all widgets should not allow themselves to be flatterned
 *
 * @type {!Object}
 */

recoil.ui.widgets.TextAreaWidget.prototype.flatten = recoil.frp.struct.NO_FLATTEN;
/**
 * @return {!goog.ui.Component}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getComponent = function() {
    return this.textarea_;
};

/**
 *
 * @return {goog.ui.Container}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getContainer = function() {
    return this.container_;
};

/**
 *
 * @return {recoil.ui.widgets.LabelWidget}
 */
recoil.ui.widgets.TextAreaWidget.prototype.getLabel = function() {
    return this.label_;
};
/**
 * @param {recoil.frp.Behaviour<string>|string} nameB
 * @param {recoil.frp.Behaviour<string>|string} valueB
 * @param {!recoil.frp.Behaviour<!recoil.ui.BoolWithExplanation>=} opt_enabledB
 */
recoil.ui.widgets.TextAreaWidget.prototype.attach = function(nameB, valueB, opt_enabledB) {
    var frp = this.helper_.getFrp();

    this.attachStruct({'name': nameB, 'value': valueB, 'enabled': opt_enabledB });
};

/**
 * @param {!Object| !recoil.frp.Behaviour<Object>} options
 */
recoil.ui.widgets.TextAreaWidget.prototype.attachStruct = function(options) {
    var frp = this.helper_.getFrp();
    var util = new recoil.frp.Util(frp);

    var structs = recoil.frp.struct;
    var optionsB = structs.flatten(frp, options);

    this.maxLengthB_ = structs.get('maxLength', optionsB, null);
    this.valueB_ = structs.get('value', optionsB);
    this.minHeightB_ = structs.get('minHeight', optionsB, 70);
    this.immediateB_ = structs.get('immediate', optionsB, false);
    this.enabledB_ = structs.get('enabled', optionsB, recoil.ui.BoolWithExplanation.TRUE);
    this.editableB_ = structs.get('editable', optionsB, true);
    this.placeholderB_ = structs.get('placeholder', optionsB, null);
    var readyB = util.isAllGoodExplain(this.valueB_, this.enabledB_);

    this.label_.attach(
          structs.get('name', optionsB),
          recoil.ui.BoolWithExplanation.and(frp, this.enabledB_, readyB));

    this.helper_.attach(this.valueB_, this.immediateB_, this.enabledB_, this.editableB_, this.placeholderB_, this.maxLengthB_);
    this.configHelper_.attach(this.minHeightB_);

    var me = this;
    this.changeHelper_.listen(this.scope_.getFrp().createCallback(function(v) {
        if (me.immediateB_.get()) {
            me.valueB_.set(v.target.value);
        }
    }, this.valueB_, this.immediateB_));

    this.blurChangeHelper_.listen(this.scope_.getFrp().createCallback(
        function(v) {
            if (!me.immediateB_.get()) {
                me.valueB_.set(v.target.value);
            }
        }, this.valueB_, this.immediateB_));

};

/**
 *
 * @param {recoil.ui.WidgetHelper} helper
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.updateState_ = function(helper) {

    var len = this.maxLengthB_.good() && this.maxLengthB_.get() ? this.maxLengthB_.get() : undefined;
    if (!this.textarea_.getElement()) {
        this.textarea_.createDom();
    }
    if (len) {
        this.textarea_.getElement().maxLength = len;
    }
    else {
        this.textarea_.getElement().removeAttribute('maxlength');
    }

    this.textarea_.setPlaceholder(
        this.placeholderB_.good() && this.placeholderB_.get() ? this.placeholderB_.get() : '');
    if (helper.isGood()) {
        this.textarea_.setContent(this.valueB_.get());
        this.textarea_.setEnabled(this.enabledB_.get().val() && this.editableB_.get());
    }
    else {
        this.textarea_.setEnabled(false);
    }

};

/**
 *
 * @param {recoil.ui.WidgetHelper} helper
 * @private
 */
recoil.ui.widgets.TextAreaWidget.prototype.updateConfig_ = function(helper) {

    if (helper.isGood()) {
        this.textarea_.setMinHeight(this.minHeightB_.get());
    }

};

<|endoftext|>


<commit_before>import Component from '@ember/component';
import { observer } from '@ember/object';
import { inject as service } from '@ember/service';
import {
  formatSi, parseSi, exponentNeeded
} from 'shared/utils/parse-unit';
import layout from './template';

export default Component.extend({
  intl: service(),

  layout,

  nodes: null,

  gauges: null,

  updateDashboard: observer('nodes.@each.{allocatable,requested}', function() {

    this.setDashboard();

  }),

  init() {

    this._super(...arguments);
    setTimeout(() => {

      this.setDashboard();

    }, 150);

  },

  setDashboard() {

    const cpuGauge = this.getCpuGauge();
    const memoryGauge = this.getMemoryGauge();
    const podsGauge = this.getPodsGauge();

    this.set('gauges', [cpuGauge, memoryGauge, podsGauge]);

  },

  getCpuGauge() {

    return this.getGauge('cpu',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'reserved',
    );

  },

  getMemoryGauge() {

    return this.getGauge('memory',
      (u, t) => formatSi(u, 1024, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1024, 'iB', 'B', 0, exponentNeeded(t), 1), 'reserved',
    );

  },

  getPodsGauge() {

    return this.getGauge('pods',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'used',
    );

  },

  getGauge(field, usedFormatCb, totalFormatCb, keyword) {

    const nodes = this.getNodes(field);

    return {
      value:    this.getValue(nodes),
      title:    this.get('intl').t(`clusterDashboard.${ field }`),
      subtitle: this.getSubtitle(nodes, totalFormatCb, usedFormatCb, keyword),
      ticks:    this.getTicks(nodes),
    };

  },

  getTicks(nodes) {

    let filtered = [];

    if (nodes.length > 0) {

      const min = nodes[0].value;
      const max = nodes[nodes.length - 1].value;

      filtered = nodes.filter((node) => node.value === min || node.value === max);

    }
    const ticks = [];

    filtered.forEach((node) => {

      const found = ticks.find((tick) => tick.value === Math.round(node.value));

      if (found) {

        found.labels.push(node.node.nodeName);

      } else {

        ticks.push({
          value:  Math.round(node.value),
          labels: [node.node.nodeName],
        });

      }

    });

    return ticks;

  },

  getNodes(field) {

    return (this.get('nodes') || []).map((node) => {

      const tValue = node.allocatable && node.allocatable[field] ? node.allocatable[field] : '0';
      const uValue = node.requested && node.requested[field] ? node.requested[field] : '0';

      let total = parseSi(tValue);
      let used = parseSi(uValue);

      if (isNaN(total) || isNaN(used)) {

        used = 0;
        total = 0;

      }

      let value = 0;

      if (total > 0) {

        value = used * 100 / total;

      }

      return {
        node,
        used,
        total,
        value,
      };

    }).sortBy('value');

  },

  getValue(nodes) {

    let used = 0;
    let total = 0;

    nodes.forEach((node) => {

      total += node.total;
      used += node.used;

    });
    const value = Math.round(used * 100 / total);

    return isNaN(value) ? 0 : value;

  },

  getSubtitle(nodes, totalCb, usedCb, keyword) {

    let used = 0;
    let total = 0;

    nodes.forEach((node) => {

      total += node.total;
      used += node.used;

    });

    return this.get('intl').t(`clusterDashboard.subtitle.${ keyword }`, {
      used:  usedCb ? usedCb(used, total) : used,
      total: totalCb ? totalCb(total) : total,
    });

  },
});<commit_msg>Update node gauge when selected language changed<commit_after>import Component from '@ember/component';
import { observer } from '@ember/object';
import { inject as service } from '@ember/service';
import {
  formatSi, parseSi, exponentNeeded
} from 'shared/utils/parse-unit';
import layout from './template';

export default Component.extend({
  intl: service(),

  layout,

  nodes: null,

  gauges: null,

  updateDashboard: observer('nodes.@each.{allocatable,requested}', 'intl.locale', function() {

    this.setDashboard();

  }),

  init() {

    this._super(...arguments);
    setTimeout(() => {

      this.setDashboard();

    }, 150);

  },

  setDashboard() {

    const cpuGauge = this.getCpuGauge();
    const memoryGauge = this.getMemoryGauge();
    const podsGauge = this.getPodsGauge();

    this.set('gauges', [cpuGauge, memoryGauge, podsGauge]);

  },

  getCpuGauge() {

    return this.getGauge('cpu',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'reserved',
    );

  },

  getMemoryGauge() {

    return this.getGauge('memory',
      (u, t) => formatSi(u, 1024, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1024, 'iB', 'B', 0, exponentNeeded(t), 1), 'reserved',
    );

  },

  getPodsGauge() {

    return this.getGauge('pods',
      (u, t) => formatSi(u, 1000, '', '', 0, exponentNeeded(t), 1).replace(/\s.*$/, ''),
      (t) => formatSi(t, 1000, '', '', 0, exponentNeeded(t), 1), 'used',
    );

  },

  getGauge(field, usedFormatCb, totalFormatCb, keyword) {

    const nodes = this.getNodes(field);

    return {
      value:    this.getValue(nodes),
      title:    this.get('intl').t(`clusterDashboard.${ field }`),
      subtitle: this.getSubtitle(nodes, totalFormatCb, usedFormatCb, keyword),
      ticks:    this.getTicks(nodes),
    };

  },

  getTicks(nodes) {

    let filtered = [];

    if (nodes.length > 0) {

      const min = nodes[0].value;
      const max = nodes[nodes.length - 1].value;

      filtered = nodes.filter((node) => node.value === min || node.value === max);

    }
    const ticks = [];

    filtered.forEach((node) => {

      const found = ticks.find((tick) => tick.value === Math.round(node.value));

      if (found) {

        found.labels.push(node.node.nodeName);

      } else {

        ticks.push({
          value:  Math.round(node.value),
          labels: [node.node.nodeName],
        });

      }

    });

    return ticks;

  },

  getNodes(field) {

    return (this.get('nodes') || []).map((node) => {

      const tValue = node.allocatable && node.allocatable[field] ? node.allocatable[field] : '0';
      const uValue = node.requested && node.requested[field] ? node.requested[field] : '0';

      let total = parseSi(tValue);
      let used = parseSi(uValue);

      if (isNaN(total) || isNaN(used)) {

        used = 0;
        total = 0;

      }

      let value = 0;

      if (total > 0) {

        value = used * 100 / total;

      }

      return {
        node,
        used,
        total,
        value,
      };

    }).sortBy('value');

  },

  getValue(nodes) {

    let used = 0;
    let total = 0;

    nodes.forEach((node) => {

      total += node.total;
      used += node.used;

    });
    const value = Math.round(used * 100 / total);

    return isNaN(value) ? 0 : value;

  },

  getSubtitle(nodes, totalCb, usedCb, keyword) {

    let used = 0;
    let total = 0;

    nodes.forEach((node) => {

      total += node.total;
      used += node.used;

    });

    return this.get('intl').t(`clusterDashboard.subtitle.${ keyword }`, {
      used:  usedCb ? usedCb(used, total) : used,
      total: totalCb ? totalCb(total) : total,
    });

  },
});<|endoftext|>


<commit_before>angular
    .module('EventsSlides')
    .controller('appController', AppController);

function AppController($scope, $http, $interval, $sce) {
    $scope.isLoading = true;
    $scope.now = null;
    $scope.formateDate = formateDate;
    $scope.formatTimeRange = formatTimeRange;
    $scope.eventsList = getInitialEventsList();
    $scope.displayedEventsList = [];

    var perPage = 30;
    var eventsUpdateInterval = 30 * 1000; // Shift displayed events list
    var eventsUpdateIntervalId = 0;
    var loadAllEventsInterval = 30 * 60 * 1000; // Reload events list each half ah hour
    var displayedEventsListOffset = 0;
    var displayedEventsListCount = 3;

    init();

    function init() {
        if ($scope.eventsList.length) {
            play();
        }
        loadAllEvents();
        $interval(loadAllEvents, loadAllEventsInterval);
    }

    function getInitialEventsList() {
        var events = [];
        if (window.localStorage && localStorage.getItem('eventsList')) {
            events = JSON.parse(localStorage.getItem('eventsList'));
            events = modifyRawEvents(events);
        }
        return events;
    }

    function updateEventsList(events) {
        $scope.eventsList = events;
        if (window.localStorage) {
            localStorage.setItem('eventsList', JSON.stringify(events));
        }
    }

    function loadAllEvents() {
        var eventsList = [];
        $scope.now = moment();
        $scope.isLoading = true;
        function afterPageLoading(page, newEvents) {
            newEvents = filterEvents(newEvents);
            eventsList = eventsList.concat(newEvents);
            if (isLoadNext(newEvents)) {
                var promise = loadEventsPage(page + 1);
                if (promise) {
                    promise.then(afterPageLoading.bind(this, page + 1));
                }
            } else {
                $scope.isLoading = false;
                displayedEventsListOffset = 0;
                updateEventsList(eventsList);
                play();
            }
        }
        function isLoadNext(events) {
            var tomorrowEvents = events.filter(function (event) {
                return event.startTime.isSame($scope.now, 'day');
            });
            return events.length == perPage && tomorrowEvents.length == 0;
        }
        function filterEvents(newEvents) {
            var targetLocationNames = [
                'Палац Потоцьких',
                'Палац мистецтв',
                'музей книги'
            ].map(function (name) {
                return name.toLowerCase();
            });
            return newEvents
                .filter(function (event) {
                    return event.startTime.isSame($scope.now, 'day') &&
                           event.startTime.diff($scope.now) > 0;
                })
                .filter(function (event) {
                    var loc = event.locationName.toLowerCase();
                    for (var i = 0; i < targetLocationNames.length; i++) {
                        if (loc.indexOf(targetLocationNames[i]) !== -1) {
                            return true;
                        }
                    }
                    return false;
                });
        }
        loadEventsPage(1).then(afterPageLoading.bind(this, 1));
    }

    function loadEventsPage(page) {
        page = page || 1;
        return $scope.isLoading ? $http({
            method: 'GET',
            url: '/api/get_events?page=' + page
        }).then(function (response) {
            var newEvents = modifyRawEvents(response.data)
            return newEvents;
        }, function () {
            console.log('Events loading error');
            $scope.isLoading = false;
        }) : false;
    }

    function modifyRawEvents(events) {
        return events.map(function (event) {
            event.startTime = moment(event.startTime);
            event.endTime = moment(event.endTime);
            return event;
        });
    }

    function play() {
        $interval.cancel(eventsUpdateIntervalId);
        $scope.displayedEventsList = $scope.eventsList.slice(0, displayedEventsListCount);
        if ($scope.eventsList.length > displayedEventsListCount) {
            eventsUpdateIntervalId = $interval(playIteration, eventsUpdateInterval);
        }
    }

    function playIteration() {
        if (displayedEventsListOffset < $scope.eventsList.length) {
            displayedEventsListOffset++;
        } else {
            displayedEventsListOffset = -1 * displayedEventsListCount;
        }
        $scope.displayedEventsList = $scope.displayedEventsList.slice(1, displayedEventsListCount);
        var index = (displayedEventsListOffset + displayedEventsListCount) % $scope.eventsList.length;
        $scope.displayedEventsList.push($scope.eventsList[index]);
    }

    function formateDate(date) {
        var day = date.date();
        var month = date.month();
        var months = ['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Черв',
            'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'];
        return $sce.trustAsHtml('
' + day + '
' + months[month] + '');
    }

    function formatTimeRange(time1, time2) {
        return time1.format('HH:mm') + ' - ' + time2.format('HH:mm');
    }
}<commit_msg>Fixed issue with detecting is still need to load next page<commit_after>angular
    .module('EventsSlides')
    .controller('appController', AppController);

function AppController($scope, $http, $interval, $sce) {
    $scope.isLoading = true;
    $scope.now = moment();
    $scope.formateDate = formateDate;
    $scope.formatTimeRange = formatTimeRange;
    $scope.eventsList = getInitialEventsList();
    $scope.displayedEventsList = [];

    var perPage = 30;
    var eventsUpdateInterval = 30 * 1000; // Shift displayed events list
    var eventsUpdateIntervalId = 0;
    var loadAllEventsInterval = 30 * 60 * 1000; // Reload events list each half ah hour
    var displayedEventsListOffset = 0;
    var displayedEventsListCount = 3;

    init();

    function init() {
        if ($scope.eventsList.length) {
            play();
        }
        loadAllEvents();
        $interval(loadAllEvents, loadAllEventsInterval);
    }

    function getInitialEventsList() {
        var events = [];
        if (window.localStorage && localStorage.getItem('eventsList')) {
            events = JSON.parse(localStorage.getItem('eventsList'));
            events = modifyRawEvents(events);
        }
        return events;
    }

    function updateEventsList(events) {
        $scope.eventsList = events;
        if (window.localStorage) {
            localStorage.setItem('eventsList', JSON.stringify(events));
        }
    }

    function loadAllEvents() {
        var eventsList = [];
        $scope.now = moment();
        $scope.isLoading = true;
        function afterPageLoading(page, newEvents) {
            eventsList = eventsList.concat(newEvents);
            if (isLoadNext(newEvents)) {
                var promise = loadEventsPage(page + 1);
                if (promise) {
                    promise.then(afterPageLoading.bind(this, page + 1));
                }
            } else {
                $scope.isLoading = false;
                displayedEventsListOffset = 0;
                eventsList = filterEvents(eventsList);
                updateEventsList(eventsList);
                play();
            }
        }
        function isLoadNext(events) {
            var tomorrowEvents = events.filter(function (event) {
                return !event.startTime.isSame($scope.now, 'day') &&
                        event.startTime.diff($scope.now) > 0;
            });
            return events.length == perPage && tomorrowEvents.length == 0;
        }
        function filterEvents(newEvents) {
            var targetLocationNames = [
                'Палац Потоцьких',
                'Палац мистецтв',
                'музей книги'
            ].map(function (name) {
                return name.toLowerCase();
            });
            return newEvents
                .filter(function (event) {
                    return event.startTime.isSame($scope.now, 'day') &&
                           event.endTime.diff($scope.now) > 0;
                })
                .filter(function (event) {
                    var loc = event.locationName.toLowerCase();
                    for (var i = 0; i < targetLocationNames.length; i++) {
                        if (loc.indexOf(targetLocationNames[i]) !== -1) {
                            return true;
                        }
                    }
                    return false;
                });
        }
        loadEventsPage(1).then(afterPageLoading.bind(this, 1));
    }

    function loadEventsPage(page) {
        page = page || 1;
        return $scope.isLoading ? $http({
            method: 'GET',
            url: '/api/get_events?page=' + page
        }).then(function (response) {
            return modifyRawEvents(response.data);
        }, function () {
            console.log('Events loading error');
            $scope.isLoading = false;
        }) : false;
    }

    function modifyRawEvents(events) {
        return events.map(function (event) {
            event.startTime = moment(event.startTime);
            event.endTime = moment(event.endTime);
            return event;
        });
    }

    function play() {
        $interval.cancel(eventsUpdateIntervalId);
        $scope.displayedEventsList = $scope.eventsList.slice(0, displayedEventsListCount);
        if ($scope.eventsList.length > displayedEventsListCount) {
            eventsUpdateIntervalId = $interval(playIteration, eventsUpdateInterval);
        }
    }

    function playIteration() {
        if (displayedEventsListOffset < $scope.eventsList.length) {
            displayedEventsListOffset++;
        } else {
            displayedEventsListOffset = -1 * displayedEventsListCount;
        }
        $scope.displayedEventsList = $scope.displayedEventsList.slice(1, displayedEventsListCount);
        var index = (displayedEventsListOffset + displayedEventsListCount) % $scope.eventsList.length;
        $scope.displayedEventsList.push($scope.eventsList[index]);
    }

    function formateDate(date) {
        var day = date.date();
        var month = date.month();
        var months = ['Січ', 'Лют', 'Бер', 'Квіт', 'Трав', 'Черв',
            'Лип', 'Серп', 'Вер', 'Жовт', 'Лист', 'Груд'];
        return $sce.trustAsHtml('
' + day + '
' + months[month] + '');
    }

    function formatTimeRange(time1, time2) {
        return time1.format('HH:mm') + ' - ' + time2.format('HH:mm');
    }
}<|endoftext|>


<commit_before>// @flow

import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import styled from 'styled-components';

import Button from '../../shared/Button';
import Dropdown from '../../shared/Dropdown';
import Emojify from '../../shared/Emojify';
import Icon from '../../shared/Icon';

import CreateBuildDialog from '../CreateBuildDialog';
import Builds from './builds';

import permissions from '../../../lib/permissions';
import { repositoryProviderIcon } from '../../../lib/repositories';

const HeaderVitals = styled.div.attrs({
  className: 'flex flex-auto items-center my2'
})`
  flex-basis: 100%;

  @media (min-width: 768px) {
    flex-basis: 320px;
  }
`;

const HeaderBuilds = styled(Builds)`
  flex: 1 1 auto;
  margin-bottom: 5px;

  @media (min-width: 768px) and (max-width: 991px) {
    order: 3;
  }

  @media (min-width: 992px) {
    flex: 0 1 auto;
    margin-bottom: 0;
    margin-left: 10px;
  }
`;

type Props = {
  pipeline: Object,
  isCurrentOrganizationMember: boolean,
  buildState?: string
};

type State = {
  showingActionsDropdown: boolean,
  showingCreateBuildDialog: boolean
};

class Header extends React.Component<Props, State> {
  static propTypes = {
    pipeline: PropTypes.object.isRequired,
    isCurrentOrganizationMember: PropTypes.bool,
    buildState: PropTypes.string
  };

  state = {
    showingActionsDropdown: false,
    showingCreateBuildDialog: false
  };

  actionsDropdown: ?Dropdown;

  componentWillMount() {
    if (window.location.hash.split('?').shift() === '#new') {
      this.setState({
        showingCreateBuildDialog: true
      });
    }
  }

  render() {
    const actions = this.getAvailableActions();

    return (

          <HeaderVitals>

                {this.renderPipelineName()}

                  {this.renderDescription()}

            {this.renderProviderBadge()}
            {this.renderDropdownForActions(actions)}
          </HeaderVitals>
          <HeaderBuilds
            pipeline={this.props.pipeline}
            buildState={this.props.buildState}
          />
          {this.renderButtonsForActions(actions)}

        <CreateBuildDialog
          isOpen={this.state.showingCreateBuildDialog}
          onRequestClose={this.handleCreateBuildDialogClose}
          pipeline={this.props.pipeline}
        />

    );
  }

  renderPipelineName() {
    return (

        {this.props.isCurrentOrganizationMember || <Emojify text={this.props.pipeline.organization.name} />}
        {this.props.isCurrentOrganizationMember ||  / }
        <Emojify text={this.props.pipeline.name} />

    );
  }

  renderProviderBadge() {
    const uri = this.props.pipeline.repository.provider.url;

    if (!uri) {
      return null;
    }

    return (

        <Icon
          title={uri}
          icon={repositoryProviderIcon(this.props.pipeline.repository.provider.__typename)}
        />

    );
  }

  getAvailableActions() {
    // NOTE: We "render" props here so we can show near-identical
    // action lists in the dropdown and header. `children` is assumed
    // to be any renderable thing. Have at it!
    return permissions(this.props.pipeline.permissions).collect(
      {
        allowed: "buildCreate",
        render: () => ({
          key: 'newBuild',
          onClick: this.handleBuildCreateClick,
          children: 'New Build'
        })
      },
      {
        allowed: "pipelineUpdate",
        render: () => ({
          key: 'pipelineSettings',
          href: `${this.props.pipeline.url}/settings`,
          children: 'Pipeline Settings'
        })
      }
    );
  }

  renderDropdownForActions(actions) {
    if (actions.length < 1) {
      return;
    }

    const content = actions.map(({ key, ...action }) => (

    ));

    return (
      <Dropdown
        className="sm-hide md-hide lg-hide ml2"
        width={200}
        ref={(actionsDropdown) => this.actionsDropdown = actionsDropdown}
        onToggle={this.handleActionsDropdownToggle}
      >
        <Button
          outline={true}
          theme="default"
          className={classNames({ lime: this.state.showingActionsDropdown })}
          iconOnly={true}
        >
          <Icon icon="down-triangle" style={{ width: 7, height: 7 }} className="flex-none" />
        </Button>
        {content}
      </Dropdown>
    );
  }

  renderButtonsForActions(actions) {
    if (actions.length < 1) {
      return;
    }

    const content = actions.map(({ key, ...action }) => (
      <Button
        key={key}
        outline={true}
        theme="default"
        className="ml2 flex items-center"
        {...action}
      />
    ));

    return (

        {content}

    );
  }

  renderDescription() {
    const { pipeline } = this.props;

    if (pipeline.description) {
      return <Emojify text={pipeline.description} />;
    }

    // Hide passwords and limit to 8 dots in repository URLs
    return pipeline.repository.url.replace(
      /:([^\/@]{1,8})[^\/@]*@/,
      (match, password) => `:${password.replace(/./g, '•')}@`
    );
  }

  handleActionsDropdownToggle = (visible) => {
    this.setState({ showingActionsDropdown: visible });
  };

  handleBuildCreateClick = () => {
    if (this.state.showingActionsDropdown && this.actionsDropdown) {
      this.actionsDropdown.setShowing(false);
    }

    this.setState({ showingCreateBuildDialog: true });
  };

  handleCreateBuildDialogClose = () => {
    this.setState({ showingCreateBuildDialog: false });
  };
}

export default Relay.createContainer(Header, {
  fragments: {
    pipeline: () => Relay.QL`
      fragment on Pipeline {
        ${CreateBuildDialog.getFragment('pipeline')}
        ${Builds.getFragment('pipeline')}
        id
        name
        description
        url
        organization {
          name
        }
        repository {
          url
          provider {
            __typename
            url
          }
        }
        permissions {
          pipelineUpdate {
            allowed
            code
            message
          }
          buildCreate {
            allowed
            code
            message
          }
        }
      }
    `
  }
});
<commit_msg>Always show the pipeline settings button<commit_after>// @flow

import classNames from 'classnames';
import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';
import styled from 'styled-components';

import Button from '../../shared/Button';
import Dropdown from '../../shared/Dropdown';
import Emojify from '../../shared/Emojify';
import Icon from '../../shared/Icon';

import CreateBuildDialog from '../CreateBuildDialog';
import Builds from './builds';

import permissions from '../../../lib/permissions';
import { repositoryProviderIcon } from '../../../lib/repositories';

const HeaderVitals = styled.div.attrs({
  className: 'flex flex-auto items-center my2'
})`
  flex-basis: 100%;

  @media (min-width: 768px) {
    flex-basis: 320px;
  }
`;

const HeaderBuilds = styled(Builds)`
  flex: 1 1 auto;
  margin-bottom: 5px;

  @media (min-width: 768px) and (max-width: 991px) {
    order: 3;
  }

  @media (min-width: 992px) {
    flex: 0 1 auto;
    margin-bottom: 0;
    margin-left: 10px;
  }
`;

type Props = {
  pipeline: Object,
  isCurrentOrganizationMember: boolean,
  buildState?: string
};

type State = {
  showingActionsDropdown: boolean,
  showingCreateBuildDialog: boolean
};

class Header extends React.Component<Props, State> {
  static propTypes = {
    pipeline: PropTypes.object.isRequired,
    isCurrentOrganizationMember: PropTypes.bool,
    buildState: PropTypes.string
  };

  state = {
    showingActionsDropdown: false,
    showingCreateBuildDialog: false
  };

  actionsDropdown: ?Dropdown;

  componentWillMount() {
    if (window.location.hash.split('?').shift() === '#new') {
      this.setState({
        showingCreateBuildDialog: true
      });
    }
  }

  render() {
    const actions = this.getAvailableActions();

    return (

          <HeaderVitals>

                {this.renderPipelineName()}

                  {this.renderDescription()}

            {this.renderProviderBadge()}
            {this.renderDropdownForActions(actions)}
          </HeaderVitals>
          <HeaderBuilds
            pipeline={this.props.pipeline}
            buildState={this.props.buildState}
          />
          {this.renderButtonsForActions(actions)}

        <CreateBuildDialog
          isOpen={this.state.showingCreateBuildDialog}
          onRequestClose={this.handleCreateBuildDialogClose}
          pipeline={this.props.pipeline}
        />

    );
  }

  renderPipelineName() {
    return (

        {this.props.isCurrentOrganizationMember || <Emojify text={this.props.pipeline.organization.name} />}
        {this.props.isCurrentOrganizationMember ||  / }
        <Emojify text={this.props.pipeline.name} />

    );
  }

  renderProviderBadge() {
    const uri = this.props.pipeline.repository.provider.url;

    if (!uri) {
      return null;
    }

    return (

        <Icon
          title={uri}
          icon={repositoryProviderIcon(this.props.pipeline.repository.provider.__typename)}
        />

    );
  }

  getAvailableActions() {
    // NOTE: We "render" props here so we can show near-identical
    // action lists in the dropdown and header. `children` is assumed
    // to be any renderable thing. Have at it!
    return permissions(this.props.pipeline.permissions).collect(
      {
        allowed: "buildCreate",
        render: () => ({
          key: 'newBuild',
          onClick: this.handleBuildCreateClick,
          children: 'New Build'
        })
      },
      {
        always: true,
        render: () => ({
          key: 'pipelineSettings',
          href: `${this.props.pipeline.url}/settings`,
          children: 'Pipeline Settings'
        })
      }
    );
  }

  renderDropdownForActions(actions) {
    if (actions.length < 1) {
      return;
    }

    const content = actions.map(({ key, ...action }) => (

    ));

    return (
      <Dropdown
        className="sm-hide md-hide lg-hide ml2"
        width={200}
        ref={(actionsDropdown) => this.actionsDropdown = actionsDropdown}
        onToggle={this.handleActionsDropdownToggle}
      >
        <Button
          outline={true}
          theme="default"
          className={classNames({ lime: this.state.showingActionsDropdown })}
          iconOnly={true}
        >
          <Icon icon="down-triangle" style={{ width: 7, height: 7 }} className="flex-none" />
        </Button>
        {content}
      </Dropdown>
    );
  }

  renderButtonsForActions(actions) {
    if (actions.length < 1) {
      return;
    }

    const content = actions.map(({ key, ...action }) => (
      <Button
        key={key}
        outline={true}
        theme="default"
        className="ml2 flex items-center"
        {...action}
      />
    ));

    return (

        {content}

    );
  }

  renderDescription() {
    const { pipeline } = this.props;

    if (pipeline.description) {
      return <Emojify text={pipeline.description} />;
    }

    // Hide passwords and limit to 8 dots in repository URLs
    return pipeline.repository.url.replace(
      /:([^\/@]{1,8})[^\/@]*@/,
      (match, password) => `:${password.replace(/./g, '•')}@`
    );
  }

  handleActionsDropdownToggle = (visible) => {
    this.setState({ showingActionsDropdown: visible });
  };

  handleBuildCreateClick = () => {
    if (this.state.showingActionsDropdown && this.actionsDropdown) {
      this.actionsDropdown.setShowing(false);
    }

    this.setState({ showingCreateBuildDialog: true });
  };

  handleCreateBuildDialogClose = () => {
    this.setState({ showingCreateBuildDialog: false });
  };
}

export default Relay.createContainer(Header, {
  fragments: {
    pipeline: () => Relay.QL`
      fragment on Pipeline {
        ${CreateBuildDialog.getFragment('pipeline')}
        ${Builds.getFragment('pipeline')}
        id
        name
        description
        url
        organization {
          name
        }
        repository {
          url
          provider {
            __typename
            url
          }
        }
        permissions {
          pipelineUpdate {
            allowed
            code
            message
          }
          buildCreate {
            allowed
            code
            message
          }
        }
      }
    `
  }
});
<|endoftext|>