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

foam.CLASS({
  package: 'foam.u2.search',
  name: 'TextSearchView',
  extends: 'foam.u2.View',

  requires: [
    'foam.mlang.predicate.True',
    'foam.mlang.predicate.False',
    'foam.parse.QueryParser',
    'foam.u2.tag.Input'
  ],

  imports: [
    'filterController'
  ],

  implements: [
    'foam.mlang.Expressions'
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'Boolean',
      name: 'richSearch',
      value: false
    },
    {
      class: 'Boolean',
      name: 'keywordSearch',
      value: false
    },
    {
      name: 'queryParser',
      factory: function() {
        return this.QueryParser.create({ of: this.of });
      }
    },
    {
      class: 'Int',
      name: 'width',
      value: 47
    },
    'property',
    {
      name: 'predicate',
      factory: function() { return this.True.create(); }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'viewSpec',
      value: { class: 'foam.u2.tag.Input' }
    },
    {
      name: 'view'
    },
    {
      name: 'label',
      expression: function(property) {
        return property && property.label ? property.label : 'Search';
      }
    },
    {
      // All search views (in the SearchManager) need a name.
      // This defaults to 'query'.
      name: 'name',
      value: 'query'
    }
  ],

  methods: [
    function initE() {
      this
        .addClass(this.myClass())
        .tag(this.viewSpec, { alwaysFloatLabel: true, label$: this.label$ }, this.view$);

      this.view.data$.sub(this.updateValue);
    },

    function clear() {
      this.view.data = '';
      this.predicate = this.True.create();
    }
  ],

  listeners: [
    {
      name: 'updateValue',
      code: function() {
        var value = this.view.data;

        if ( ! value ) {
          this.predicate = this.True.create();
          return;
        }

        if ( this.richSearch ) {
          this.predicate = this.OR(...this.filterController.filters
            .map((name) => this.of.getAxiomByName(name))
            .filter((property) => foam.core.String.isInstance(property))
            .map((property) => this.CONTAINS_IC(property, value))
            .concat(this.queryParser.parseString(value) || this.False.create()));
          return;
        }

        this.predicate = this.CONTAINS_IC(this.property, value);
      }
    }
  ]
});
<commit_msg>Add flag to use strict equality check<commit_after>/**
 * @license
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.CLASS({
  package: 'foam.u2.search',
  name: 'TextSearchView',
  extends: 'foam.u2.View',

  requires: [
    'foam.mlang.predicate.True',
    'foam.mlang.predicate.False',
    'foam.parse.QueryParser',
    'foam.u2.tag.Input'
  ],

  imports: [
    'filterController'
  ],

  implements: [
    'foam.mlang.Expressions'
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'Boolean',
      name: 'richSearch',
      value: false
    },
    {
      class: 'Boolean',
      name: 'keywordSearch',
      value: false
    },
    {
      class: 'Boolean',
      name: 'checkStrictEquality',
      documentation: `
        Set this flag if you want to match by strict equality instead of
        checking if the text contains the string. Doing so should improve
        performance.
      `,
      value: false
    },
    {
      name: 'queryParser',
      factory: function() {
        return this.QueryParser.create({ of: this.of });
      }
    },
    {
      class: 'Int',
      name: 'width',
      value: 47
    },
    'property',
    {
      name: 'predicate',
      factory: function() { return this.True.create(); }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'viewSpec',
      value: { class: 'foam.u2.tag.Input' }
    },
    {
      name: 'view'
    },
    {
      name: 'label',
      expression: function(property) {
        return property && property.label ? property.label : 'Search';
      }
    },
    {
      // All search views (in the SearchManager) need a name.
      // This defaults to 'query'.
      name: 'name',
      value: 'query'
    }
  ],

  methods: [
    function initE() {
      this
        .addClass(this.myClass())
        .tag(this.viewSpec, { alwaysFloatLabel: true, label$: this.label$ }, this.view$);

      this.view.data$.sub(this.updateValue);
    },

    function clear() {
      this.view.data = '';
      this.predicate = this.True.create();
    }
  ],

  listeners: [
    {
      name: 'updateValue',
      code: function() {
        var value = this.view.data;
        var pred = this.checkStrictEquality ?
          this.EQ.bind(this) :
          this.CONTAINS_IC.bind(this);

        if ( ! value ) {
          this.predicate = this.True.create();
          return;
        }

        if ( this.richSearch ) {
          this.predicate = this.OR(...this.filterController.filters
            .map((name) => this.of.getAxiomByName(name))
            .filter((property) => foam.core.String.isInstance(property))
            .map((property) => pred(property, value))
            .concat(this.queryParser.parseString(value) || this.False.create()));
          return;
        }

        this.predicate = pred(this.property, value);
      }
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */
foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnsConfigView',
  extends: 'foam.u2.View',

  documentation: 'A view for configuring table columns.',

  requires: [
    'foam.u2.view.ColumnConfigPropView',
    'foam.u2.view.ColumnOptionsSelectConfig',
  ],

  properties: [
    'of',
    'allColumns',
    'selectedColumns',
    {
      name: 'allProperties',
      expression: function(allColumns, of) {
        var props = [];
        allColumns.map(axiomName => {//what is overridesMap??? can't find
          props.push(this.of.getAxiomByName(axiomName[0]));
          });

        return props;
      }
    },
    'isColumnChanged',
    {
      name: 'columns',
      value: []
    }
  ],

  css: `
    ^ {
      font-size: 18px;
      padding: 8px 0;
      //display: grid;
      grid-template-columns: 1fr 1fr;
    }
    ^ > * {
      align-self: center;
    }
  `,

  methods: [
    function initE() {
      this.SUPER();
      var self = this;
      for (var i = 0; i < this.data.selectedColumnNames.length; i++) {
        self.columns.push(self.ColumnOptionsSelectConfig.create({selectedColumns: self.data.selectedColumnNames[i], of:self.data.of}));
      }

      this
        .addClass(this.myClass())
        .forEach(this.columns, function(c) {
          self.add(foam.u2.ViewSpec.createView(self.ColumnConfigPropView, {data:c},  this, this.__subSubContext__));
        });
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnConfigPropView',
  extends: 'foam.u2.View',
  requires: [
    'foam.u2.view.ColumnViewHeader',
    'foam.u2.view.ColumnViewBody',
    'foam.u2.view.RootColumnConfigPropView',

  ],
  css: `
    ^move-right {
      margin-left: 15px;
    }
  `,
  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      this.start()//.addClass(self.data.myClass('container-handle'))
          .add(this.slot(function(data, data$isPropertySelected, data$columnOptions, data$rootProperty, data$rootProperty$expanded) {
            return this.E()
            .start()
              .add(foam.u2.ViewSpec.createView(self.ColumnViewHeader, {data$:self.data.rootProperty$},  self, self.__subSubContext__))
            .end()
            .start()
              //.show(self.data.rootProperty.parentExpanded$)
              .start()
                .add(foam.u2.ViewSpec.createView(self.ColumnViewBody, {data$:self.data.rootProperty$},  self, self.__subSubContext__))
              .end()
              .forEach(self.data.columnOptions, function(o) {
                this
                  .start()
                    .show(self.data.rootProperty.expanded$)
                    .addClass(self.myClass('move-right'))
                      .add(foam.u2.ViewSpec.createView(self.RootColumnConfigPropView, {data:o},  self, self.__subSubContext__))
                  .end();
              })
            .end();
          }))
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'RootColumnConfigPropView',
  extends: 'foam.u2.View',
  properties: [
    {
      class: 'foam.u2.ViewSpec',
      name: 'head',
      value: { class:'foam.u2.view.ColumnViewHeader'}
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'body',
      value: { class:'foam.u2.view.ColumnViewBody'}
    }
  ],
  methods: [
    function initE() {
      this.SUPER();
      this
      .start()
          .add(foam.u2.ViewSpec.createView(this.head, {data$:this.data$},  this, this.__subSubContext__))
        .end()
        .start()
          .add(foam.u2.ViewSpec.createView(this.body, {data$:this.data$},  this, this.__subSubContext__))
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnOptionsSelectConfig',
  requires: [
    'foam.u2.view.SubColumnSelectConfig'
  ],
  properties: [
    'selectedColumns',
    'of',
    {
      name: 'rootProperty',
      class: 'FObjectProperty',
      of: 'foam.u2.view.SubColumnSelectConfig',
      expression: function(of, selectedColumns, isPropertySelected) {
        return this.updateRootProperty();
      }
    },
    {
      name: 'columnOptions',
      class: 'FObjectArray',
      of: 'foam.u2.view.SubColumnSelectConfig',
      factory: function() {
        return this.updateOptionsProperty();
      }
    },
    {
      name: 'isPropertySelected',
      class: 'Boolean',
      value: true,
      postSet: function() {
        if ( this.isPropertySelected) {
          this.rootProperty = this.updateRootProperty();
          this.columnOptions = this.updateOptionsProperty();
          this.isPropertySelected = false;
        }
      }
    }
  ],
  methods: [
    function updateRootProperty() {
      var p = this.of.getAxiomByName(this.selectedColumns[0]);
      return this.SubColumnSelectConfig.create({ rootProperty: p, hasOtherOptions:true, selectedColumns$:this.selectedColumns$, level:0, isPropertySelected$:this.isPropertySelected$ });
    },
    function updateOptionsProperty() {
      var props = this.of.getAxiomsByClass(foam.core.Property);
      var propViews = [];
      props = props.filter(p => p.name !== this.selectedColumns[0]);
      for ( var i = 0; i < props.length; i++) {
        propViews.push(this.SubColumnSelectConfig.create({ rootProperty: props[i], hasOtherOptions:false, selectedColumns$:this.selectedColumns$, level:0, parentExpanded$:this.rootProperty.parentExpanded$, isPropertySelected$:this.isPropertySelected$ }));
      }
      this.isPropertySelected = false;
      return propViews;
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnViewHeader',
  extends: 'foam.u2.View',
  css: `

  ^selected {
    background: lightblue;
  }
  `,

  methods: [
    function initE() {
      this.SUPER();
      this.start()
        //.style({'margin-right': '20px'})
        .enableClass(this.myClass('selected'), this.data.level < this.data.selectedColumns.length && this.data.selectedColumns[this.data.level] == this.data.rootProperty.name)
        .on('click', this.toggleExpanded)
        .start()
          .add(this.data.rootProperty.name)
          .start('span')
            .show(this.data.hasOtherOptions || this.data.hasSubProperties)
            .style({
              'vertical-align': 'middle',
              'font-weight':    'bold',
              'visibility':     'visible',
              'font-size':      '16px',
              'float':          'right',
              'transform':      this.data.expanded$.map(function(c) { return c ? 'rotate(180deg)' : 'rotate(90deg)'; })
            })
            .on('click', this.toggleExpanded)
            .add('\u2303')
          .end()
        .end()
      .end();
    }
  ],
  listeners: [
    function toggleExpanded(e) {
      this.data.expanded = !this.data.expanded;

      if ( !this.data.expanded ) {
        while ( this.data.level > this.data.selectedColumns.length - 1 ) {
          this.data.selectedColumns.push(undefined);
        }
        if ( this.data.selectedColumns[this.data.level] !== this.data.rootProperty.name )
          this.data.selectedColumns[this.data.level] = this.data.rootProperty.name;
          this.data.isPropertySelected = true;

          //if level higher then previous one
        if ( !this.data.hasSubProperties && this.data.level !== this.data.selectedColumns.length - 1 ) {
          this.data.selectedColumns.splice(0, this.data.selectedColumns.length - 1 - this.data.level);
        }

        if ( !this.data.hasSubProperties ) {
          this.parentExpanded = !this.parentExpanded;
        }
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnViewBody',
  extends: 'foam.u2.View',
  requires: [
    'foam.u2.view.RootColumnConfigPropView',
    'foam.u2.view.SubColumnSelectConfig'
  ],
  css: `
  ^move-right {
    margin-left: 20px;
  }
  `,
  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      // self.data.rootProperty.parentExpanded$.sub(function() {
      //   console.log(self.data.rootProperty.expanded);
      // });

      this
        .start()
            .forEach(this.data.subColumnSelectConfig, function(p) {
            self
              .addClass(self.myClass('move-right'))
              .show(self.data.expanded$)
              .add(foam.u2.ViewSpec.createView(self.RootColumnConfigPropView, {data:p}, self, self.__subSubContext__));
          })
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'SubColumnSelectConfig',
  properties: [
    {
      name: 'hasSubProperties',
      class: 'Boolean',
      expression: function(rootProperty) {
        if ( rootProperty.cls_.name === 'FObjectProperty' )
          return true;
        return false;
      }
    },
    {
      name: 'selectedColumns',
      documentation: 'array of names of selected proprties'
    },
    {
      name: 'subProperties',
      expression: function(rootProperty) {
        if ( rootProperty.cls_.name === 'FObjectProperty' )
          return rootProperty.of.getAxiomsByClass(foam.core.Property);
        return [];
      }
    },
    {
      name: 'subColumnSelectConfig',
      expression: function(subProperties, level) {
        var arr = [];
        var l = level + 1;
        for ( var i = 0; i < subProperties.length; i++ )
          arr.push(this.cls_.create({ rootProperty: subProperties[i], selectedColumns$:this.selectedColumns$, level:l, isPropertySelected$:this.isPropertySelected$, parentExpanded$:this.expanded$ }));
        return arr;
      }
    },
    {
      name: 'rootProperty'
    },
    {
      name: 'isPropertySelected',
      class: 'Boolean'
    },
    {
      name: 'level',
      class: 'Int',
    },
    {
      name: 'parentExpanded',
      class: 'Boolean',
      value: false
    },
    {
      name: 'expanded',
      class: 'Boolean',
      value: false
    },
    // {
    //   name: 'parentExpanded',
    //   class: 'Boolean',
    //   value: false
    // },
    {
      name: 'hasOtherOptions',
      class: 'Boolean',
      value: false
    }
  ]
});
<commit_msg>Fixing view<commit_after>/**
 * @license
 * Copyright 2020 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */
foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnsConfigView',
  extends: 'foam.u2.View',

  documentation: 'A view for configuring table columns.',

  requires: [
    'foam.u2.view.ColumnConfigPropView',
    'foam.u2.view.ColumnOptionsSelectConfig',
  ],

  properties: [
    'of',
    'allColumns',
    'selectedColumns',
    {
      name: 'allProperties',
      expression: function(allColumns, of) {
        var props = [];
        allColumns.map(axiomName => {//what is overridesMap??? can't find
          props.push(this.of.getAxiomByName(axiomName[0]));
          });

        return props;
      }
    },
    'isColumnChanged',
    {
      name: 'columns',
      value: []
    }
  ],

  css: `
    ^ {
      font-size: 18px;
      padding: 8px 0;
      //display: grid;
      grid-template-columns: 1fr 1fr;
    }
    ^ > * {
      align-self: center;
    }
  `,

  methods: [
    function initE() {
      this.SUPER();
      var self = this;
      for (var i = 0; i < this.data.selectedColumnNames.length; i++) {
        self.columns.push(self.ColumnOptionsSelectConfig.create({selectedColumns: self.data.selectedColumnNames[i], of:self.data.of}));
      }

      this
        .addClass(this.myClass())
        .forEach(this.columns, function(c) {
          self.add(foam.u2.ViewSpec.createView(self.ColumnConfigPropView, {data:c},  this, this.__subSubContext__));
        });
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnConfigPropView',
  extends: 'foam.u2.View',
  requires: [
    'foam.u2.view.ColumnViewHeader',
    'foam.u2.view.ColumnViewBody',
    'foam.u2.view.RootColumnConfigPropView',

  ],
  css: `
    ^move-right {
      margin-left: 15px;
    }
  `,
  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      this.start()//.addClass(self.data.myClass('container-handle'))
          .add(this.slot(function(data, data$isPropertySelected, data$columnOptions, data$rootProperty, data$rootProperty$expanded) {
            return this.E()
            .start()
              .add(foam.u2.ViewSpec.createView(self.ColumnViewHeader, {data$:self.data.rootProperty$},  self, self.__subSubContext__))
            .end()
            .start()
              //.show(self.data.rootProperty.parentExpanded$)
              .start()
                .add(foam.u2.ViewSpec.createView(self.ColumnViewBody, {data$:self.data.rootProperty$},  self, self.__subSubContext__))
              .end()
              .forEach(self.data.columnOptions, function(o) {
                this
                  .start()
                    .show(self.data.rootProperty.expanded$)
                    .addClass(self.myClass('move-right'))
                      .add(foam.u2.ViewSpec.createView(self.RootColumnConfigPropView, {data:o},  self, self.__subSubContext__))
                  .end();
              })
            .end();
          }))
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'RootColumnConfigPropView',
  extends: 'foam.u2.View',
  properties: [
    {
      class: 'foam.u2.ViewSpec',
      name: 'head',
      value: { class:'foam.u2.view.ColumnViewHeader'}
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'body',
      value: { class:'foam.u2.view.ColumnViewBody'}
    }
  ],
  methods: [
    function initE() {
      this.SUPER();
      this
      .start()
          .add(foam.u2.ViewSpec.createView(this.head, {data$:this.data$},  this, this.__subSubContext__))
        .end()
        .start()
          .add(foam.u2.ViewSpec.createView(this.body, {data$:this.data$},  this, this.__subSubContext__))
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnOptionsSelectConfig',
  requires: [
    'foam.u2.view.SubColumnSelectConfig'
  ],
  properties: [
    'selectedColumns',
    'of',
    {
      name: 'rootProperty',
      class: 'FObjectProperty',
      of: 'foam.u2.view.SubColumnSelectConfig',
      expression: function(of, selectedColumns, isPropertySelected) {
        return this.updateRootProperty();
      }
    },
    {
      name: 'columnOptions',
      class: 'FObjectArray',
      of: 'foam.u2.view.SubColumnSelectConfig',
      factory: function() {
        return this.updateOptionsProperty();
      }
    },
    {
      name: 'isPropertySelected',
      class: 'Boolean',
      value: true,
      postSet: function() {
        if ( this.isPropertySelected) {
          this.rootProperty = this.updateRootProperty();
          this.columnOptions = this.updateOptionsProperty();
          this.isPropertySelected = false;
        }
      }
    }
  ],
  methods: [
    function updateRootProperty() {
      var p = this.of.getAxiomByName(this.selectedColumns[0]);
      return this.SubColumnSelectConfig.create({ rootProperty: p, hasOtherOptions:true, selectedColumns$:this.selectedColumns$, level:0, isPathSelected$:this.isPropertySelected$ });
    },
    function updateOptionsProperty() {
      var props = this.of.getAxiomsByClass(foam.core.Property);
      var propViews = [];
      props = props.filter(p => p.name !== this.selectedColumns[0]);
      for ( var i = 0; i < props.length; i++) {
        propViews.push(this.SubColumnSelectConfig.create({ rootProperty: props[i], hasOtherOptions:false, selectedColumns$:this.selectedColumns$, level:0, isPathSelected$:this.isPropertySelected$ }));
      }
      this.isPropertySelected = false;
      return propViews;
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnViewHeader',
  extends: 'foam.u2.View',
  css: `

  ^selected {
    background: lightblue;
  }
  `,

  methods: [
    function initE() {
      this.SUPER();
      this.start()
        //.style({'margin-right': '20px'})
        .enableClass(this.myClass('selected'), this.data.level < this.data.selectedColumns.length && this.data.selectedColumns[this.data.level] == this.data.rootProperty.name)
        .on('click', this.toggleExpanded)
        .start()
          .add(this.data.rootProperty.name)
          .start('span')
            .show(this.data.hasOtherOptions || this.data.hasSubProperties)
            .style({
              'vertical-align': 'middle',
              'font-weight':    'bold',
              'visibility':     'visible',
              'font-size':      '16px',
              'float':          'right',
              'transform':      this.data.expanded$.map(function(c) { return c ? 'rotate(180deg)' : 'rotate(90deg)'; })
            })
            .on('click', this.toggleExpanded)
            .add('\u2303')
          .end()
        .end()
      .end();
    }
  ],
  listeners: [
    function toggleExpanded(e) {
      this.data.expanded = !this.data.expanded;
      if ( !this.data.expanded || !(this.data.hasSubProperties || this.data.hasOtherOptions)) {
        if ( !this.data.hasSubProperties ) {
          this.data.parentExpanded = !this.data.parentExpanded;
        }
        this.data.isPropertySelected = true;
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ColumnViewBody',
  extends: 'foam.u2.View',
  requires: [
    'foam.u2.view.RootColumnConfigPropView',
    'foam.u2.view.SubColumnSelectConfig'
  ],
  css: `
  ^move-right {
    margin-left: 20px;
  }
  `,
  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      // self.data.rootProperty.parentExpanded$.sub(function() {
      //   console.log(self.data.rootProperty.expanded);
      // });

      this
        .start()
            .forEach(this.data.subColumnSelectConfig, function(p) {
            self
              .addClass(self.myClass('move-right'))
              .show(self.data.expanded$)
              .add(foam.u2.ViewSpec.createView(self.RootColumnConfigPropView, {data:p}, self, self.__subSubContext__));
          })
        .end();
    }
  ]
});

foam.CLASS({
  package: 'foam.u2.view',
  name: 'SubColumnSelectConfig',
  properties: [
    {
      name: 'hasSubProperties',
      class: 'Boolean',
      expression: function(rootProperty) {
        if ( rootProperty.cls_.name === 'FObjectProperty' )
          return true;
        return false;
      }
    },
    {
      name: 'selectedColumns',
      documentation: 'array of names of selected proprties'
    },
    {
      name: 'subProperties',
      expression: function(rootProperty) {
        if ( rootProperty.cls_.name === 'FObjectProperty' )
          return rootProperty.of.getAxiomsByClass(foam.core.Property);
        return [];
      }
    },
    {
      name: 'subColumnSelectConfig',
      expression: function(subProperties, level) {
        var arr = [];
        var l = level + 1;
        for ( var i = 0; i < subProperties.length; i++ )
          arr.push(this.cls_.create({ rootProperty: subProperties[i], selectedColumns$:this.selectedColumns$, level:l, isPathSelected$:this.isPropertySelected$, parentExpanded$:this.expanded$ }));
        return arr;
      }
    },
    {
      name: 'rootProperty'
    },
    {
      name: 'isPropertySelected',
      class: 'Boolean',
      postSet: function() {
        if ( this.isPropertySelected ) {
          while ( this.level > this.selectedColumns.length - 1 ) {
            this.selectedColumns.push(undefined);
          }
          if ( this.selectedColumns[this.level] !== this.rootProperty.name )
            this.selectedColumns[this.level] = this.rootProperty.name;

            //if level higher then previous one
          if ( !this.hasSubProperties && this.level !== this.selectedColumns.length - 1 ) {
            this.selectedColumns.splice(0, this.selectedColumns.length - 1 - this.level);
          }
          this.isPathSelected = true;
        }
      }
    },
    {
      name: 'isPathSelected',
      class: 'Boolean',
      value: false
    },
    {
      name: 'level',
      class: 'Int',
    },
    {
      name: 'parentExpanded',
      class: 'Boolean',
      value: false
    },
    {
      name: 'expanded',
      class: 'Boolean',
      value: false
    },
    // {
    //   name: 'parentExpanded',
    //   class: 'Boolean',
    //   value: false
    // },
    {
      name: 'hasOtherOptions',
      class: 'Boolean',
      value: false
    }
  ]
});
<|endoftext|>


<commit_before>import { createDrawerNavigator } from '@react-navigation/drawer';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Drawer, DrawerItem, IndexPath, useTheme } from '@ui-kitten/components';
import Constants from 'expo-constants';
import * as Analytics from 'expo-firebase-analytics';
import * as Linking from 'expo-linking';
import * as SecureStorage from 'expo-secure-store';
import * as Updates from 'expo-updates';
import propTypes from 'prop-types';
import React from 'react';
import { View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import { v4 as uuid } from 'uuid';
import useAuth from '../auth/context';
import ChooseTypeScreen from '../screens/ChooseType';
import MainScreen from '../screens/Main';
import MyReportsScreen from '../screens/MyReports';
import NewUserScreen from '../screens/NewUser';
import ProfileScreen from '../screens/Profile';
import ReportInfoScreen from '../screens/ReportInfo';
import config from '../services/config';
import { getIcon } from '../services/icons';
import { PADDING } from '../services/styles';
import { forceUpdate, getReleaseChannelBranch, sendEmailToSupport } from '../services/utilities';

const { Navigator, Screen } = createStackNavigator();
const prefix = Linking.createURL('/');

let StorybookUIRoot;
if (config.SHOW_STORYBOOK) {
  // don't ship this code in production
  StorybookUIRoot = require('../storybook').default;
}

const AuthNavigator = () => {
  return (
    <Navigator screenOptions={{ headerShown: false }}>
      <Screen name="choose-type" component={ChooseTypeScreen} />
      <Screen name="new-user" component={NewUserScreen} />
      {config.SHOW_STORYBOOK ? <Screen name="storybook" component={StorybookUIRoot} /> : null}
    </Navigator>
  );
};

const DrawerIcon = (props) => {
  const theme = useTheme();
  const iconSize = 30;

  const Icon = getIcon({
    pack: 'eva',
    name: props.name,
    size: iconSize,
    color: theme['color-basic-700'],
  });

  return (
    <View style={{ paddingRight: PADDING }}>
      <Icon />
    </View>
  );
};
DrawerIcon.propTypes = {
  name: propTypes.string.isRequired,
};

const getDrawContent = ({ navigation, state, logOut }) => {
  let openStorybook;
  if (config.SHOW_STORYBOOK) {
    openStorybook = () => navigation.navigate('Storybook');
  }
  const actions = {
    4: sendEmailToSupport,
    5: logOut,
    6: forceUpdate,
    7: openStorybook,
  };
  const onSelect = (index) => {
    if (actions[index]) {
      return actions[index]();
    }

    navigation.navigate(state.routeNames[index.row]);
  };

  let versionTitle = `App version: ${Constants.manifest.version} (${Constants.manifest?.ios?.buildNumber})`;
  if (getReleaseChannelBranch(Updates.releaseChannel) !== config.RELEASE_BRANCHES.production) {
    versionTitle += ` - ${__DEV__ ? 'dev' : Updates.releaseChannel}`;
  }

  return (
    <SafeAreaView>
      <Drawer selectedIndex={state.index === 0 ? null : new IndexPath(state.index)} onSelect={onSelect}>
        <DrawerItem title="Main" style={{ display: 'none' }} />
        <DrawerItem title="My Reports" accessoryLeft={() => <DrawerIcon name="list" />} />
        <DrawerItem title="Profile" accessoryLeft={() => <DrawerIcon name="person" />} />
        <DrawerItem title="Contact" accessoryLeft={() => <DrawerIcon name="email" />} />
        <DrawerItem title="Logout" accessoryLeft={() => <DrawerIcon name="log-out" />} />
        <DrawerItem title={versionTitle} accessoryLeft={() => <DrawerIcon name="info" />} />
        {config.SHOW_STORYBOOK ? <DrawerItem title="Storybook" /> : null}
      </Drawer>
    </SafeAreaView>
  );
};

const CloseButton = () => {
  const navigation = useNavigation();
  const theme = useTheme();
  const CloseIcon = getIcon({
    pack: 'material-community',
    name: 'close-circle-outline',
    size: 30,
    color: theme['color-basic-700'],
  });

  return (
    <TouchableOpacity onPress={() => navigation.goBack()} style={{ paddingHorizontal: PADDING }}>
      <CloseIcon />
    </TouchableOpacity>
  );
};
const getHeaderIcon = (name) => <DrawerIcon name={name} style={{ marginRight: 10 }} />;
const BackButton = () => {
  const navigation = useNavigation();
  const theme = useTheme();
  const BackIcon = getIcon({
    pack: 'font-awesome-5',
    name: 'chevron-left',
    size: 30,
    color: theme['color-basic-700'],
  });

  return (
    <TouchableOpacity onPress={() => navigation.goBack()} style={{ paddingHorizontal: PADDING }}>
      <BackIcon />
    </TouchableOpacity>
  );
};
const ReportsNavigator = () => {
  const { Navigator, Screen } = createStackNavigator();

  return (
    <Navigator screenOptions={{ headerLeft: BackButton }}>
      <Screen
        name="My Reports"
        component={MyReportsScreen}
        options={{ headerLeft: CloseButton, headerRight: () => getHeaderIcon('list') }}
      />
      <Screen name="Report Info" component={ReportInfoScreen} options={{ headerRight: () => getHeaderIcon('info') }} />
    </Navigator>
  );
};

const MainNavigator = () => {
  const { Navigator, Screen } = createDrawerNavigator();
  const { logOut } = useAuth();

  return (
    <Navigator
      drawerContent={(args) => getDrawContent({ ...args, logOut })}
      screenOptions={{ swipeEnabled: false, headerLeft: CloseButton }}
    >
      <Screen name="Main" component={MainScreen} options={{ headerShown: false }} />
      <Screen name="Reports Navigator" component={ReportsNavigator} options={{ headerShown: false }} />
      <Screen name="Profile" component={ProfileScreen} options={{ headerRight: () => getHeaderIcon('person') }} />
      {config.SHOW_STORYBOOK ? <Screen name="Storybook" component={StorybookUIRoot} /> : null}
    </Navigator>
  );
};

const AppNavigator = () => {
  const navigationRef = React.useRef(null);
  const routeNameRef = React.useRef(null);
  const { authInfo } = useAuth();

  React.useEffect(() => {
    const initAnalytics = async () => {
      if (Constants.appOwnership === 'expo') {
        const key = 'CLIENT_ID';
        let id = await SecureStorage.getItemAsync(key);

        if (!id) {
          id = uuid();

          SecureStorage.setItemAsync(key, id);
        }

        // needs to be called before any other analytics methods
        Analytics.setClientId(id);
      }

      if (__DEV__) {
        Analytics.setDebugModeEnabled(true);
      }
    };

    initAnalytics();
  }, []);

  const linking = { prefixes: [prefix], config: { screens: { login: 'oauthredirect' } } };
  const setInitialRouteName = () => (routeNameRef.current = navigationRef.current.getCurrentRoute().name);
  const updateRouteName = async () => {
    const previousRouteName = routeNameRef.current;
    const currentRouteName = navigationRef.current.getCurrentRoute().name;

    if (previousRouteName !== currentRouteName) {
      await Analytics.logEvent('screen_view', { screen_name: currentRouteName });
    }

    routeNameRef.current = currentRouteName;
  };

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={setInitialRouteName}
      onStateChange={updateRouteName}
      linking={linking}
    >
      {authInfo?.user && authInfo.registered ? <MainNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
};

export default AppNavigator;
<commit_msg>fix(front): fix analytics crash on sims<commit_after>import { createDrawerNavigator } from '@react-navigation/drawer';
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { Drawer, DrawerItem, IndexPath, useTheme } from '@ui-kitten/components';
import Constants from 'expo-constants';
import * as Analytics from 'expo-firebase-analytics';
import * as Linking from 'expo-linking';
import * as SecureStorage from 'expo-secure-store';
import * as Updates from 'expo-updates';
import propTypes from 'prop-types';
import React from 'react';
import { View } from 'react-native';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { SafeAreaView } from 'react-native-safe-area-context';
import { v4 as uuid } from 'uuid';
import useAuth from '../auth/context';
import ChooseTypeScreen from '../screens/ChooseType';
import MainScreen from '../screens/Main';
import MyReportsScreen from '../screens/MyReports';
import NewUserScreen from '../screens/NewUser';
import ProfileScreen from '../screens/Profile';
import ReportInfoScreen from '../screens/ReportInfo';
import config from '../services/config';
import { getIcon } from '../services/icons';
import { PADDING } from '../services/styles';
import { forceUpdate, getReleaseChannelBranch, sendEmailToSupport } from '../services/utilities';

const { Navigator, Screen } = createStackNavigator();
const prefix = Linking.createURL('/');

let StorybookUIRoot;
if (config.SHOW_STORYBOOK) {
  // don't ship this code in production
  StorybookUIRoot = require('../storybook').default;
}

const AuthNavigator = () => {
  return (
    <Navigator screenOptions={{ headerShown: false }}>
      <Screen name="choose-type" component={ChooseTypeScreen} />
      <Screen name="new-user" component={NewUserScreen} />
      {config.SHOW_STORYBOOK ? <Screen name="storybook" component={StorybookUIRoot} /> : null}
    </Navigator>
  );
};

const DrawerIcon = (props) => {
  const theme = useTheme();
  const iconSize = 30;

  const Icon = getIcon({
    pack: 'eva',
    name: props.name,
    size: iconSize,
    color: theme['color-basic-700'],
  });

  return (
    <View style={{ paddingRight: PADDING }}>
      <Icon />
    </View>
  );
};
DrawerIcon.propTypes = {
  name: propTypes.string.isRequired,
};

const getDrawContent = ({ navigation, state, logOut }) => {
  let openStorybook;
  if (config.SHOW_STORYBOOK) {
    openStorybook = () => navigation.navigate('Storybook');
  }
  const actions = {
    4: sendEmailToSupport,
    5: logOut,
    6: forceUpdate,
    7: openStorybook,
  };
  const onSelect = (index) => {
    if (actions[index]) {
      return actions[index]();
    }

    navigation.navigate(state.routeNames[index.row]);
  };

  let versionTitle = `App version: ${Constants.manifest.version} (${Constants.manifest?.ios?.buildNumber})`;
  if (getReleaseChannelBranch(Updates.releaseChannel) !== config.RELEASE_BRANCHES.production) {
    versionTitle += ` - ${__DEV__ ? 'dev' : Updates.releaseChannel}`;
  }

  return (
    <SafeAreaView>
      <Drawer selectedIndex={state.index === 0 ? null : new IndexPath(state.index)} onSelect={onSelect}>
        <DrawerItem title="Main" style={{ display: 'none' }} />
        <DrawerItem title="My Reports" accessoryLeft={() => <DrawerIcon name="list" />} />
        <DrawerItem title="Profile" accessoryLeft={() => <DrawerIcon name="person" />} />
        <DrawerItem title="Contact" accessoryLeft={() => <DrawerIcon name="email" />} />
        <DrawerItem title="Logout" accessoryLeft={() => <DrawerIcon name="log-out" />} />
        <DrawerItem title={versionTitle} accessoryLeft={() => <DrawerIcon name="info" />} />
        {config.SHOW_STORYBOOK ? <DrawerItem title="Storybook" /> : null}
      </Drawer>
    </SafeAreaView>
  );
};

const CloseButton = () => {
  const navigation = useNavigation();
  const theme = useTheme();
  const CloseIcon = getIcon({
    pack: 'material-community',
    name: 'close-circle-outline',
    size: 30,
    color: theme['color-basic-700'],
  });

  return (
    <TouchableOpacity onPress={() => navigation.goBack()} style={{ paddingHorizontal: PADDING }}>
      <CloseIcon />
    </TouchableOpacity>
  );
};
const getHeaderIcon = (name) => <DrawerIcon name={name} style={{ marginRight: 10 }} />;
const BackButton = () => {
  const navigation = useNavigation();
  const theme = useTheme();
  const BackIcon = getIcon({
    pack: 'font-awesome-5',
    name: 'chevron-left',
    size: 30,
    color: theme['color-basic-700'],
  });

  return (
    <TouchableOpacity onPress={() => navigation.goBack()} style={{ paddingHorizontal: PADDING }}>
      <BackIcon />
    </TouchableOpacity>
  );
};
const ReportsNavigator = () => {
  const { Navigator, Screen } = createStackNavigator();

  return (
    <Navigator screenOptions={{ headerLeft: BackButton }}>
      <Screen
        name="My Reports"
        component={MyReportsScreen}
        options={{ headerLeft: CloseButton, headerRight: () => getHeaderIcon('list') }}
      />
      <Screen name="Report Info" component={ReportInfoScreen} options={{ headerRight: () => getHeaderIcon('info') }} />
    </Navigator>
  );
};

const MainNavigator = () => {
  const { Navigator, Screen } = createDrawerNavigator();
  const { logOut } = useAuth();

  return (
    <Navigator
      drawerContent={(args) => getDrawContent({ ...args, logOut })}
      screenOptions={{ swipeEnabled: false, headerLeft: CloseButton }}
    >
      <Screen name="Main" component={MainScreen} options={{ headerShown: false }} />
      <Screen name="Reports Navigator" component={ReportsNavigator} options={{ headerShown: false }} />
      <Screen name="Profile" component={ProfileScreen} options={{ headerRight: () => getHeaderIcon('person') }} />
      {config.SHOW_STORYBOOK ? <Screen name="Storybook" component={StorybookUIRoot} /> : null}
    </Navigator>
  );
};

const AppNavigator = () => {
  const navigationRef = React.useRef(null);
  const routeNameRef = React.useRef(null);
  const { authInfo } = useAuth();
  const [analyticsIsReady, setAnalyticsIsReady] = React.useState(false);

  React.useEffect(() => {
    const initAnalytics = async () => {
      if (Constants.appOwnership === 'expo') {
        const key = 'CLIENT_ID';
        let id = await SecureStorage.getItemAsync(key);

        if (!id) {
          id = uuid();

          SecureStorage.setItemAsync(key, id);
        }

        // needs to be called before any other analytics methods
        Analytics.setClientId(id);
      }

      if (__DEV__) {
        Analytics.setDebugModeEnabled(true);
      }

      setAnalyticsIsReady(true);
    };

    initAnalytics();
  }, []);

  const linking = { prefixes: [prefix], config: { screens: { login: 'oauthredirect' } } };
  const setInitialRouteName = () => (routeNameRef.current = navigationRef.current.getCurrentRoute().name);
  const updateRouteName = async () => {
    const previousRouteName = routeNameRef.current;
    const currentRouteName = navigationRef.current.getCurrentRoute().name;

    if (previousRouteName !== currentRouteName && analyticsIsReady) {
      await Analytics.logEvent('screen_view', { screen_name: currentRouteName });
    }

    routeNameRef.current = currentRouteName;
  };

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={setInitialRouteName}
      onStateChange={updateRouteName}
      linking={linking}
    >
      {authInfo?.user && authInfo.registered ? <MainNavigator /> : <AuthNavigator />}
    </NavigationContainer>
  );
};

export default AppNavigator;
<|endoftext|>


<commit_before>var _ = require('underscore');
var Backbone = require('backbone');
var WidgetModel = require('../widget_model');
var CategoriesCollection = require('./categories_collection');
var MAXCATEGORIES = 12;

/**
 * Category widget model
 */
module.exports = WidgetModel.extend({

  initialize: function(attrs, opts) {
    this._data = new CategoriesCollection(this.get('data'));
    this._dataOrigin = new Backbone.Collection(this.get('data'));

    WidgetModel.prototype.initialize.call(this, attrs, opts);
    this.filter.setDataOrigin(this._dataOrigin);
  },

  getData: function() {
    return this._data;
  },

  getSize: function() {
    return this._data.size();
  },

  toJSON: function() {
    return {
      type: "aggregation",
      options: {
        column: this.get('column'),
        aggregation: this.get('aggregation')
      }
    };
  },

  _parseData: function(categories) {
    // Get info stats from categories
    var min = 0;
    var max = 0;
    var totalCount = 0;
    var newData = [];
    var _tmpArray = {};

    _.each(categories, function(datum) {
      var category = datum.category;
      var count = datum.value;
      var isRejected = this.filter.isRejected(category);
      min = Math.min(min, count);
      max = Math.max(max, count);
      totalCount = totalCount + count;
      _tmpArray[category] = true;
      newData.push({
        'selected': !isRejected,
        'name': category,
        'agg': datum.agg,
        'value': count
      });
    }, this);

    // this._dataOrigin.each(function(mdl) {
    //   if (newData.length >= MAXCATEGORIES) {
    //     return;
    //   }
    //
    //   var value = mdl.get('category');
    //   var isRejected = this.filter.isRejected(value);
    //   var alreadyAdded = _tmpArray[value];
    //
    //   if (!alreadyAdded) {
    //     newData.push({
    //       'selected': !isRejected,
    //       'name': value,
    //       'agg': false,
    //       'value': 0
    //     });
    //   }
    // }, this);

    return {
      data: newData
    }
  },

  setCategories: function(d) {
    this._dataOriginChecker(d);
    var attrs = this._parseData(d);
    this._data.reset(attrs.data);
    this.set(attrs);
  },

  parse: function(d) {
    var data = d.ownFilterOff;
    var categories = data.categories;
    this._dataOriginChecker(categories);
    var attrs = this._parseData(categories);

    _.extend(attrs, {
        nulls: data.nulls,
        min: data.min,
        max: data.max,
        count: data.count,
        categoriesCount: data.categoriesCount
      }
    );
    this._data.reset(attrs.data);
    return attrs;
  },

  _dataOriginChecker: function(d) {
    // If there is no data from the beginning,
    // complete data origin.
    if (this._dataOrigin.isEmpty()) {
      this._dataOrigin.reset(d);
    }
  },

  _onFilterChanged: function(filter) {
    this.trigger('change:filter', this, filter);
  }
});
<commit_msg>Changing way to parse data from category widget<commit_after>var _ = require('underscore');
var Backbone = require('backbone');
var WidgetModel = require('../widget_model');
var CategoriesCollection = require('./categories_collection');
var MAXCATEGORIES = 12;

/**
 * Category widget model
 */
module.exports = WidgetModel.extend({

  initialize: function(attrs, opts) {
    this._data = new CategoriesCollection(this.get('data'));
    this._dataOrigin = new Backbone.Collection(this.get('data'));

    WidgetModel.prototype.initialize.call(this, attrs, opts);
    this.filter.setDataOrigin(this._dataOrigin);
  },

  getData: function() {
    return this._data;
  },

  getSize: function() {
    return this._data.size();
  },

  toJSON: function() {
    return {
      type: "aggregation",
      options: {
        column: this.get('column'),
        aggregation: this.get('aggregation')
      }
    };
  },

  _parseData: function(categories) {
    // Get info stats from categories
    var min = 0;
    var max = 0;
    var totalCount = 0;
    var newData = [];
    var _tmpArray = {};

    _.each(categories, function(datum) {
      var category = datum.category;
      var count = datum.value;
      var isRejected = this.filter.isRejected(category);
      min = Math.min(min, count);
      max = Math.max(max, count);
      totalCount = totalCount + count;
      _tmpArray[category] = true;
      newData.push({
        'selected': !isRejected,
        'name': category,
        'agg': datum.agg,
        'value': count
      });
    }, this);

    // this._dataOrigin.each(function(mdl) {
    //   if (newData.length >= MAXCATEGORIES) {
    //     return;
    //   }
    //
    //   var value = mdl.get('category');
    //   var isRejected = this.filter.isRejected(value);
    //   var alreadyAdded = _tmpArray[value];
    //
    //   if (!alreadyAdded) {
    //     newData.push({
    //       'selected': !isRejected,
    //       'name': value,
    //       'agg': false,
    //       'value': 0
    //     });
    //   }
    // }, this);

    return {
      data: newData
    }
  },

  setCategories: function(d) {
    this._dataOriginChecker(d);
    var attrs = this._parseData(d);
    this._data.reset(attrs.data);
    this.set(attrs);
  },

  parse: function(d) {
    var categories = d.categories;
    this._dataOriginChecker(categories);
    var attrs = this._parseData(categories);

    _.extend(attrs, {
        nulls: d.nulls,
        min: d.min,
        max: d.max,
        count: d.count,
        categoriesCount: d.categoriesCount
      }
    );
    this._data.reset(attrs.data);
    return attrs;
  },

  _dataOriginChecker: function(d) {
    // If there is no data from the beginning,
    // complete data origin.
    if (this._dataOrigin.isEmpty()) {
      this._dataOrigin.reset(d);
    }
  },

  _onFilterChanged: function(filter) {
    this.trigger('change:filter', this, filter);
  }
});
<|endoftext|>


<commit_before>/*
 * fuse.js
 *
 * Copyright (C) 2019-20 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

const { task, context } = require("fuse-box/sparky");
const { FuseBox, CSSPlugin, CSSResourcePlugin, SassPlugin, WebIndexPlugin, QuantumPlugin } = require("fuse-box");

const path = require('path');
const fs = require('fs');

const kLibraryName = "Panmirror"
const kLibraryNameLower = kLibraryName.toLowerCase()
const kIdeJsDir = "../../../www/js"
const kIdeOutputDir = path.join(kIdeJsDir, kLibraryNameLower);

context(
  class {
    getConfig(outputDir, webIndex = false, vendorSrcMap = false) {
      return FuseBox.init({
        homeDir: "src",
        modulesFolder: "./node_modules",
        target: "browser@es6",
        globals: { default: kLibraryName },
        output: path.join(outputDir, "$name.js"),
        sourceMaps: { inline: true , project: true, vendor: vendorSrcMap && !this.isProduction },
        plugins: [
          webIndex && WebIndexPlugin({ template: "dev/index.html" }),
          [ CSSResourcePlugin({ inline: true }), CSSPlugin() ],
          this.isProduction && QuantumPlugin({ uglify: { es6: true }, bakeApiIntoBundle: true, containedAPI: true }),
        ],
      });
    }
  },
);

const bundle = (fuse) => {
  return fuse
    .bundle(kLibraryNameLower)
    .instructions("> editor.ts")
}

const watch = (fuse, hmrReload) => {
  return fuse
    .hmr({ reload: hmrReload })
    .watch()
}

const dev = (context, webIndex, watchChanges, hmrReload, outputDir) => {

  // setup fuse for development
  const fuse = context.getConfig(outputDir, webIndex, true);
  const bdl = bundle(fuse)
  if (watchChanges)
    watch(bdl, hmrReload)

  // copy prosemirror-devtools
  const devtools = 'prosemirror-dev-tools.min.js';
  fs.copyFileSync(
    path.join('./node_modules/prosemirror-dev-tools/dist/umd', devtools),
    path.join(outputDir, devtools)
  );

  return fuse;
}

const dist = (context, outputDir) => {
  context.isProduction = true;
  const fuse = context.getConfig(outputDir);
  bundle(fuse);
  return fuse;
}

task("ide-dev", async context => {
  await dev(context, false, false, false, kIdeOutputDir).run()
});

task("ide-dev-watch", async context => {
  const fuse = dev(context, false, true, true, kIdeOutputDir);
  fuse.dev();
  await fuse.run();
})

task("ide-dist",async context => {
  await dist(context, kIdeOutputDir).run();
});

<commit_msg>search /opt/rstudio-tools for npm panmirror modules<commit_after>/*
 * fuse.js
 *
 * Copyright (C) 2019-20 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

const { task, context } = require("fuse-box/sparky");
const { FuseBox, CSSPlugin, CSSResourcePlugin, SassPlugin, WebIndexPlugin, QuantumPlugin } = require("fuse-box");

const path = require('path');
const fs = require('fs');

const kLibraryName = "Panmirror"
const kLibraryNameLower = kLibraryName.toLowerCase()
const kIdeJsDir = "../../../www/js"
const kIdeOutputDir = path.join(kIdeJsDir, kLibraryNameLower);

context(
  class {
    getConfig(outputDir, webIndex = false, vendorSrcMap = false) {
      return FuseBox.init({
        homeDir: "src",
        modulesFolder: ["./node_modules", "/opt/rstudio-tools/panmirror/node_modules"],
        target: "browser@es6",
        globals: { default: kLibraryName },
        output: path.join(outputDir, "$name.js"),
        sourceMaps: { inline: true , project: true, vendor: vendorSrcMap && !this.isProduction },
        plugins: [
          webIndex && WebIndexPlugin({ template: "dev/index.html" }),
          [ CSSResourcePlugin({ inline: true }), CSSPlugin() ],
          this.isProduction && QuantumPlugin({ uglify: { es6: true }, bakeApiIntoBundle: true, containedAPI: true }),
        ],
      });
    }
  },
);

const bundle = (fuse) => {
  return fuse
    .bundle(kLibraryNameLower)
    .instructions("> editor.ts")
}

const watch = (fuse, hmrReload) => {
  return fuse
    .hmr({ reload: hmrReload })
    .watch()
}

const dev = (context, webIndex, watchChanges, hmrReload, outputDir) => {

  // setup fuse for development
  const fuse = context.getConfig(outputDir, webIndex, true);
  const bdl = bundle(fuse)
  if (watchChanges)
    watch(bdl, hmrReload)

  // copy prosemirror-devtools
  const devtools = 'prosemirror-dev-tools.min.js';
  fs.copyFileSync(
    path.join('./node_modules/prosemirror-dev-tools/dist/umd', devtools),
    path.join(outputDir, devtools)
  );

  return fuse;
}

const dist = (context, outputDir) => {
  context.isProduction = true;
  const fuse = context.getConfig(outputDir);
  bundle(fuse);
  return fuse;
}

task("ide-dev", async context => {
  await dev(context, false, false, false, kIdeOutputDir).run()
});

task("ide-dev-watch", async context => {
  const fuse = dev(context, false, true, true, kIdeOutputDir);
  fuse.dev();
  await fuse.run();
})

task("ide-dist",async context => {
  await dist(context, kIdeOutputDir).run();
});

<|endoftext|>


<commit_before>(function(){

    "use strict";

    XMOT.namespace("XMOT.interaction.geometry");

    /** Geometry is used specifically by widgets to
     *  encapsulate it's geometry construction and handling. Usually
     *  a widget defines a certain type of geometry constructor using
     *  the GeometryType field of XMOT.interaction.widgets.Widget
     *  and the Widget does the rest.
     *
     *  Derived classes should override the following methods:
     *  o onCreateDefsElements(): called when the defs elements should be setup
     *  o onCreateGraph(): called when the actual graph is to be set-up. The defs elements are already
     *      attached at this point
     *  o onTargetXfmChanged(): called when the world transformation of the widget's target node changes.
     */
    XMOT.interaction.geometry.Geometry = new XMOT.Class({

        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @param {XMOT.interaction.widgets.Widget} widget
         */
        initialize: function(widget)
        {
            this.geo = new XMOT.util.GeoObject(widget.ID, widget.xml3d, widget.root.object);
            this.widget = widget;

            this._targetTracker = new XMOT.TransformTracker(widget.target.object);
            this._targetTracker.xfmChanged = this.callback("onTargetXfmChanged");
            this._targetTracker.attach();
        },

        /** Shortcut to graph root
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        getRoot: function()
        {
            return this.geo.getGraphRoot();
        },

        /** Shortcut to geometry access
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @param {string} id
         */
        getGeo: function(id)
        {
            return this.geo.graph[id];
        },

        /** Shortcut to geometry access
         *  @this {XMOT.interaction.geometry.Geometry}
         *
         *  @param {string} id
         *  @param {Object} geo geometry to be stored
         *  @return {Object} the given geometry
         */
        setGeo: function(id, geo)
        {
            this.geo.graph[id] = geo;
            return geo;
        },

        /** Setup the defs elements, attach them to the scene graph,
         *  create the graph and attach the graph, too.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        constructAndAttach: function()
        {
            this.createDefsElements();
            this.geo.attachDefs();

            this.createGraph();
            this.geo.attachGraph();

            this.onTargetXfmChanged();
        },

        destroy: function()
        {
            this.geo.destroy();
        },

        // to be overriden by derived class
        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        createDefsElements: function()
        {
            this.geo.addTransforms("t_root", {
                rotation: this.widget.target.getOrientation().str(),
                translation: this.widget.target.getPosition().str()
            });
            this.onCreateDefsElements();
        },

        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        createGraph: function()
        {
            this.geo.setGraphRoot(XMOT.creation.element("group", {
                id: this.geo.globalID("g_root"),
                transform: "#" + this.geo.globalID("t_root")
            }));

            this.onCreateGraph();
        },

        /** This is called when the target transformation changes.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onTargetXfmChanged: function() {},

        /** This is called when the defs elements are created. The geometry's
         *  root transform t_root is already created.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onCreateDefsElements: function() {},

        /** This is called when the graph is created. The geometry's root
         *  is already initialized.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onCreateGraph: function() {}
    });
}());
<commit_msg>interaction-geometry: incorporated target node's scaling<commit_after>(function(){

    "use strict";

    XMOT.namespace("XMOT.interaction.geometry");

    /** Geometry is used specifically by widgets to
     *  encapsulate it's geometry construction and handling. Usually
     *  a widget defines a certain type of geometry constructor using
     *  the GeometryType field of XMOT.interaction.widgets.Widget
     *  and the Widget does the rest.
     *
     *  Derived classes should override the following methods:
     *  o onCreateDefsElements(): called when the defs elements should be setup
     *  o onCreateGraph(): called when the actual graph is to be set-up. The defs elements are already
     *      attached at this point
     *  o onTargetXfmChanged(): called when the world transformation of the widget's target node changes.
     */
    XMOT.interaction.geometry.Geometry = new XMOT.Class({

        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @param {XMOT.interaction.widgets.Widget} widget
         */
        initialize: function(widget)
        {
            this.geo = new XMOT.util.GeoObject(widget.ID, widget.xml3d, widget.root.object);
            this.widget = widget;

            this._targetTracker = new XMOT.TransformTracker(widget.target.object);
            this._targetTracker.xfmChanged = this.callback("onTargetXfmChanged");
            this._targetTracker.attach();
        },

        /** Shortcut to graph root
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        getRoot: function()
        {
            return this.geo.getGraphRoot();
        },

        /** Shortcut to geometry access
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @param {string} id
         */
        getGeo: function(id)
        {
            return this.geo.graph[id];
        },

        /** Shortcut to geometry access
         *  @this {XMOT.interaction.geometry.Geometry}
         *
         *  @param {string} id
         *  @param {Object} geo geometry to be stored
         *  @return {Object} the given geometry
         */
        setGeo: function(id, geo)
        {
            this.geo.graph[id] = geo;
            return geo;
        },

        /** Setup the defs elements, attach them to the scene graph,
         *  create the graph and attach the graph, too.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        constructAndAttach: function()
        {
            this.createDefsElements();
            this.geo.attachDefs();

            this.createGraph();
            this.geo.attachGraph();

            this.onTargetXfmChanged();
        },

        destroy: function()
        {
            this.geo.destroy();
        },

        // to be overriden by derived class
        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        createDefsElements: function()
        {
            this.geo.addTransforms("t_root", {
                rotation: this.widget.target.getOrientation().str(),
                translation: this.widget.target.getPosition().str(),
                scale: this.widget.target.getScale().str()
            });
            this.onCreateDefsElements();
        },

        /**
         *  @this {XMOT.interaction.geometry.Geometry}
         */
        createGraph: function()
        {
            this.geo.setGraphRoot(XMOT.creation.element("group", {
                id: this.geo.globalID("g_root"),
                transform: "#" + this.geo.globalID("t_root")
            }));

            this.onCreateGraph();
        },

        /** This is called when the target transformation changes.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onTargetXfmChanged: function() {},

        /** This is called when the defs elements are created. The geometry's
         *  root transform t_root is already created.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onCreateDefsElements: function() {},

        /** This is called when the graph is created. The geometry's root
         *  is already initialized.
         *
         *  @this {XMOT.interaction.geometry.Geometry}
         *  @protected
         */
        onCreateGraph: function() {}
    });
}());
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import Tooltip, { TooltipElement } from 'common/Tooltip';
import DiscordLogo from 'interface/icons/DiscordTiny';
import GithubLogo from 'interface/icons/GitHubMarkSmall';
import PatreonIcon from 'interface/icons/PatreonTiny';
import { hasPremium } from 'interface/selectors/user';
import Logo from 'interface/images/logo.svg';
import Ad from 'interface/common/Ad';

import './style.scss';

class Footer extends React.PureComponent {
  static propTypes = {
    premium: PropTypes.bool,
  };

  render() {
    const { premium } = this.props;

    return (

              Be a part of us

              {/* For some reason the tooltip disappears and reappears when mousing over the svg icons (maybe when the cursor leaves filled areas)*/}
              <Tooltip content="Discord">

                  <DiscordLogo />

              </Tooltip>
              <Tooltip content="GitHub">

                  <GithubLogo />

              </Tooltip>
              <Tooltip content="Patreon">

                  <PatreonIcon />

              </Tooltip>

              Log data from Warcaft Logs.{' '}
              <TooltipElement
                content={(
                  <>
                    Icon creators:

Fingerprint by IconsGhost

Scroll by jngll

Delete by johartcamp

Skull by Royyan Razka

Heart by Emir Palavan

armor by Jetro Cabau Quirós

Checklist by David

Idea by Anne

About Document by Deepz

Stats by Barracuda

Dropdown by zalhan

timeline by Alexander Blagochevsky

abilities by sachin modgekar

Vision by supalerk laipawat

Lightning by Mooms

Grid Many Rows by Justin White

Info by Gregor Cresnar

Plus by Galaxicon

duration by Bohdan Burmich

link by arjuazka

                  </>
                )}
              >Icons by the Noun Project.</TooltipElement>

              World of Warcraft and related artwork is copyright of Blizzard Entertainment, Inc.

              This is a fan site and we are not affiliated.

          {premium === false && (

              <Ad
                data-ad-slot="3815063023" // Footer
                data-ad-format="rectangle"
                data-full-width-responsive="true"
              />

          )}

    );
  }
}

const mapStateToProps = state => ({
  premium: hasPremium(state),
});

export default connect(mapStateToProps)(Footer);
<commit_msg>Remove rectangle format from footer ad<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';

import Tooltip, { TooltipElement } from 'common/Tooltip';
import DiscordLogo from 'interface/icons/DiscordTiny';
import GithubLogo from 'interface/icons/GitHubMarkSmall';
import PatreonIcon from 'interface/icons/PatreonTiny';
import { hasPremium } from 'interface/selectors/user';
import Logo from 'interface/images/logo.svg';
import Ad from 'interface/common/Ad';

import './style.scss';

class Footer extends React.PureComponent {
  static propTypes = {
    premium: PropTypes.bool,
  };

  render() {
    const { premium } = this.props;

    return (

              Be a part of us

              {/* For some reason the tooltip disappears and reappears when mousing over the svg icons (maybe when the cursor leaves filled areas)*/}
              <Tooltip content="Discord">

                  <DiscordLogo />

              </Tooltip>
              <Tooltip content="GitHub">

                  <GithubLogo />

              </Tooltip>
              <Tooltip content="Patreon">

                  <PatreonIcon />

              </Tooltip>

              Log data from Warcaft Logs.{' '}
              <TooltipElement
                content={(
                  <>
                    Icon creators:

Fingerprint by IconsGhost

Scroll by jngll

Delete by johartcamp

Skull by Royyan Razka

Heart by Emir Palavan

armor by Jetro Cabau Quirós

Checklist by David

Idea by Anne

About Document by Deepz

Stats by Barracuda

Dropdown by zalhan

timeline by Alexander Blagochevsky

abilities by sachin modgekar

Vision by supalerk laipawat

Lightning by Mooms

Grid Many Rows by Justin White

Info by Gregor Cresnar

Plus by Galaxicon

duration by Bohdan Burmich

link by arjuazka

                  </>
                )}
              >Icons by the Noun Project.</TooltipElement>

              World of Warcraft and related artwork is copyright of Blizzard Entertainment, Inc.

              This is a fan site and we are not affiliated.

          {premium === false && (

              <Ad
                data-ad-slot="3815063023" // Footer
              />

          )}

    );
  }
}

const mapStateToProps = state => ({
  premium: hasPremium(state),
});

export default connect(mapStateToProps)(Footer);
<|endoftext|>


<commit_before>// @flow

import minimist from 'minimist';
import React from 'react';
import chalk from 'chalk';
import { renderToString } from 'react-dom/server';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import WebpackCleanupPlugin from 'webpack-cleanup-plugin';
import nodeExternals from 'webpack-node-externals';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
// import PolyfillInjectorPlugin from 'webpack-polyfill-injector';
import frameworkConfig from '../../shared/framework-config';
import projectMetadata from '../../shared/project-metadata';
import frameworkMetadata from '../../shared/framework-metadata';
import { resolveFrameworkSource } from '../util/resolve-util';
import argv from '../rjs-argv';
import logger from '../../shared/logger';
import getWebpackSettings from '../../shared/webpack-settings';
import BaseHelmet from '../../framework/app/BaseHelmet';
import renderPage from '../../framework/server/setup-http-server/render-page';
import RjsDumpStatsPlugin from './DumpEntryPointsPlugin';
import RequireEnsureHookPlugin from './RequireEnsureHookPlugin';
import BaseFeature from './BaseFeature';
import WebpackConfigBuilder, * as wcbUtils from './WebpackConfigBuilder';
import sortDependencies from './sort-dependencies';
import featureClasses from './features';

const isDev = process.env.NODE_ENV === 'development';
const isTest = process.env.NODE_ENV === 'test';

function parseFeatures(str) {
  if (!str) {
    return {};
  }

  const features = {};

  str.split(',').forEach(featureName => {
    if (featureName.charAt(0) === '-') {
      features[featureName.substr(1)] = false;
    } else {
      features[featureName] = true;
    }
  });

  return features;
}

export default class WebpackBase {

  static SIDE_SERVER = 0;
  static SIDE_CLIENT = 1;

  /** @private */
  isDev: boolean;

  /** @private */
  isTest: boolean;

  /** @private */
  side: number;

  constructor(side: number) {
    this.isDev = isDev;
    this.isTest = isTest;
    this.side = side;

    this.webpackConfigBuilder = new WebpackConfigBuilder();

    this.injectFeatures();
  }

  /** @private */
  isServer() {
    return this.side === WebpackBase.SIDE_SERVER;
  }

  injectFeatures() {
    logger.debug('Injecting webpack Features.');
    const requestedFeatures = parseFeatures(argv.features);

    const features = featureClasses.map(FeatureClass => new FeatureClass(this.isServer(), process.env.NODE_ENV));
    sortDependencies(features);

    for (const feature of features) {
      this.injectFeature(feature, requestedFeatures);
    }
  }

  /** @private */
  injectFeature(feature: BaseFeature, requestedFeatures: { [string]: boolean }) {

    const name = feature.getFeatureName();
    const enabled = requestedFeatures[name] != null ? requestedFeatures[name] : feature.isDefaultEnabled();

    logger.debug(`${enabled ? chalk.green('✓') : chalk.red('✘')} Feature ${name}`);

    if (!enabled) {
      return false;
    }

    feature.visit(this.webpackConfigBuilder);

    return true;
  }

  buildConfig() {
    const config: Object = {
      cache: true,
      name: this.isServer() ? 'Server' : 'Client',
      entry: this.getEntry(),
      output: this.getOutput(),
      module: {
        rules: this.buildRules(),
      },
      plugins: this.getPlugins(),

      // Allow error boundaries to display errors in non-main chunks
      // https://reactjs.org/docs/cross-origin-errors.html
      devtool: 'cheap-module-source-map',
      performance: {
        hints: false,
      },
      target: this.isServer() ? 'node' : 'web',
      resolve: {
        modules: ['node_modules'],
        extensions: [
          '.mjs',
          '.js',
          '.jsx',
          '.react.js',
        ],
        mainFields: [
          'webpack:main',
          'jsnext:module',
          'module',
          'jsnext:main',
          'main',
        ],
        alias: this.getAliases(),
        mode: this.isDev ? 'development' : 'production',
      },
    };

    if (this.isServer()) {
      // exclude any absolute module (npm/node) from the build, except this module.
      // this module must be included otherwise the server build will have two
      // separate versions of the framework loaded.
      // const anyAbsoluteExceptFrameworkAndCss = new RegExp(`^(?!${}(\\/.+)?$)[a-z\\-_0-9]+(?!.+\\.css$).+$`);

      config.externals = [
        nodeExternals({
          whitelist: [
            new RegExp(`^${frameworkMetadata.name}`),
            /\.css$/i,
          ],
        }),
      ];
    } else {

      // https://github.com/SebastianS90/webpack-polyfill-injector
      // config.entry = `webpack-polyfill-injector?${JSON.stringify({
      //   modules: [config.entry],
      // })}!`;

      config.resolve.mainFields.unshift('web');
      config.resolve.mainFields.unshift('jsnext:web');
      config.resolve.mainFields.unshift('browser');
      config.resolve.mainFields.unshift('jsnext:browser');
    }

    return wcbUtils.mergeRaw(this.webpackConfigBuilder, config);
  }

  /** @private */
  getEntry(): string[] {
    // front-end entry point.
    const entry = [
      this.isServer()
        ? resolveFrameworkSource('server/index')
        : resolveFrameworkSource('client/index'),
    ];

    // front-end dev libs.
    if (this.isDev && !this.isServer()) {
      entry.unshift(
        // Necessary for hot reloading with IE
        'eventsource-polyfill',
        'webpack-hot-middleware/client',
        require.resolve('./dev-preamble'),
      );
    } else if (this.isDev && this.isServer()) {
      entry.unshift(
        // hot reload if parent sends signal SIGUSR2
        require.resolve('./hmr-server'),
      );
    }

    return entry;
  }

  /** @private */
  getOutput(): Object {
    // Output to build directory.
    const output = getWebpackSettings(this.isServer()).output;

    if (this.isServer()) {
      output.libraryTarget = 'commonjs2';
    }

    if (this.isDev && !this.isServer()) {
      // Allow error boundaries to display errors in non-main chunks
      // https://reactjs.org/docs/cross-origin-errors.html
      output.crossOriginLoading = 'anonymous';
    }

    if (this.isDev || this.isServer()) {
      // Don't use hashes in dev mode for better building performance.
      // Don't use them on the server because they're not needed and not user friendly.
      Object.assign(output, {
        filename: '[name].js',
        chunkFilename: '[name].chunk.js',

        // https://github.com/mxstbr/react-boilerplate/issues/443
        // publicPath: 'http://localhost:3000',
      });
    } else {
      // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets.
      Object.assign(output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].chunk.js',
      });
    }

    return output;
  }

  /** @private */
  buildRules() {

    // TODO consider using url-loader rather than file-loader maybe ?
    // Need to test perf gain.

    return [{
      test: /\.(eot|ttf|woff|woff2)(\?.*$|$)/i,
      loader: 'file-loader',
    }, {
      test: wcbUtils.getFileTypeRegExp(this.webpackConfigBuilder, WebpackConfigBuilder.FILE_TYPE_IMG),
      use: [
        {
          loader: 'srcset-loader',
          options: {
            lightweight: true,
          },
        },
        'file-loader',
      ],
    }, {
      test: /\.(mp4|webm)/i,
      loader: 'file-loader',
    }, {
      // FIXME temporary fix for webpack 4 https://github.com/webpack-contrib/bundle-loader/issues/74
      test: /\.json$/i,
      type: 'javascript/auto',
      loader: 'json-loader',
    }]
      .concat(this.buildCssLoaders())
      .concat(wcbUtils.buildRules(this.webpackConfigBuilder));
  }

  /** @private */
  buildCssLoaders() {
    const pluggedCssLoaders = wcbUtils.getCssLoaders(this.webpackConfigBuilder);

    const cssLoaders = [{
      test: wcbUtils.getFileTypeRegExp(this.webpackConfigBuilder, WebpackConfigBuilder.FILE_TYPE_CSS),
      exclude: /node_modules/,
      use: [
        getCssLoader({
          modules: true,
          importLoaders: pluggedCssLoaders.length,
        }),
        ...pluggedCssLoaders,
      ],
    }, {
      test: /\.css$/i,
      include: /node_modules/,
      use: [getCssLoader({ modules: false })],
    }];

    const styleLoader = this.isServer() ? 'node-style-loader' : 'style-loader';
    for (const cssLoader of cssLoaders) {
      if (this.isDev) {
        cssLoader.use = [styleLoader, ...cssLoader.use];
      } else {
        cssLoader.use = ExtractTextPlugin.extract({
          fallback: styleLoader,
          use: cssLoader.use,
        });
      }
    }

    return cssLoaders;
  }

  /** @private */
  getAliases() {
    const frameworkAliases = {

      // Framework configuration directories
      '@@pre-init': frameworkConfig['pre-init'],
      '@@main-component': frameworkConfig['entry-react'],
      '@@directories.routes': frameworkConfig.directories.routes,
      '@@directories.translations': frameworkConfig.directories.translations,
      '@@directories.providers': frameworkConfig.directories.providers,

      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    };

    const customAliases = wcbUtils.getAliases(this.webpackConfigBuilder);
    Object.assign(frameworkAliases, customAliases);

    return frameworkAliases;
  }

  /** @private */
  getDefinedVars() {
    const NODE_ENV = JSON.stringify(process.env.NODE_ENV);
    const SIDE = JSON.stringify(this.isServer() ? 'server' : 'client');

    const programArgv = minimist(argv['--'] || []);

    const definedVariables = {
      'process.env.SIDE': SIDE,
      'process.env.BUILD_ENV': NODE_ENV,
      'process.env.PROCESS_NAME': JSON.stringify(`${projectMetadata.name} (${this.isServer() ? 'server' : 'client'})`),
      $$RJS_VARS$$: {
        FRAMEWORK_CONFIG: JSON.stringify(frameworkConfig),
        FRAMEWORK_METADATA: JSON.stringify(frameworkMetadata),
        PROJECT_METADATA: JSON.stringify(projectMetadata),
        PARSED_ARGV: JSON.stringify(programArgv),
      },
    };

    if (!this.isServer()) {
      // define process on the browser
      definedVariables.process = {
        env: {
          NODE_ENV,
        },
      };
    }

    return definedVariables;
  }

  /** @private */
  getPlugins() {
    // TODO inject DLLs <script data-dll='true' src='/${dllName}.dll.js'></script>`
    // TODO https://github.com/diurnalist/chunk-manifest-webpack-plugin
    const plugins = [
      new webpack.DefinePlugin(this.getDefinedVars()),
      new CopyWebpackPlugin([{
        from: { glob: `${frameworkConfig.directories.resources}/**/**/*` },
        to: './',
        toType: 'dir',
      }]),

      // Inject webpack bundle into HTML.
      new HtmlWebpackPlugin({
        inject: true,
        templateContent: buildIndexPage(),

        // FIXME temporary hack for webpack 4 https://github.com/jantimon/html-webpack-plugin/issues/870
        chunksSortMode: 'none',
        minify: this.isDev ? false : {
          removeComments: true,
          collapseWhitespace: true,
          removeRedundantAttributes: true,
          useShortDoctype: true,
          removeEmptyAttributes: true,
          removeStyleLinkTypeAttributes: true,
          keepClosingSlash: true,
          minifyJS: true,
          minifyCSS: true,
          minifyURLs: true,
        },
      }),

      // Dump chunk mapping and entryPoints so we can determine which client chunks to send depending on which
      // chunks were imported during server-side rendering
      new RjsDumpStatsPlugin(),
    ];

    if (this.isServer()) {
      plugins.push(
        // Hook import() directives on the server-side so we can know which
        // chunks are loaded for which routes and give them along with the HTTP response.
        new RequireEnsureHookPlugin(),
      );
    }

    // if (!this.isServer()) {
    //   plugins.push(
    //     new PolyfillInjectorPlugin({
    //       polyfills: ['Promise'],
    //     }),
    //   );
    // }

    if (this.isDev) {
      plugins.push(
        // enable hot reloading.
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NamedModulesPlugin(),

        new webpack.NoEmitOnErrorsPlugin(),

        // Watcher doesn't work well if you mistype casing in a path so we use
        // a plugin that prints an error when you attempt to do this.
        // See https://github.com/facebookincubator/create-react-app/issues/240
        new CaseSensitivePathsPlugin(),

        // If you require a missing module and then `npm install` it, you still have
        // to restart the development server for Webpack to discover it. This plugin
        // makes the discovery automatic so you don't have to restart.
        // See https://github.com/facebookincubator/create-react-app/issues/186
        // new WatchMissingNodeModulesPlugin(paths.appNodeModules), // TODO
      );
    }

    if (!this.isDev) {
      plugins.push(
        new WebpackCleanupPlugin({ quiet: true }),

        // Extract the CSS into a seperate file
        new ExtractTextPlugin('[name].[contenthash].css'),
      );
    }

    return plugins.concat(...wcbUtils.getPlugins(this.webpackConfigBuilder));
  }
}

function buildIndexPage() {
  return renderPage({
    body: renderToString(<BaseHelmet />),
  });
}

function getCssLoader(options = {}) {
  const loaderOptions = {
    importLoaders: options.importLoaders || 0,
  };

  if (isDev) {
    Object.assign(loaderOptions, {
      sourceMap: true,
    });
  } else {
    // TODO cssnano options
    Object.assign(loaderOptions, {
      minimize: true,
    });
  }

  if (options.modules) {
    Object.assign(loaderOptions, {
      modules: true,
      camelCase: true,
    });

    if (isDev) {
      Object.assign(loaderOptions, {
        localIdentName: '[local]__[hash:base64:5]',
      });
    }
  }

  return {
    loader: 'css-loader',
    options: loaderOptions,
  };
}
<commit_msg>refactor(webpack): Replace more plugins with new optimization option<commit_after>// @flow

import minimist from 'minimist';
import React from 'react';
import chalk from 'chalk';
import { renderToString } from 'react-dom/server';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import WebpackCleanupPlugin from 'webpack-cleanup-plugin';
import nodeExternals from 'webpack-node-externals';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import CopyWebpackPlugin from 'copy-webpack-plugin';
// import PolyfillInjectorPlugin from 'webpack-polyfill-injector';
import frameworkConfig from '../../shared/framework-config';
import projectMetadata from '../../shared/project-metadata';
import frameworkMetadata from '../../shared/framework-metadata';
import { resolveFrameworkSource } from '../util/resolve-util';
import argv from '../rjs-argv';
import logger from '../../shared/logger';
import getWebpackSettings from '../../shared/webpack-settings';
import BaseHelmet from '../../framework/app/BaseHelmet';
import renderPage from '../../framework/server/setup-http-server/render-page';
import RjsDumpStatsPlugin from './DumpEntryPointsPlugin';
import RequireEnsureHookPlugin from './RequireEnsureHookPlugin';
import BaseFeature from './BaseFeature';
import WebpackConfigBuilder, * as wcbUtils from './WebpackConfigBuilder';
import sortDependencies from './sort-dependencies';
import featureClasses from './features';

const isDev = process.env.NODE_ENV === 'development';
const isTest = process.env.NODE_ENV === 'test';

function parseFeatures(str) {
  if (!str) {
    return {};
  }

  const features = {};

  str.split(',').forEach(featureName => {
    if (featureName.charAt(0) === '-') {
      features[featureName.substr(1)] = false;
    } else {
      features[featureName] = true;
    }
  });

  return features;
}

export default class WebpackBase {

  static SIDE_SERVER = 0;
  static SIDE_CLIENT = 1;

  /** @private */
  isDev: boolean;

  /** @private */
  isTest: boolean;

  /** @private */
  side: number;

  constructor(side: number) {
    this.isDev = isDev;
    this.isTest = isTest;
    this.side = side;

    this.webpackConfigBuilder = new WebpackConfigBuilder();

    this.injectFeatures();
  }

  /** @private */
  isServer() {
    return this.side === WebpackBase.SIDE_SERVER;
  }

  injectFeatures() {
    logger.debug('Injecting webpack Features.');
    const requestedFeatures = parseFeatures(argv.features);

    const features = featureClasses.map(FeatureClass => new FeatureClass(this.isServer(), process.env.NODE_ENV));
    sortDependencies(features);

    for (const feature of features) {
      this.injectFeature(feature, requestedFeatures);
    }
  }

  /** @private */
  injectFeature(feature: BaseFeature, requestedFeatures: { [string]: boolean }) {

    const name = feature.getFeatureName();
    const enabled = requestedFeatures[name] != null ? requestedFeatures[name] : feature.isDefaultEnabled();

    logger.debug(`${enabled ? chalk.green('✓') : chalk.red('✘')} Feature ${name}`);

    if (!enabled) {
      return false;
    }

    feature.visit(this.webpackConfigBuilder);

    return true;
  }

  buildConfig() {
    const config: Object = {
      cache: true,
      name: this.isServer() ? 'Server' : 'Client',
      entry: this.getEntry(),
      output: this.getOutput(),
      module: {
        rules: this.buildRules(),
      },
      plugins: this.getPlugins(),

      // Allow error boundaries to display errors in non-main chunks
      // https://reactjs.org/docs/cross-origin-errors.html
      devtool: 'cheap-module-source-map',
      performance: {
        hints: false,
      },
      target: this.isServer() ? 'node' : 'web',
      resolve: {
        modules: ['node_modules'],
        extensions: [
          '.mjs',
          '.js',
          '.jsx',
          '.react.js',
        ],
        mainFields: [
          'webpack:main',
          'jsnext:module',
          'module',
          'jsnext:main',
          'main',
        ],
        alias: this.getAliases(),
        mode: this.isDev ? 'development' : 'production',
        optimization: {
          noEmitOnErrors: true,
        },
      },
    };

    if (this.isServer()) {
      // exclude any absolute module (npm/node) from the build, except this module.
      // this module must be included otherwise the server build will have two
      // separate versions of the framework loaded.
      // const anyAbsoluteExceptFrameworkAndCss = new RegExp(`^(?!${}(\\/.+)?$)[a-z\\-_0-9]+(?!.+\\.css$).+$`);

      config.externals = [
        nodeExternals({
          whitelist: [
            new RegExp(`^${frameworkMetadata.name}`),
            /\.css$/i,
          ],
        }),
      ];
    } else {

      // https://github.com/SebastianS90/webpack-polyfill-injector
      // config.entry = `webpack-polyfill-injector?${JSON.stringify({
      //   modules: [config.entry],
      // })}!`;

      config.resolve.mainFields.unshift('web');
      config.resolve.mainFields.unshift('jsnext:web');
      config.resolve.mainFields.unshift('browser');
      config.resolve.mainFields.unshift('jsnext:browser');
    }

    return wcbUtils.mergeRaw(this.webpackConfigBuilder, config);
  }

  /** @private */
  getEntry(): string[] {
    // front-end entry point.
    const entry = [
      this.isServer()
        ? resolveFrameworkSource('server/index')
        : resolveFrameworkSource('client/index'),
    ];

    // front-end dev libs.
    if (this.isDev && !this.isServer()) {
      entry.unshift(
        // Necessary for hot reloading with IE
        'eventsource-polyfill',
        'webpack-hot-middleware/client',
        require.resolve('./dev-preamble'),
      );
    } else if (this.isDev && this.isServer()) {
      entry.unshift(
        // hot reload if parent sends signal SIGUSR2
        require.resolve('./hmr-server'),
      );
    }

    return entry;
  }

  /** @private */
  getOutput(): Object {
    // Output to build directory.
    const output = getWebpackSettings(this.isServer()).output;

    if (this.isServer()) {
      output.libraryTarget = 'commonjs2';
    }

    if (this.isDev && !this.isServer()) {
      // Allow error boundaries to display errors in non-main chunks
      // https://reactjs.org/docs/cross-origin-errors.html
      output.crossOriginLoading = 'anonymous';
    }

    if (this.isDev || this.isServer()) {
      // Don't use hashes in dev mode for better building performance.
      // Don't use them on the server because they're not needed and not user friendly.
      Object.assign(output, {
        filename: '[name].js',
        chunkFilename: '[name].chunk.js',

        // https://github.com/mxstbr/react-boilerplate/issues/443
        // publicPath: 'http://localhost:3000',
      });
    } else {
      // Utilize long-term caching by adding content hashes (not compilation hashes) to compiled assets.
      Object.assign(output, {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].chunk.js',
      });
    }

    return output;
  }

  /** @private */
  buildRules() {

    // TODO consider using url-loader rather than file-loader maybe ?
    // Need to test perf gain.

    return [{
      test: /\.(eot|ttf|woff|woff2)(\?.*$|$)/i,
      loader: 'file-loader',
    }, {
      test: wcbUtils.getFileTypeRegExp(this.webpackConfigBuilder, WebpackConfigBuilder.FILE_TYPE_IMG),
      use: [
        {
          loader: 'srcset-loader',
          options: {
            lightweight: true,
          },
        },
        'file-loader',
      ],
    }, {
      test: /\.(mp4|webm)/i,
      loader: 'file-loader',
    }, {
      // FIXME temporary fix for webpack 4 https://github.com/webpack-contrib/bundle-loader/issues/74
      test: /\.json$/i,
      type: 'javascript/auto',
      loader: 'json-loader',
    }]
      .concat(this.buildCssLoaders())
      .concat(wcbUtils.buildRules(this.webpackConfigBuilder));
  }

  /** @private */
  buildCssLoaders() {
    const pluggedCssLoaders = wcbUtils.getCssLoaders(this.webpackConfigBuilder);

    const cssLoaders = [{
      test: wcbUtils.getFileTypeRegExp(this.webpackConfigBuilder, WebpackConfigBuilder.FILE_TYPE_CSS),
      exclude: /node_modules/,
      use: [
        getCssLoader({
          modules: true,
          importLoaders: pluggedCssLoaders.length,
        }),
        ...pluggedCssLoaders,
      ],
    }, {
      test: /\.css$/i,
      include: /node_modules/,
      use: [getCssLoader({ modules: false })],
    }];

    const styleLoader = this.isServer() ? 'node-style-loader' : 'style-loader';
    for (const cssLoader of cssLoaders) {
      if (this.isDev) {
        cssLoader.use = [styleLoader, ...cssLoader.use];
      } else {
        cssLoader.use = ExtractTextPlugin.extract({
          fallback: styleLoader,
          use: cssLoader.use,
        });
      }
    }

    return cssLoaders;
  }

  /** @private */
  getAliases() {
    const frameworkAliases = {

      // Framework configuration directories
      '@@pre-init': frameworkConfig['pre-init'],
      '@@main-component': frameworkConfig['entry-react'],
      '@@directories.routes': frameworkConfig.directories.routes,
      '@@directories.translations': frameworkConfig.directories.translations,
      '@@directories.providers': frameworkConfig.directories.providers,

      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    };

    const customAliases = wcbUtils.getAliases(this.webpackConfigBuilder);
    Object.assign(frameworkAliases, customAliases);

    return frameworkAliases;
  }

  /** @private */
  getDefinedVars() {
    const NODE_ENV = JSON.stringify(process.env.NODE_ENV);
    const SIDE = JSON.stringify(this.isServer() ? 'server' : 'client');

    const programArgv = minimist(argv['--'] || []);

    const definedVariables = {
      'process.env.SIDE': SIDE,
      'process.env.BUILD_ENV': NODE_ENV,
      'process.env.PROCESS_NAME': JSON.stringify(`${projectMetadata.name} (${this.isServer() ? 'server' : 'client'})`),
      $$RJS_VARS$$: {
        FRAMEWORK_CONFIG: JSON.stringify(frameworkConfig),
        FRAMEWORK_METADATA: JSON.stringify(frameworkMetadata),
        PROJECT_METADATA: JSON.stringify(projectMetadata),
        PARSED_ARGV: JSON.stringify(programArgv),
      },
    };

    if (!this.isServer()) {
      // define process on the browser
      definedVariables.process = {
        env: {
          NODE_ENV,
        },
      };
    }

    return definedVariables;
  }

  /** @private */
  getPlugins() {
    // TODO inject DLLs <script data-dll='true' src='/${dllName}.dll.js'></script>`
    // TODO https://github.com/diurnalist/chunk-manifest-webpack-plugin
    const plugins = [
      new webpack.DefinePlugin(this.getDefinedVars()),
      new CopyWebpackPlugin([{
        from: { glob: `${frameworkConfig.directories.resources}/**/**/*` },
        to: './',
        toType: 'dir',
      }]),

      // Inject webpack bundle into HTML.
      new HtmlWebpackPlugin({
        inject: true,
        templateContent: buildIndexPage(),

        // FIXME temporary hack for webpack 4 https://github.com/jantimon/html-webpack-plugin/issues/870
        chunksSortMode: 'none',
        minify: this.isDev ? false : {
          removeComments: true,
          collapseWhitespace: true,
          removeRedundantAttributes: true,
          useShortDoctype: true,
          removeEmptyAttributes: true,
          removeStyleLinkTypeAttributes: true,
          keepClosingSlash: true,
          minifyJS: true,
          minifyCSS: true,
          minifyURLs: true,
        },
      }),

      // Dump chunk mapping and entryPoints so we can determine which client chunks to send depending on which
      // chunks were imported during server-side rendering
      new RjsDumpStatsPlugin(),
    ];

    if (this.isServer()) {
      plugins.push(
        // Hook import() directives on the server-side so we can know which
        // chunks are loaded for which routes and give them along with the HTTP response.
        new RequireEnsureHookPlugin(),
      );
    }

    // if (!this.isServer()) {
    //   plugins.push(
    //     new PolyfillInjectorPlugin({
    //       polyfills: ['Promise'],
    //     }),
    //   );
    // }

    if (this.isDev) {
      plugins.push(
        // enable hot reloading.
        new webpack.HotModuleReplacementPlugin(),

        // Watcher doesn't work well if you mistype casing in a path so we use
        // a plugin that prints an error when you attempt to do this.
        // See https://github.com/facebookincubator/create-react-app/issues/240
        new CaseSensitivePathsPlugin(),

        // If you require a missing module and then `npm install` it, you still have
        // to restart the development server for Webpack to discover it. This plugin
        // makes the discovery automatic so you don't have to restart.
        // See https://github.com/facebookincubator/create-react-app/issues/186
        // new WatchMissingNodeModulesPlugin(paths.appNodeModules), // TODO
      );
    }

    if (!this.isDev) {
      plugins.push(
        new WebpackCleanupPlugin({ quiet: true }),

        // Extract the CSS into a seperate file
        new ExtractTextPlugin('[name].[contenthash].css'),
      );
    }

    return plugins.concat(...wcbUtils.getPlugins(this.webpackConfigBuilder));
  }
}

function buildIndexPage() {
  return renderPage({
    body: renderToString(<BaseHelmet />),
  });
}

function getCssLoader(options = {}) {
  const loaderOptions = {
    importLoaders: options.importLoaders || 0,
  };

  if (isDev) {
    Object.assign(loaderOptions, {
      sourceMap: true,
    });
  } else {
    // TODO cssnano options
    Object.assign(loaderOptions, {
      minimize: true,
    });
  }

  if (options.modules) {
    Object.assign(loaderOptions, {
      modules: true,
      camelCase: true,
    });

    if (isDev) {
      Object.assign(loaderOptions, {
        localIdentName: '[local]__[hash:base64:5]',
      });
    }
  }

  return {
    loader: 'css-loader',
    options: loaderOptions,
  };
}
<|endoftext|>