<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.core',
  name: 'Int',
  extends: 'Property',

  properties: [
    'units',
    [ 'value', 0 ],
    'min',
    'max',
    [ 'adapt', function adaptInt(_, v) {
        return typeof v === 'number' ? Math.trunc(v) :
          v ? parseInt(v) :
          0 ;
      }
    ],
    [ 'fromString', function intFromString(str) {
        return str ? parseInt(str) : 0;
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'String',
  extends: 'Property',

  documentation: 'StringProperties coerce their arguments into Strings.',

  properties: [
    { class: 'Int', name: 'width', value: 30 },
    [ 'adapt', function(_, a) {
        return typeof a === 'function' ? foam.String.multiline(a) :
               typeof a === 'number'   ? String(a)                :
               a && a.toString         ? a.toString()             :
                                         ''                       ;
      }
    ],
    [ 'value', '' ]
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',

  documentation: 'Upgrade Mode.documentation to a proper String property.',

  properties: [
    { class: 'String', name: 'documentation' }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Date',
  extends: 'Property',

  // documentation: 'Describes properties of type Date.',
  label: 'Date',

  properties: [
    {
      name: 'adapt',
      value: function (_, d) {
        if ( typeof d === 'number' ) return new Date(d);
        if ( typeof d === 'string' ) {
          var ret = new Date(d);

          if ( isNaN(ret.getTime()) ) throw 'Invalid Date: ' + d;

          return ret;
        }
        return d;
      }
    },
    {
      name: 'comparePropertyValues',
      value: function(o1, o2) {
        if ( ! o1 ) return o2 ? -1 : 0;
        if ( ! o2 ) return 1;

        return foam.Date.compare(o1, o2);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'DateTime',
  extends: 'Date',

  documentation: 'Describes properties of type DateTime.',
  label: 'Date and time'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Byte',
  extends: 'Int',

  documentation: 'Describes properties of type Byte.',
  label: 'Round byte numbers'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Short',
  extends: 'Int',

  documentation: 'Describes properties of type Short.',
  label: 'Round short numbers'
});

foam.CLASS({
  package: 'foam.core',
  name:  'Long',
  extends: 'Int',

  documentation:  'Describes properties of type Long.',
  label: 'Round long numbers'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Float',
  extends: 'Int',

  // documentation:  'Describes properties of type Float.',
  label: 'Decimal numbers',

  properties: [
    'precision',
    [
      'adapt',
      function (_, v) {
        return typeof v === 'number' ? v : v ? parseFloat(v) : 0.0 ;
      }
    ]
  ]
});

/**
 No different than Float for JS, but useful when targeting with other languages.
 **/
foam.CLASS({
  package: 'foam.core',
  name: 'Double',
  extends: 'Float'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Function',
  extends: 'Property',

  documentation: 'Describes properties of type Function.',
  label: 'Code that can be run',

  properties: [
    [
      'value',
      function() {}
    ],
    [
      'assertValue',
      function(value, prop) {
        foam.assert(typeof value === 'function', prop.name, 'Cannot set to non function type.');
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Object',
  extends: 'Property',
  documentation: ''
});

foam.CLASS({
  package: 'foam.core',
  name: 'Array',
  extends: 'Property',

  properties: [
    [
      'factory',
      function() { return []; }
    ],
    [
      'isDefaultValue',
      function(v) { return ! v || ! v.length; }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'List',
  extends: 'foam.core.Object'
});

foam.CLASS({
  package: 'foam.core',
  name: 'StringArray',
  extends: 'Property',

  documentation: 'An array of String values.',
  label: 'List of text strings',

  properties: [
    {
      name: 'of',
      value: 'String',
      documentation: 'The FOAM sub-type of this property.'
    },
    [
      'factory',
      function() { return []; }
    ],
    [
      'adapt',
      function(_, v, prop) {
        if ( ! Array.isArray(v) ) return v;

        var copy;
        for ( var i = 0 ; i < v.length ; i++ ) {
          if ( typeof v[i] !== 'string' ) {
            if ( ! copy ) copy = v.slice();
            copy[i] = '' + v[i];
          }
        }

        return copy || v;
      }
    ],
    [
      'assertValue',
      function(v, prop) {
        if ( v === null ) return;

        foam.assert(Array.isArray(v),
            prop.name, 'Tried to set StringArray to non-array type.');
        for ( var i = 0 ; i < v.length ; i++ ) {
          foam.assert(typeof v[i] === 'string',
              prop.name, 'Element', i, 'is not a string', v[i]);
        }
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Class',
  extends: 'Property',

  properties: [
    {
      name: 'getter',
      value: function(prop) {
        var c = this.instance_[prop.name];

        // Implement value and factory support.
        if ( foam.Undefined.isInstance(c) ) {
          if ( ! foam.Undefined.isInstance(prop.value) ) {
            c = prop.value;
          } else if ( prop.factory ) {
            c = this.instance_[prop.name] = prop.factory.call(this, prop);
          }
        }

        // Upgrade Strings to actual classes, if available.
        if ( foam.String.isInstance(c) ) {
          c = this.lookup(c, true);
          if ( c ) this.instance_[prop.name] = c;
        }

        return c;
      }
    },
    {
      name: 'toJSON',
      value: function(value) { return value ? value.id : value; }
    }
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);

      var name = this.name;

      Object.defineProperty(proto, name + '$cls', {
        get: function classGetter() {
          console.warn("Deprecated use of 'cls.$cls'. Just use 'cls' instead.");
          return typeof this[name] !== 'string' ? this[name] :
            this.__context__.lookup(this[name], true);
        },
        configurable: true
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'EMail',
  extends: 'String',
  // FUTURE: verify
  label: 'Email address'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Image',
  extends: 'String',
  // FUTURE: verify
  label: 'Image data or link'
});

foam.CLASS({
  package: 'foam.core',
  name: 'URL',
  extends: 'String',
  // FUTURE: verify
  label: 'Web link (URL or internet address)'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Color',
  extends: 'String',
  label: 'Color'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Password',
  extends: 'String',
  label: 'Password that displays protected or hidden text'
});

foam.CLASS({
  package: 'foam.core',
  name: 'PhoneNumber',
  extends: 'String',
  label: 'Phone number'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Currency',
  extends: 'Long'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Map',
  extends: 'Property',

  // TODO: Remove need for sorting
  properties: [
    [ 'factory', function() { return {} } ],
    [
      'comparePropertyValues',
      function(o1, o2) {
        if ( foam.typeOf(o1) != foam.typeOf(o2) ) return -1;

        var keys1 = Object.keys(o1).sort();
        var keys2 = Object.keys(o2).sort();
        if ( keys1.length < keys2.length ) return -1;
        if ( keys1.length > keys2.length ) return 1;
        for ( var i = 0 ; i < keys1.length ; i++ ) {
          var c = foam.String.compare(keys1[i], keys2[i]);
          if ( c != 0 ) return c;
          c = foam.util.compare(o1[keys1[i]], o2[keys2[i]]);
          if ( c != 0 ) return c;
        }

        return 0;
      }
    ],
    [
      'cloneProperty',
      function(value, cloneMap) {
        if ( value ) {
          var tmp = cloneMap[this.name] = {};
          for ( var key in value ) {
            tmp[key] = value[key];
          }
        }
      }
    ],
    [
      'diffPropertyValues',
      function(o1, o2) {
        // TODO
      }
    ],
    'of'
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'FObjectProperty',
  extends: 'Property',

  properties: [
    {
      class: 'Class',
      name: 'of',
      value: 'foam.core.FObject'
    },
    {
      name: 'fromJSON',
      value: function(json, ctx, prop) {
        return foam.json.parse(json, prop.of, ctx);
      }
    },
    {
      name: 'adapt',
      value: function(_, v, prop) {
        // All FObjects may be null.
        if (v === null) return v;

        var of = prop.of;

        return of.isInstance(v) ?
            v :
            ( v.class ?
                this.lookup(v.class) :
                of ).create(v, this.__subContext__);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Reference',
  extends: 'Property',

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'String',
      name: 'targetDAOKey',
      expression: function(of) { return foam.String.daoize(of.name); }
    },
    {
      name: 'adapt',
      value: function(oldValue, newValue, prop) {
        return prop.of.isInstance(newValue) ?
          newValue.id :
          newValue ;
      }
    }
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);
      var key  = this.targetDAOKey;
      var name = this.name;

      Object.defineProperty(proto, name + '$find', {
        get: function classGetter() {
          return this.__context__[key].find(this[name]);
        },
        configurable: true
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',

  documentation: 'Update Model Property types.',

  properties: [
    { class: 'String',  name: 'name' },
    { class: 'Boolean', name: 'abstract' }
  ]
});

foam.CLASS({
  refines: 'Property',

  axioms: [
    foam.pattern.Faceted.create()
  ],

  properties: [
    {
      name: 'of'
    }
  ]
});

foam.CLASS({
  refines: 'Property',

  properties: [
    /**
      A short-name is an optional shorter name for a property.
      It is used by JSON and XML support when 'useShortNames'
      is enabled. Short-names enable output to be smaller,
      which can save disk space and/or network bandwidth.
      Ex.
    <pre>
      properties: [
        { name: 'firstName', shortName: 'fn' },
        { name: 'lastName',  shortName: 'ln' }
      ]
    </pre>
    */
    { class: 'String', name: 'shortName' }
  ]
});
<commit_msg>Added error logging for unknown class name.<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.core',
  name: 'Int',
  extends: 'Property',

  properties: [
    'units',
    [ 'value', 0 ],
    'min',
    'max',
    [ 'adapt', function adaptInt(_, v) {
        return typeof v === 'number' ? Math.trunc(v) :
          v ? parseInt(v) :
          0 ;
      }
    ],
    [ 'fromString', function intFromString(str) {
        return str ? parseInt(str) : 0;
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'String',
  extends: 'Property',

  documentation: 'StringProperties coerce their arguments into Strings.',

  properties: [
    { class: 'Int', name: 'width', value: 30 },
    [ 'adapt', function(_, a) {
        return typeof a === 'function' ? foam.String.multiline(a) :
               typeof a === 'number'   ? String(a)                :
               a && a.toString         ? a.toString()             :
                                         ''                       ;
      }
    ],
    [ 'value', '' ]
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',

  documentation: 'Upgrade Mode.documentation to a proper String property.',

  properties: [
    { class: 'String', name: 'documentation' }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Date',
  extends: 'Property',

  // documentation: 'Describes properties of type Date.',
  label: 'Date',

  properties: [
    {
      name: 'adapt',
      value: function (_, d) {
        if ( typeof d === 'number' ) return new Date(d);
        if ( typeof d === 'string' ) {
          var ret = new Date(d);

          if ( isNaN(ret.getTime()) ) throw 'Invalid Date: ' + d;

          return ret;
        }
        return d;
      }
    },
    {
      name: 'comparePropertyValues',
      value: function(o1, o2) {
        if ( ! o1 ) return o2 ? -1 : 0;
        if ( ! o2 ) return 1;

        return foam.Date.compare(o1, o2);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'DateTime',
  extends: 'Date',

  documentation: 'Describes properties of type DateTime.',
  label: 'Date and time'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Byte',
  extends: 'Int',

  documentation: 'Describes properties of type Byte.',
  label: 'Round byte numbers'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Short',
  extends: 'Int',

  documentation: 'Describes properties of type Short.',
  label: 'Round short numbers'
});

foam.CLASS({
  package: 'foam.core',
  name:  'Long',
  extends: 'Int',

  documentation:  'Describes properties of type Long.',
  label: 'Round long numbers'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Float',
  extends: 'Int',

  // documentation:  'Describes properties of type Float.',
  label: 'Decimal numbers',

  properties: [
    'precision',
    [
      'adapt',
      function (_, v) {
        return typeof v === 'number' ? v : v ? parseFloat(v) : 0.0 ;
      }
    ]
  ]
});

/**
 No different than Float for JS, but useful when targeting with other languages.
 **/
foam.CLASS({
  package: 'foam.core',
  name: 'Double',
  extends: 'Float'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Function',
  extends: 'Property',

  documentation: 'Describes properties of type Function.',
  label: 'Code that can be run',

  properties: [
    [
      'value',
      function() {}
    ],
    [
      'assertValue',
      function(value, prop) {
        foam.assert(typeof value === 'function', prop.name, 'Cannot set to non function type.');
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Object',
  extends: 'Property',
  documentation: ''
});

foam.CLASS({
  package: 'foam.core',
  name: 'Array',
  extends: 'Property',

  properties: [
    [
      'factory',
      function() { return []; }
    ],
    [
      'isDefaultValue',
      function(v) { return ! v || ! v.length; }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'List',
  extends: 'foam.core.Object'
});

foam.CLASS({
  package: 'foam.core',
  name: 'StringArray',
  extends: 'Property',

  documentation: 'An array of String values.',
  label: 'List of text strings',

  properties: [
    {
      name: 'of',
      value: 'String',
      documentation: 'The FOAM sub-type of this property.'
    },
    [
      'factory',
      function() { return []; }
    ],
    [
      'adapt',
      function(_, v, prop) {
        if ( ! Array.isArray(v) ) return v;

        var copy;
        for ( var i = 0 ; i < v.length ; i++ ) {
          if ( typeof v[i] !== 'string' ) {
            if ( ! copy ) copy = v.slice();
            copy[i] = '' + v[i];
          }
        }

        return copy || v;
      }
    ],
    [
      'assertValue',
      function(v, prop) {
        if ( v === null ) return;

        foam.assert(Array.isArray(v),
            prop.name, 'Tried to set StringArray to non-array type.');
        for ( var i = 0 ; i < v.length ; i++ ) {
          foam.assert(typeof v[i] === 'string',
              prop.name, 'Element', i, 'is not a string', v[i]);
        }
      }
    ]
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Class',
  extends: 'Property',

  properties: [
    [ 'getter', function(prop) {
        var c = this.instance_[prop.name];

        // Implement value and factory support.
        if ( foam.Undefined.isInstance(c) ) {
          if ( ! foam.Undefined.isInstance(prop.value) ) {
            c = prop.value;
          } else if ( prop.factory ) {
            c = this.instance_[prop.name] = prop.factory.call(this, prop);
          }
        }

        // Upgrade Strings to actual classes, if available.
        if ( foam.String.isInstance(c) ) {
          c = this.lookup(c, true);
          if ( c ) {
            this.instance_[prop.name] = c;
          } else {
            console.error('Unknown class: ' + c);
          }
        }

        return c;
      }
    ],
    ['toJSON', function(value) { return value ? value.id : value; } ]
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);

      var name = this.name;

      Object.defineProperty(proto, name + '$cls', {
        get: function classGetter() {
          console.warn("Deprecated use of 'cls.$cls'. Just use 'cls' instead.");
          return typeof this[name] !== 'string' ? this[name] :
            this.__context__.lookup(this[name], true);
        },
        configurable: true
      });
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'EMail',
  extends: 'String',
  // FUTURE: verify
  label: 'Email address'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Image',
  extends: 'String',
  // FUTURE: verify
  label: 'Image data or link'
});

foam.CLASS({
  package: 'foam.core',
  name: 'URL',
  extends: 'String',
  // FUTURE: verify
  label: 'Web link (URL or internet address)'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Color',
  extends: 'String',
  label: 'Color'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Password',
  extends: 'String',
  label: 'Password that displays protected or hidden text'
});

foam.CLASS({
  package: 'foam.core',
  name: 'PhoneNumber',
  extends: 'String',
  label: 'Phone number'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Currency',
  extends: 'Long'
});

foam.CLASS({
  package: 'foam.core',
  name: 'Map',
  extends: 'Property',

  // TODO: Remove need for sorting
  properties: [
    [ 'factory', function() { return {} } ],
    [
      'comparePropertyValues',
      function(o1, o2) {
        if ( foam.typeOf(o1) != foam.typeOf(o2) ) return -1;

        var keys1 = Object.keys(o1).sort();
        var keys2 = Object.keys(o2).sort();
        if ( keys1.length < keys2.length ) return -1;
        if ( keys1.length > keys2.length ) return 1;
        for ( var i = 0 ; i < keys1.length ; i++ ) {
          var c = foam.String.compare(keys1[i], keys2[i]);
          if ( c != 0 ) return c;
          c = foam.util.compare(o1[keys1[i]], o2[keys2[i]]);
          if ( c != 0 ) return c;
        }

        return 0;
      }
    ],
    [
      'cloneProperty',
      function(value, cloneMap) {
        if ( value ) {
          var tmp = cloneMap[this.name] = {};
          for ( var key in value ) {
            tmp[key] = value[key];
          }
        }
      }
    ],
    [
      'diffPropertyValues',
      function(o1, o2) {
        // TODO
      }
    ],
    'of'
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'FObjectProperty',
  extends: 'Property',

  properties: [
    {
      class: 'Class',
      name: 'of',
      value: 'foam.core.FObject'
    },
    {
      name: 'fromJSON',
      value: function(json, ctx, prop) {
        return foam.json.parse(json, prop.of, ctx);
      }
    },
    {
      name: 'adapt',
      value: function(_, v, prop) {
        // All FObjects may be null.
        if (v === null) return v;

        var of = prop.of;

        return of.isInstance(v) ?
            v :
            ( v.class ?
                this.lookup(v.class) :
                of ).create(v, this.__subContext__);
      }
    }
  ]
});

foam.CLASS({
  package: 'foam.core',
  name: 'Reference',
  extends: 'Property',

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      class: 'String',
      name: 'targetDAOKey',
      expression: function(of) { return foam.String.daoize(of.name); }
    },
    {
      name: 'adapt',
      value: function(oldValue, newValue, prop) {
        return prop.of.isInstance(newValue) ?
          newValue.id :
          newValue ;
      }
    }
  ],

  methods: [
    function installInProto(proto) {
      this.SUPER(proto);
      var key  = this.targetDAOKey;
      var name = this.name;

      Object.defineProperty(proto, name + '$find', {
        get: function classGetter() {
          return this.__context__[key].find(this[name]);
        },
        configurable: true
      });
    }
  ]
});

foam.CLASS({
  refines: 'foam.core.Model',

  documentation: 'Update Model Property types.',

  properties: [
    { class: 'String',  name: 'name' },
    { class: 'Boolean', name: 'abstract' }
  ]
});

foam.CLASS({
  refines: 'Property',

  axioms: [
    foam.pattern.Faceted.create()
  ],

  properties: [
    {
      name: 'of'
    }
  ]
});

foam.CLASS({
  refines: 'Property',

  properties: [
    /**
      A short-name is an optional shorter name for a property.
      It is used by JSON and XML support when 'useShortNames'
      is enabled. Short-names enable output to be smaller,
      which can save disk space and/or network bandwidth.
      Ex.
    <pre>
      properties: [
        { name: 'firstName', shortName: 'fn' },
        { name: 'lastName',  shortName: 'ln' }
      ]
    </pre>
    */
    { class: 'String', name: 'shortName' }
  ]
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

require("../lib/cli/commands/_node")({}, process.argv.slice(2), function(code) {
  if (typeof code === "number" && code !== 0) process.exit(code);
});
<commit_msg>fix: fixup node command from upgrade<commit_after>#!/usr/bin/env node

require("../lib/cli/commands/_node").default({}, process.argv.slice(2), function(code) {
  if (typeof code === "number" && code !== 0) process.exit(code);
});
<|endoftext|>


<commit_before>(function(e,t){t.Controller=function(t){var n=this;this.element=e(t)};t.Controller.extend=function(e){var n=function(n){var r=[],i=false,s=this;t.Controller.prototype.constructor.call(this,n);for(var o in e){if(e.hasOwnProperty(o)){if(o.indexOf(" ")!=-1){r[o]=e[o]}else if(o!=="init"){this[o]=e[o]}}}var u=function(){for(var e in r){var t=e.split(" "),n=t[0],o=t[1];(function(){var t=r[e];s.element.find(n).on(o,function(e){t.call(s,this,e)})})()}i=true};this.element.render=function(e,n,r){t.loadTemplate(e,n,function(e){s.element.html(e);u();if(r&&typeof r==="function"){r()}})};if(e["init"]){e["init"].call(this);if(!i){u()}}};n.prototype=Object.create(t.Controller.prototype);n.prototype.constructor=n;return n}})(window.jQuery,window.ich)<commit_msg>updates minified version<commit_after>(function(e,t){t.Controller=function(t){var n=this;this.element=e(t)};t.Controller.extend=function(e){var n=function(n){var r={},i=false,s=this;t.Controller.prototype.constructor.call(this,n);for(var o in e){if(e.hasOwnProperty(o)){if(o.indexOf(" ")!=-1){r[o]=e[o]}else if(o!=="init"){this[o]=e[o]}}}var u=function(){if(!i){for(var e in r){var t=e.split(" "),n=t[0],o=t[1];(function(){var t=r[e];s.element.find(n).on(o,function(e){t.call(s,this,e)})})()}i=true}};this.element.render=function(e,n,r){t.loadTemplate(e,n,function(e){s.element.html(e);u();if(r&&typeof r==="function"){r()}})};if(e["init"]&&typeof e["init"]==="function"){e["init"].call(this)}if(e["afterInit"]&&typeof e["afterInit"]==="function"){u();e["afterInit"].call(this)}};n.prototype=Object.create(t.Controller.prototype);n.prototype.constructor=n;return n}})(window.jQuery,window.ich)
<|endoftext|>


<commit_before>import { readFileSync } from 'fs'
import graphqlHTTP from 'express-graphql'
import { join } from 'path'
import setupBridge from '../../lib/graphql-bookshelf-bridge'
import {
  createComment,
  createPost,
  updatePost,
  findOrCreateThread,
  leaveCommunity,
  markActivityRead,
  markAllActivitiesRead,
  subscribe,
  updateCommunitySettings,
  updateCommunityTopic,
  updateMe,
  updateMembership,
  unlinkAccount,
  vote,
  addModerator,
  removeModerator
} from './mutations'
import makeModels from './makeModels'
import { makeExecutableSchema } from 'graphql-tools'

const schemaText = readFileSync(join(__dirname, 'schema.graphql')).toString()

function createSchema (userId, isAdmin) {
  const models = makeModels(userId, isAdmin)
  const { resolvers, fetchOne, fetchMany } = setupBridge(models)

  const allResolvers = Object.assign({
    Query: {
      me: () => fetchOne('Me', userId),
      community: (root, { id, slug }) => // you can specify id or slug, but not both
        fetchOne('Community', slug || id, slug ? 'slug' : 'id'),
      person: (root, { id }) => fetchOne('Person', id),
      messageThread: (root, { id }) => fetchOne('MessageThread', id),
      post: (root, { id }) => fetchOne('Post', id),
      posts: (root, args) => fetchMany('Post', args),
      people: (root, args) => fetchMany('Person', args),
      topics: (root, args) => fetchMany('Topic', args),
      connections: (root, args) => fetchMany('PersonConnection', args),
      communityTopics: (root, args) => fetchMany('CommunityTopic', args),
      topic: (root, { id, name }) => // you can specify id or name, but not both
        fetchOne('Topic', name || id, name ? 'name' : 'id'),
      communityTopic: (root, { topicName, communitySlug }) =>
        CommunityTag.findByTagAndCommunity(topicName, communitySlug)
    },
    Mutation: {
      updateMe: (root, { changes }) => updateMe(userId, changes),
      createPost: (root, { data }) => createPost(userId, data),
      updatePost: (root, args) => updatePost(userId, args),
      createComment: (root, { data }) => createComment(userId, data),
      createMessage: (root, { data }) => {
        data.postId = data.messageThreadId
        return createComment(userId, data)
      },
      findOrCreateThread: (root, { data }) => findOrCreateThread(userId, data),
      leaveCommunity: (root, { id }) => leaveCommunity(userId, id),
      markActivityRead: (root, { id }) => markActivityRead(userId, id),
      markAllActivitiesRead: (root) => markAllActivitiesRead(userId),
      subscribe: (root, { communityId, topicId, isSubscribing }) =>
        subscribe(userId, topicId, communityId, isSubscribing),
      updateCommunitySettings: (root, { id, changes }) =>
        updateCommunitySettings(userId, id, changes),
      updateCommunityTopic: (root, args) => updateCommunityTopic(userId, args),
      updateMembership: (root, args) => updateMembership(userId, args),
      unlinkAccount: (root, { provider }) => unlinkAccount(userId, provider),
      vote: (root, { postId, isUpvote }) => vote(userId, postId, isUpvote),
      addModerator: (root, { personId, communityId }) =>
        addModerator(userId, personId, communityId),
      removeModerator: (root, { personId, communityId }) =>
        removeModerator(userId, personId, communityId)
    },

    FeedItemContent: {
      __resolveType (data, context, info) {
        if (data instanceof bookshelf.Model) {
          return info.schema.getType('Post')
        }
        throw new Error('Post is the only implemented FeedItemContent type')
      }
    }
  }, resolvers)

  return makeExecutableSchema({
    typeDefs: [schemaText],
    resolvers: allResolvers
  })
}

export const createRequestHandler = () =>
  graphqlHTTP((req, res) => {
    // console.log(req.body.query)
    // console.log(req.body.variables)
    // TODO: this function can return a promise -- maybe run through some
    // policies based on the current user here and assign them to context, so
    // that the resolvers can use them to deny or restrict access?
    //
    // ideally we would be able to associate paths with policies, analyze the
    // query to find the policies which should be tested, and run them to allow
    // or deny access to those paths
    return {
      schema: createSchema(req.session.userId, Admin.isSignedIn(req)),
      graphiql: true
    }
  })
<commit_msg>log graphql queries<commit_after>import { readFileSync } from 'fs'
import graphqlHTTP from 'express-graphql'
import { join } from 'path'
import setupBridge from '../../lib/graphql-bookshelf-bridge'
import {
  createComment,
  createPost,
  updatePost,
  findOrCreateThread,
  leaveCommunity,
  markActivityRead,
  markAllActivitiesRead,
  subscribe,
  updateCommunitySettings,
  updateCommunityTopic,
  updateMe,
  updateMembership,
  unlinkAccount,
  vote,
  addModerator,
  removeModerator
} from './mutations'
import makeModels from './makeModels'
import { makeExecutableSchema } from 'graphql-tools'
import { inspect } from 'util'
import { red } from 'chalk'

const schemaText = readFileSync(join(__dirname, 'schema.graphql')).toString()

function createSchema (userId, isAdmin) {
  const models = makeModels(userId, isAdmin)
  const { resolvers, fetchOne, fetchMany } = setupBridge(models)

  const allResolvers = Object.assign({
    Query: {
      me: () => fetchOne('Me', userId),
      community: (root, { id, slug }) => // you can specify id or slug, but not both
        fetchOne('Community', slug || id, slug ? 'slug' : 'id'),
      person: (root, { id }) => fetchOne('Person', id),
      messageThread: (root, { id }) => fetchOne('MessageThread', id),
      post: (root, { id }) => fetchOne('Post', id),
      posts: (root, args) => fetchMany('Post', args),
      people: (root, args) => fetchMany('Person', args),
      topics: (root, args) => fetchMany('Topic', args),
      connections: (root, args) => fetchMany('PersonConnection', args),
      communityTopics: (root, args) => fetchMany('CommunityTopic', args),
      topic: (root, { id, name }) => // you can specify id or name, but not both
        fetchOne('Topic', name || id, name ? 'name' : 'id'),
      communityTopic: (root, { topicName, communitySlug }) =>
        CommunityTag.findByTagAndCommunity(topicName, communitySlug)
    },
    Mutation: {
      updateMe: (root, { changes }) => updateMe(userId, changes),
      createPost: (root, { data }) => createPost(userId, data),
      updatePost: (root, args) => updatePost(userId, args),
      createComment: (root, { data }) => createComment(userId, data),
      createMessage: (root, { data }) => {
        data.postId = data.messageThreadId
        return createComment(userId, data)
      },
      findOrCreateThread: (root, { data }) => findOrCreateThread(userId, data),
      leaveCommunity: (root, { id }) => leaveCommunity(userId, id),
      markActivityRead: (root, { id }) => markActivityRead(userId, id),
      markAllActivitiesRead: (root) => markAllActivitiesRead(userId),
      subscribe: (root, { communityId, topicId, isSubscribing }) =>
        subscribe(userId, topicId, communityId, isSubscribing),
      updateCommunitySettings: (root, { id, changes }) =>
        updateCommunitySettings(userId, id, changes),
      updateCommunityTopic: (root, args) => updateCommunityTopic(userId, args),
      updateMembership: (root, args) => updateMembership(userId, args),
      unlinkAccount: (root, { provider }) => unlinkAccount(userId, provider),
      vote: (root, { postId, isUpvote }) => vote(userId, postId, isUpvote),
      addModerator: (root, { personId, communityId }) =>
        addModerator(userId, personId, communityId),
      removeModerator: (root, { personId, communityId }) =>
        removeModerator(userId, personId, communityId)
    },

    FeedItemContent: {
      __resolveType (data, context, info) {
        if (data instanceof bookshelf.Model) {
          return info.schema.getType('Post')
        }
        throw new Error('Post is the only implemented FeedItemContent type')
      }
    }
  }, resolvers)

  return makeExecutableSchema({
    typeDefs: [schemaText],
    resolvers: allResolvers
  })
}

export const createRequestHandler = () =>
  graphqlHTTP((req, res) => {
    sails.log.info('\n' +
      red('graphql query start') + '\n' +
      req.body.query + '\n' +
      red('graphql query end')
    )
    sails.log.info(inspect(req.body.variables))
    // TODO: this function can return a promise -- maybe run through some
    // policies based on the current user here and assign them to context, so
    // that the resolvers can use them to deny or restrict access?
    //
    // ideally we would be able to associate paths with policies, analyze the
    // query to find the policies which should be tested, and run them to allow
    // or deny access to those paths
    return {
      schema: createSchema(req.session.userId, Admin.isSignedIn(req)),
      graphiql: true
    }
  })
<|endoftext|>


<commit_before>const assert = require('assert');
const {stripIndents} = require('common-tags');
const Router = require('express-promise-router');
const router = Router();

const Stages = require('../models/stage');
const Submissions = require('../models/submission');
const slack = require('../utils/slack');
const twitter = require('../utils/twitter');

const {validateSubmission, calculateScore} = require('../../lib/validator');
const stageData = require('../../stages');

router.get('/', async (req, res) => {
	const stages = await Stages.findAll();
	res.json(stages);
});

router.get('/:stage', async (req, res) => {
	const stageName = req.params.stage;

	const stage = await Stages.findOne({
		where: {
			name: stageName,
		},
	});

	if (stage === null) {
		res.status(404).json({
			error: true,
			message: 'not found',
		});
	} else {
		res.json(stage);
	}
});

router.get('/:stage/submissions', async (req, res) => {
	const stageName = req.params.stage;

	const submissions = await Submissions.findAll({
		include: [{
			model: Stages,
			where: {
				name: stageName,
				migratedVersion: {
					$col: 'submissions.version',
				},
			},
		}],
		order: [
			['score', 'DESC'],
			['updatedAt', 'ASC'],
		],
		limit: 20,
	});

	const data = submissions.map((submission) => ({
		name: submission.name,
		score: submission.score,
		blocks: submission.blocks,
		clocks: submission.clocks,
	}));

	res.json(data);
});

router.post('/:stage/submissions', async (req, res) => {
	const stageName = req.params.stage;

	const [stage, existingSubmission] = await Promise.all([
		Stages.findOne({
			where: {
				name: stageName,
			},
		}),
		Submissions.findOne({
			where: {
				name: req.body.name,
			},
			include: [{
				model: Stages,
				where: {
					name: stageName,
				},
			}],
			order: [
				['score', 'DESC'],
			],
		}),
	]);

	const submissionData = req.body;

	if (stage === null) {
		res.status(404).json({
			error: true,
			message: 'stage not found',
		});
		return;
	}

	submissionData.stage = stageName;

	const {pass, message, blocks, clocks} = validateSubmission(submissionData);

	if (!pass) {
		res.status(400).json({
			error: true,
			message,
		});
		return;
	}

	const stageDatum = stageData.find((s) => s.name === stageName);

	const score = calculateScore({
		clocks,
		blocks,
		stage: stageDatum,
	});

	if (existingSubmission) {
		if (score <= existingSubmission.score && stageDatum.version <= existingSubmission.version) {
			res.status(400).json({
				error: true,
				message: 'user name existing',
			});
			return;
		}

		const [count] = await Submissions.update({
			name: req.body.name,
			board: JSON.stringify(req.body.board),
			score,
			blocks,
			clocks,
			version: stageDatum.version,
			stageId: stage.id,
		}, {
			where: {
				name: req.body.name,
				stageId: stage.id,
			},
		});

		assert.strictEqual(count, 1);

		const submission = await Submissions.findOne({
			where: {
				name: req.body.name,
				stageId: stage.id,
			},
		});

		res.json(submission);
	} else {
		// If no previous submission is existing
		const submission = await Submissions.create({
			name: req.body.name,
			board: JSON.stringify(req.body.board),
			score,
			blocks,
			clocks,
			version: stageDatum.version,
			stageId: stage.id,
		});

		res.json(submission);
	}

	// Slack notification

	const submissions = await Submissions.findAll({
		include: [{
			model: Stages,
			where: {
				name: stageName,
				migratedVersion: {
					$col: 'submissions.version',
				},
			},
		}],
		order: [
			['score', 'DESC'],
			['updatedAt', 'ASC'],
		],
	});

	const rank = submissions.findIndex((submission) => submission.name === req.body.name);
	const surroundingSubmissions = submissions.map((submission, index) => (
		[index, submission]
	)).slice(rank - 1, rank + 2);

	slack.send({
		text: `*${req.body.name}* さんがステージ「${stageDatum.title}」を *${score}点* でクリアしました！`,
		attachments: surroundingSubmissions.map(([submissionRank, submission]) => (
			{
				text: `#${submissionRank + 1} ${submission.name}: ${submission.score}pts (${submission.blocks} blocks, ${submission.clocks} clocks)`,
				color: (submissionRank === rank) ? 'good' : null,
			}
		)),
	});

	const tweetText = stripIndents`
		${stripIndents`
			“${req.body.name}”さんがステージ「${stageDatum.title}」をクリアしました！
			${rank + 1}位にランクイン！

			💯Score: ${score}
			⏹Blocks: ${blocks}
			🕒Clocks: ${clocks}
		`.trim().replace(/([@＠#＃.．])/g, '$1 ')}
		#MNEMO
	`;

	twitter.tweet({
		status: Array.from(tweetText).slice(0, 140).join(''),
		// eslint-disable-next-line camelcase
		enable_dm_commands: false,
	});
});

module.exports = router;
<commit_msg>validate typeof req.body.name<commit_after>const assert = require('assert');
const {stripIndents} = require('common-tags');
const Router = require('express-promise-router');
const router = Router();

const Stages = require('../models/stage');
const Submissions = require('../models/submission');
const slack = require('../utils/slack');
const twitter = require('../utils/twitter');

const {validateSubmission, calculateScore} = require('../../lib/validator');
const stageData = require('../../stages');

router.get('/', async (req, res) => {
	const stages = await Stages.findAll();
	res.json(stages);
});

router.get('/:stage', async (req, res) => {
	const stageName = req.params.stage;

	const stage = await Stages.findOne({
		where: {
			name: stageName,
		},
	});

	if (stage === null) {
		res.status(404).json({
			error: true,
			message: 'not found',
		});
	} else {
		res.json(stage);
	}
});

router.get('/:stage/submissions', async (req, res) => {
	const stageName = req.params.stage;

	const submissions = await Submissions.findAll({
		include: [{
			model: Stages,
			where: {
				name: stageName,
				migratedVersion: {
					$col: 'submissions.version',
				},
			},
		}],
		order: [
			['score', 'DESC'],
			['updatedAt', 'ASC'],
		],
		limit: 20,
	});

	const data = submissions.map((submission) => ({
		name: submission.name,
		score: submission.score,
		blocks: submission.blocks,
		clocks: submission.clocks,
	}));

	res.json(data);
});

router.post('/:stage/submissions', async (req, res) => {
	const stageName = req.params.stage;

	if (typeof req.body.name !== 'string') {
		res.status(400).json({
			error: true,
			message: 'type of name must be string',
		});
		return;
	}

	const [stage, existingSubmission] = await Promise.all([
		Stages.findOne({
			where: {
				name: stageName,
			},
		}),
		Submissions.findOne({
			where: {
				name: req.body.name,
			},
			include: [{
				model: Stages,
				where: {
					name: stageName,
				},
			}],
			order: [
				['score', 'DESC'],
			],
		}),
	]);

	const submissionData = req.body;

	if (stage === null) {
		res.status(404).json({
			error: true,
			message: 'stage not found',
		});
		return;
	}

	submissionData.stage = stageName;

	const {pass, message, blocks, clocks} = validateSubmission(submissionData);

	if (!pass) {
		res.status(400).json({
			error: true,
			message,
		});
		return;
	}

	const stageDatum = stageData.find((s) => s.name === stageName);

	const score = calculateScore({
		clocks,
		blocks,
		stage: stageDatum,
	});

	if (existingSubmission) {
		if (score <= existingSubmission.score && stageDatum.version <= existingSubmission.version) {
			res.status(400).json({
				error: true,
				message: 'user name existing',
			});
			return;
		}

		const [count] = await Submissions.update({
			name: req.body.name,
			board: JSON.stringify(req.body.board),
			score,
			blocks,
			clocks,
			version: stageDatum.version,
			stageId: stage.id,
		}, {
			where: {
				name: req.body.name,
				stageId: stage.id,
			},
		});

		assert.strictEqual(count, 1);

		const submission = await Submissions.findOne({
			where: {
				name: req.body.name,
				stageId: stage.id,
			},
		});

		res.json(submission);
	} else {
		// If no previous submission is existing
		const submission = await Submissions.create({
			name: req.body.name,
			board: JSON.stringify(req.body.board),
			score,
			blocks,
			clocks,
			version: stageDatum.version,
			stageId: stage.id,
		});

		res.json(submission);
	}

	// Slack notification

	const submissions = await Submissions.findAll({
		include: [{
			model: Stages,
			where: {
				name: stageName,
				migratedVersion: {
					$col: 'submissions.version',
				},
			},
		}],
		order: [
			['score', 'DESC'],
			['updatedAt', 'ASC'],
		],
	});

	const rank = submissions.findIndex((submission) => submission.name === req.body.name);
	const surroundingSubmissions = submissions.map((submission, index) => (
		[index, submission]
	)).slice(rank - 1, rank + 2);

	slack.send({
		text: `*${req.body.name}* さんがステージ「${stageDatum.title}」を *${score}点* でクリアしました！`,
		attachments: surroundingSubmissions.map(([submissionRank, submission]) => (
			{
				text: `#${submissionRank + 1} ${submission.name}: ${submission.score}pts (${submission.blocks} blocks, ${submission.clocks} clocks)`,
				color: (submissionRank === rank) ? 'good' : null,
			}
		)),
	});

	const tweetText = stripIndents`
		${stripIndents`
			“${req.body.name}”さんがステージ「${stageDatum.title}」をクリアしました！
			${rank + 1}位にランクイン！

			💯Score: ${score}
			⏹Blocks: ${blocks}
			🕒Clocks: ${clocks}
		`.trim().replace(/([@＠#＃.．])/g, '$1 ')}
		#MNEMO
	`;

	twitter.tweet({
		status: Array.from(tweetText).slice(0, 140).join(''),
		// eslint-disable-next-line camelcase
		enable_dm_commands: false,
	});
});

module.exports = router;
<|endoftext|>


<commit_before>/* global internalScope */
'use strict';

(function () {
  function convertTranslate (input) {
    var valuesArray = internalScope.translateParse(input);

    if (!valuesArray) {
      return null;
    }

    var result = valuesArray.map(function (x) {
      return x + 'px';
    });

    return 'translate3d(' + result.join(', ') + ')';
  }

  function convertRotate (input) {
    var valuesArray = internalScope.rotateParse(input);
    if (!valuesArray) {
      return null;
    }

    if (valuesArray.length > 1) {
      return 'rotate3d(' + valuesArray.join(', ') + 'deg)';
    }
    return 'rotate(' + valuesArray.join(', ') + 'deg)';
  }

  function convertScale (input) {
    var valuesArray = internalScope.scaleParse(input);
    if (!valuesArray) {
      return null;
    }
    return 'scale3d(' + valuesArray.join(', ') + ')';
  }

  var pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');

  function convertPath (properties, positionAnchor, element) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath'], element);

    if (!offsetPath) {
      return null;
    }

    if (offsetPath.type === 'path') {
      return convertPathString(properties);
    }

    if (offsetPath.type === 'ray') {
      return convertRayString(properties, positionAnchor);
    }
  }

  function getOffsetDistanceLength (offsetDistance, pathLength, epsilon) {
    if (offsetDistance.unit === '%') {
      return Number(offsetDistance.value) * pathLength / 100 + epsilon;
    } else {
      return Number(offsetDistance.value) + epsilon;
    }
  }

  function isClosedLoop (path) {
    var pathInput = path.path.replace(/[,\s]+$/g, '');
    var lastPathInput = pathInput[pathInput.length - 1];

    return (lastPathInput === 'z' || lastPathInput === 'Z');
  }

  function getPathStringOffsetDistance (offsetPath, pathElement, offsetDistance, epsilon) {
    var closedLoop = isClosedLoop(offsetPath);
    var pathLength = pathElement.getTotalLength();

    var offsetDistanceLength = getOffsetDistanceLength(offsetDistance, pathLength, epsilon);

    if (closedLoop) {
      if (offsetDistanceLength < 0) {
        offsetDistanceLength = offsetDistanceLength % pathLength + pathLength;
      } else {
        offsetDistanceLength = offsetDistanceLength % pathLength;
      }
    } else if (offsetDistanceLength > pathLength) {
      offsetDistanceLength = pathLength;
    }

    return offsetDistanceLength;
  }

  function roundToHundredth (number) {
    return Math.round(number * 100) / 100;
  }

  function convertPathString (properties) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath']);
    var closedLoop = isClosedLoop(offsetPath);

    var offsetDistance = internalScope.offsetDistanceParse(properties['offsetDistance']);
    if (offsetDistance === undefined) {
      offsetDistance = {value: 0, unit: 'px'};
    }

    pathElement.setAttribute('d', offsetPath.path);
    var totalPathLength = pathElement.getTotalLength();

    var currentOffsetDistance = getPathStringOffsetDistance(offsetPath, pathElement, offsetDistance, 0);

    var epsilon = 0.0001;
    var rotateFlip = false;
    if (!closedLoop && (currentOffsetDistance + epsilon) > totalPathLength) {
      epsilon *= -1;
      rotateFlip = true;
    }
    var nextOffsetDistance = getPathStringOffsetDistance(offsetPath, pathElement, offsetDistance, epsilon);

    var currentPoint = pathElement.getPointAtLength(currentOffsetDistance);
    var nextPoint = pathElement.getPointAtLength(nextOffsetDistance);

    var deltaX = nextPoint.x - currentPoint.x;
    var deltaY = nextPoint.y - currentPoint.y;

    var rotation = Math.atan2(deltaY, deltaX) * 180 / Math.PI;

    if (rotateFlip) {
      rotation += 180;
    }

    return {deltaX: roundToHundredth(currentPoint.x), deltaY: roundToHundredth(currentPoint.y), rotation: roundToHundredth(rotation)};
  }

  function convertRayString (properties, positionAnchor) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath']);
    var rayLength = 0;

    if (positionAnchor) {
      rayLength = internalScope.getRayLength(offsetPath.size,
                                             positionAnchor.containerWidth,
                                             positionAnchor.containerHeight,
                                             positionAnchor.offsetPosX,
                                             positionAnchor.offsetPosY,
                                             offsetPath.angle);
    }

    var offsetDistance = internalScope.offsetDistanceParse(properties['offsetDistance']);

    if (offsetDistance === undefined) {
      offsetDistance = {value: 0, unit: 'px'};
    }

    var offsetDistanceLength = getOffsetDistanceLength(offsetDistance, rayLength, 0);

    var deltaX = Math.sin(offsetPath.angle * Math.PI / 180) * offsetDistanceLength;
    var deltaY = (-1) * Math.cos(offsetPath.angle * Math.PI / 180) * offsetDistanceLength;

    return {
      deltaX: roundToHundredth(deltaX),
      deltaY: roundToHundredth(deltaY),
      rotation: (offsetPath.angle - 90)};
  }

  function convertOffsetAnchorPosition (properties, element) {
    // According to spec: https://drafts.fxtf.org/motion-1/#offset-anchor-property
    // If offset-anchor is set to auto then it will compute to the value of offset-position.
    if (!element) {
      return null;
    }

    var position = 'auto';
    if ('offsetPosition' in properties) {
      position = internalScope.offsetPositionAnchorParse(properties['offsetPosition']);
      if (!position) {
        position = 'auto';
      }
    }

    var anchor = 'auto';
    if ('offsetAnchor' in properties) {
      anchor = internalScope.offsetPositionAnchorParse(properties['offsetAnchor']);
      if (!anchor) {
        anchor = 'auto';
      }
    }

    var transformOrigin = window.getComputedStyle(element).transformOrigin;
    transformOrigin = transformOrigin.split(/\s+/).map(internalScope.offsetDistanceParse);

    if (anchor === 'auto') {
      if (!properties['offsetPath'] || properties['offsetPath'] === 'none') {
        anchor = position;
      } else {
        anchor = transformOrigin;
      }
    }

    // TODO: find a way of doing this that doesn't involve _style
    var savedTransform = element.style._style.transform;
    // clear the transform so we can access the starting position of the element.
    element.style._style.transform = 'none';

    var offsetLeft = element.offsetLeft;
    var offsetTop = element.offsetTop;

    var elementProperties = element.getBoundingClientRect();
    var parentProperties = element.offsetParent ? element.offsetParent.getBoundingClientRect() : null;
    element.style._style.transform = savedTransform;

    if (!parentProperties) {
      return null;
    }

    var anchorPosX;
    var anchorPosY;
    if (anchor === 'auto') {
      anchorPosX = transformOrigin[0].value;
      anchorPosY = transformOrigin[1].value;
    } else {
      anchorPosX = anchor[0].value;
      anchorPosY = anchor[1].value;
      if (anchor[0].unit === '%') {
        anchorPosX = (anchorPosX * elementProperties.width) / 100;
      }

      if (anchor[1].unit === '%') {
        anchorPosY = (anchorPosY * elementProperties.height) / 100;
      }
    }

    var offsetPosX;
    var offsetPosY;
    if (position === 'auto') {
      offsetPosX = offsetLeft + anchorPosX;
      offsetPosY = offsetTop + anchorPosY;
    } else {
      offsetPosX = position[0].value;
      offsetPosY = position[1].value;
      if (position[0].unit === '%') {
        offsetPosX = (offsetPosX * parentProperties.width) / 100;
      }
      if (position[1].unit === '%') {
        offsetPosY = (offsetPosY * parentProperties.height) / 100;
      }
    }

    return {
      deltaX: (offsetPosX - anchorPosX) - offsetLeft,
      deltaY: (offsetPosY - anchorPosY) - offsetTop,
      anchorX: anchorPosX,
      anchorY: anchorPosY,
      transformOriginX: transformOrigin[0].value,
      transformOriginY: transformOrigin[1].value,
      offsetPosX: offsetPosX,
      offsetPosY: offsetPosY,
      containerWidth: parentProperties.width,
      containerHeight: parentProperties.height
    };
  }

  function convertOffsetRotate (properties, element) {
    var value = null;
    if ('offsetRotate' in properties) {
      value = internalScope.offsetRotateParse(properties['offsetRotate']);
    }

    if (!value) {
      return {angle: 0, auto: true};
    }

    return value;
  }

  function convertOffsetToTransform (properties, element) {
    /* W3C spec for what syntax toTransform() outputs:
       https://drafts.csswg.org/css-transforms/#transform-functions
       W3C spec for what syntax toTransform() parses:
       https://drafts.csswg.org/css-transforms-2/#individual-transforms
       https://drafts.fxtf.org/motion-1/#motion-paths-overview
    */
    var positionAnchor = convertOffsetAnchorPosition(properties, element);
    var pathTransform = convertPath(properties, positionAnchor, element);
    var parsedRotate = convertOffsetRotate(properties);
    var rotation = parsedRotate.angle;
    var path = internalScope.offsetPathParse(properties['offsetPath']);

    if (!pathTransform && !('offsetPosition' in properties)) {
      return null;
    }

    if (!pathTransform) {
      pathTransform = {deltaX: 0, deltaY: 0, rotation: 0};
    }

    if (parsedRotate.auto) {
      rotation += pathTransform.rotation;
    }

    if (!positionAnchor) {
      positionAnchor = {deltaX: 0, deltaY: 0};
    }

    var anchorX = positionAnchor.anchorX;
    var anchorY = positionAnchor.anchorY;
    var transformOriginX = positionAnchor.transformOriginX;
    var transformOriginY = positionAnchor.transformOriginY;
    var transform = 'translate3d(' +
        (pathTransform.deltaX + positionAnchor.deltaX) + 'px, ' +
        (pathTransform.deltaY + positionAnchor.deltaY) + 'px, 0px)';

    if (path !== undefined) {
      if (rotation !== undefined && rotation !== 0) {
        if (anchorX !== transformOriginX && anchorY !== transformOriginY) {
          var beforeShiftX = anchorX - transformOriginX;
          var beforeShiftY = anchorY - transformOriginY;
          var beforeShiftStr = 'translate3d(' + beforeShiftX + 'px, ' + beforeShiftY + 'px, 0px)';
          var afterShiftX = (-1) * beforeShiftX;
          var afterShiftY = (-1) * beforeShiftY;
          var afterShiftStr = 'translate3d(' + afterShiftX + 'px, ' + afterShiftY + 'px, 0px)';
          transform += ' ' + beforeShiftStr + ' rotate(' + rotation + 'deg) ' + afterShiftStr;
          return transform;
        }
        transform += ' rotate(' + rotation + 'deg)';
      }
    }
    return transform;
  }

  function toTransform (properties, element) {
    /* W3C spec for what syntax toTransform() outputs:
       https://drafts.csswg.org/css-transforms/#transform-functions
       W3C spec for what syntax toTransform() parses:
       https://drafts.csswg.org/css-transforms-2/#individual-transforms
       https://drafts.fxtf.org/motion-1/#motion-paths-overview
    */
    return [
      convertTranslate(properties.translate),
      convertRotate(properties.rotate),
      convertScale(properties.scale),
      convertOffsetToTransform(properties, element)
    ].filter(function (result) {
      return result !== null;
    }).join(' ') || 'none';
  }

  internalScope.toTransform = toTransform;
})();
<commit_msg>Use transform-origin (or not) in a single location<commit_after>/* global internalScope */
'use strict';

(function () {
  function convertTranslate (input) {
    var valuesArray = internalScope.translateParse(input);

    if (!valuesArray) {
      return null;
    }

    var result = valuesArray.map(function (x) {
      return x + 'px';
    });

    return 'translate3d(' + result.join(', ') + ')';
  }

  function convertRotate (input) {
    var valuesArray = internalScope.rotateParse(input);
    if (!valuesArray) {
      return null;
    }

    if (valuesArray.length > 1) {
      return 'rotate3d(' + valuesArray.join(', ') + 'deg)';
    }
    return 'rotate(' + valuesArray.join(', ') + 'deg)';
  }

  function convertScale (input) {
    var valuesArray = internalScope.scaleParse(input);
    if (!valuesArray) {
      return null;
    }
    return 'scale3d(' + valuesArray.join(', ') + ')';
  }

  var pathElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');

  function convertPath (properties, positionAnchor, element) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath'], element);

    if (!offsetPath) {
      return null;
    }

    if (offsetPath.type === 'path') {
      return convertPathString(properties);
    }

    if (offsetPath.type === 'ray') {
      return convertRayString(properties, positionAnchor);
    }
  }

  function getOffsetDistanceLength (offsetDistance, pathLength, epsilon) {
    if (offsetDistance.unit === '%') {
      return Number(offsetDistance.value) * pathLength / 100 + epsilon;
    } else {
      return Number(offsetDistance.value) + epsilon;
    }
  }

  function isClosedLoop (path) {
    var pathInput = path.path.replace(/[,\s]+$/g, '');
    var lastPathInput = pathInput[pathInput.length - 1];

    return (lastPathInput === 'z' || lastPathInput === 'Z');
  }

  function getPathStringOffsetDistance (offsetPath, pathElement, offsetDistance, epsilon) {
    var closedLoop = isClosedLoop(offsetPath);
    var pathLength = pathElement.getTotalLength();

    var offsetDistanceLength = getOffsetDistanceLength(offsetDistance, pathLength, epsilon);

    if (closedLoop) {
      if (offsetDistanceLength < 0) {
        offsetDistanceLength = offsetDistanceLength % pathLength + pathLength;
      } else {
        offsetDistanceLength = offsetDistanceLength % pathLength;
      }
    } else if (offsetDistanceLength > pathLength) {
      offsetDistanceLength = pathLength;
    }

    return offsetDistanceLength;
  }

  function roundToHundredth (number) {
    return Math.round(number * 100) / 100;
  }

  function convertPathString (properties) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath']);
    var closedLoop = isClosedLoop(offsetPath);

    var offsetDistance = internalScope.offsetDistanceParse(properties['offsetDistance']);
    if (offsetDistance === undefined) {
      offsetDistance = {value: 0, unit: 'px'};
    }

    pathElement.setAttribute('d', offsetPath.path);
    var totalPathLength = pathElement.getTotalLength();

    var currentOffsetDistance = getPathStringOffsetDistance(offsetPath, pathElement, offsetDistance, 0);

    var epsilon = 0.0001;
    var rotateFlip = false;
    if (!closedLoop && (currentOffsetDistance + epsilon) > totalPathLength) {
      epsilon *= -1;
      rotateFlip = true;
    }
    var nextOffsetDistance = getPathStringOffsetDistance(offsetPath, pathElement, offsetDistance, epsilon);

    var currentPoint = pathElement.getPointAtLength(currentOffsetDistance);
    var nextPoint = pathElement.getPointAtLength(nextOffsetDistance);

    var deltaX = nextPoint.x - currentPoint.x;
    var deltaY = nextPoint.y - currentPoint.y;

    var rotation = Math.atan2(deltaY, deltaX) * 180 / Math.PI;

    if (rotateFlip) {
      rotation += 180;
    }

    return {deltaX: roundToHundredth(currentPoint.x), deltaY: roundToHundredth(currentPoint.y), rotation: roundToHundredth(rotation)};
  }

  function convertRayString (properties, positionAnchor) {
    var offsetPath = internalScope.offsetPathParse(properties['offsetPath']);
    var rayLength = 0;

    if (positionAnchor) {
      rayLength = internalScope.getRayLength(offsetPath.size,
                                             positionAnchor.containerWidth,
                                             positionAnchor.containerHeight,
                                             positionAnchor.offsetPosX,
                                             positionAnchor.offsetPosY,
                                             offsetPath.angle);
    }

    var offsetDistance = internalScope.offsetDistanceParse(properties['offsetDistance']);

    if (offsetDistance === undefined) {
      offsetDistance = {value: 0, unit: 'px'};
    }

    var offsetDistanceLength = getOffsetDistanceLength(offsetDistance, rayLength, 0);

    var deltaX = Math.sin(offsetPath.angle * Math.PI / 180) * offsetDistanceLength;
    var deltaY = (-1) * Math.cos(offsetPath.angle * Math.PI / 180) * offsetDistanceLength;

    return {
      deltaX: roundToHundredth(deltaX),
      deltaY: roundToHundredth(deltaY),
      rotation: (offsetPath.angle - 90)};
  }

  function convertOffsetAnchorPosition (properties, element) {
    // According to spec: https://drafts.fxtf.org/motion-1/#offset-anchor-property
    // If offset-anchor is set to auto then it will compute to the value of offset-position.
    if (!element) {
      return null;
    }

    var position = 'auto';
    if ('offsetPosition' in properties) {
      position = internalScope.offsetPositionAnchorParse(properties['offsetPosition']);
      if (!position) {
        position = 'auto';
      }
    }

    var anchor = 'auto';
    if ('offsetAnchor' in properties) {
      anchor = internalScope.offsetPositionAnchorParse(properties['offsetAnchor']);
      if (!anchor) {
        anchor = 'auto';
      }
    }

    var transformOrigin = window.getComputedStyle(element).transformOrigin;
    transformOrigin = transformOrigin.split(/\s+/).map(internalScope.offsetDistanceParse);

    if (anchor === 'auto') {
      if (position === 'auto' || (properties['offsetPath'] && properties['offsetPath'] !== 'none')) {
        anchor = transformOrigin;
      } else {
        anchor = position;
      }
    }

    // TODO: find a way of doing this that doesn't involve _style
    var savedTransform = element.style._style.transform;
    // clear the transform so we can access the starting position of the element.
    element.style._style.transform = 'none';

    var offsetLeft = element.offsetLeft;
    var offsetTop = element.offsetTop;

    var elementProperties = element.getBoundingClientRect();
    var parentProperties = element.offsetParent ? element.offsetParent.getBoundingClientRect() : null;
    element.style._style.transform = savedTransform;

    if (!parentProperties) {
      return null;
    }

    var anchorPosX = anchor[0].value;
    var anchorPosY = anchor[1].value;
    if (anchor[0].unit === '%') {
      anchorPosX = (anchorPosX * elementProperties.width) / 100;
    }
    if (anchor[1].unit === '%') {
      anchorPosY = (anchorPosY * elementProperties.height) / 100;
    }

    var offsetPosX;
    var offsetPosY;
    if (position === 'auto') {
      offsetPosX = offsetLeft + anchorPosX;
      offsetPosY = offsetTop + anchorPosY;
    } else {
      offsetPosX = position[0].value;
      offsetPosY = position[1].value;
      if (position[0].unit === '%') {
        offsetPosX = (offsetPosX * parentProperties.width) / 100;
      }
      if (position[1].unit === '%') {
        offsetPosY = (offsetPosY * parentProperties.height) / 100;
      }
    }

    return {
      deltaX: (offsetPosX - anchorPosX) - offsetLeft,
      deltaY: (offsetPosY - anchorPosY) - offsetTop,
      anchorX: anchorPosX,
      anchorY: anchorPosY,
      transformOriginX: transformOrigin[0].value,
      transformOriginY: transformOrigin[1].value,
      offsetPosX: offsetPosX,
      offsetPosY: offsetPosY,
      containerWidth: parentProperties.width,
      containerHeight: parentProperties.height
    };
  }

  function convertOffsetRotate (properties, element) {
    var value = null;
    if ('offsetRotate' in properties) {
      value = internalScope.offsetRotateParse(properties['offsetRotate']);
    }

    if (!value) {
      return {angle: 0, auto: true};
    }

    return value;
  }

  function convertOffsetToTransform (properties, element) {
    /* W3C spec for what syntax toTransform() outputs:
       https://drafts.csswg.org/css-transforms/#transform-functions
       W3C spec for what syntax toTransform() parses:
       https://drafts.csswg.org/css-transforms-2/#individual-transforms
       https://drafts.fxtf.org/motion-1/#motion-paths-overview
    */
    var positionAnchor = convertOffsetAnchorPosition(properties, element);
    var pathTransform = convertPath(properties, positionAnchor, element);
    var parsedRotate = convertOffsetRotate(properties);
    var rotation = parsedRotate.angle;
    var path = internalScope.offsetPathParse(properties['offsetPath']);

    if (!pathTransform && !('offsetPosition' in properties)) {
      return null;
    }

    if (!pathTransform) {
      pathTransform = {deltaX: 0, deltaY: 0, rotation: 0};
    }

    if (parsedRotate.auto) {
      rotation += pathTransform.rotation;
    }

    if (!positionAnchor) {
      positionAnchor = {deltaX: 0, deltaY: 0};
    }

    var anchorX = positionAnchor.anchorX;
    var anchorY = positionAnchor.anchorY;
    var transformOriginX = positionAnchor.transformOriginX;
    var transformOriginY = positionAnchor.transformOriginY;
    var transform = 'translate3d(' +
        (pathTransform.deltaX + positionAnchor.deltaX) + 'px, ' +
        (pathTransform.deltaY + positionAnchor.deltaY) + 'px, 0px)';

    if (path !== undefined) {
      if (rotation !== undefined && rotation !== 0) {
        if (anchorX !== transformOriginX && anchorY !== transformOriginY) {
          var beforeShiftX = anchorX - transformOriginX;
          var beforeShiftY = anchorY - transformOriginY;
          var beforeShiftStr = 'translate3d(' + beforeShiftX + 'px, ' + beforeShiftY + 'px, 0px)';
          var afterShiftX = (-1) * beforeShiftX;
          var afterShiftY = (-1) * beforeShiftY;
          var afterShiftStr = 'translate3d(' + afterShiftX + 'px, ' + afterShiftY + 'px, 0px)';
          transform += ' ' + beforeShiftStr + ' rotate(' + rotation + 'deg) ' + afterShiftStr;
          return transform;
        }
        transform += ' rotate(' + rotation + 'deg)';
      }
    }
    return transform;
  }

  function toTransform (properties, element) {
    /* W3C spec for what syntax toTransform() outputs:
       https://drafts.csswg.org/css-transforms/#transform-functions
       W3C spec for what syntax toTransform() parses:
       https://drafts.csswg.org/css-transforms-2/#individual-transforms
       https://drafts.fxtf.org/motion-1/#motion-paths-overview
    */
    return [
      convertTranslate(properties.translate),
      convertRotate(properties.rotate),
      convertScale(properties.scale),
      convertOffsetToTransform(properties, element)
    ].filter(function (result) {
      return result !== null;
    }).join(' ') || 'none';
  }

  internalScope.toTransform = toTransform;
})();
<|endoftext|>


<commit_before>import React from 'react';
const ReactDOM = require('react-dom');
const Format = require('d3-format');
const Drag = require('d3-drag');
const Selection = require('d3-selection');

class Dynamic extends React.PureComponent {

  componentDidMount() {
    let node;
    try {
      node = ReactDOM.findDOMNode(this);
    } catch(e) {};
    if (!node) {
      return;
    }
    this.drag = Drag.drag().on('drag', () => {
      const dx = Selection.event.dx;
      const { step, value, interval } = this.props;
      const newValue = Math.max(Math.min(value + step || interval * dx, this.props.max), this.props.min);
      this.props.updateProps({ value: newValue });
    });
    this.drag(Selection.select(node));
  }

  render() {
    const { format, value } = this.props;
    const formatter = Format.format(format);
    return (

        {formatter(value)}

    );
  }
}

Dynamic.defaultProps = {
  format: '.2f',
  min: Number.NEGATIVE_INFINITY,
  max: Number.POSITIVE_INFINITY,
  step: 1
};

Dynamic._idyll = {
  name: "Dynamic",
  tagType: "closed",
  props: [{
    name: "value",
    type: "number",
    example: "x"
  }, {
    name: "step",
    type: "string",
    example: '1'
  }, {
    name: "min",
    type: "number",
    example: '-100'
  }, {
    name: "max",
    type: "number",
    example: '100'
  }]
}

export default Dynamic;
<commit_msg>fix bug in dynamic component<commit_after>import React from 'react';
const ReactDOM = require('react-dom');
const Format = require('d3-format');
const Drag = require('d3-drag');
const Selection = require('d3-selection');

class Dynamic extends React.PureComponent {

  componentDidMount() {
    let node;
    try {
      node = ReactDOM.findDOMNode(this);
    } catch(e) {};
    if (!node) {
      return;
    }
    this.drag = Drag.drag().on('drag', () => {
      const dx = Selection.event.dx;
      const { step, value, interval } = this.props;
      const newValue = Math.max(Math.min(value + (step || interval) * dx, this.props.max), this.props.min);
      this.props.updateProps({ value: newValue });
    });
    this.drag(Selection.select(node));
  }

  render() {
    const { format, value } = this.props;
    const formatter = Format.format(format);
    return (

        {formatter(value)}

    );
  }
}

Dynamic.defaultProps = {
  format: '.2f',
  min: Number.NEGATIVE_INFINITY,
  max: Number.POSITIVE_INFINITY,
  step: 1
};

Dynamic._idyll = {
  name: "Dynamic",
  tagType: "closed",
  props: [{
    name: "value",
    type: "number",
    example: "x"
  }, {
    name: "step",
    type: "string",
    example: '1'
  }, {
    name: "min",
    type: "number",
    example: '-100'
  }, {
    name: "max",
    type: "number",
    example: '100'
  }]
}

export default Dynamic;
<|endoftext|>


<commit_before>'use strict';
/**
 * `rawlist` type prompt
 */

const chalk = require('chalk');
const { map, takeUntil } = require('rxjs/operators');
const Base = require('./base');
const Separator = require('../objects/separator');
const observe = require('../utils/events');
const Paginator = require('../utils/paginator');
const incrementListIndex = require('../utils/incrementListIndex');

class RawListPrompt extends Base {
  constructor(questions, rl, answers) {
    super(questions, rl, answers);

    this.hiddenLine = '';
    this.lastKey = '';

    if (!this.opt.choices) {
      this.throwParamError('choices');
    }

    this.opt.validChoices = this.opt.choices.filter(Separator.exclude);

    this.selected = 0;
    this.rawDefault = 0;

    Object.assign(this.opt, {
      validate(val) {
        return val != null;
      },
    });

    const def = this.opt.default;
    if (typeof def === 'number' && def >= 0 && def < this.opt.choices.realLength) {
      this.selected = def;
      this.rawDefault = def;
    } else if (typeof def !== 'number' && def != null) {
      const index = this.opt.choices.realChoices.findIndex(({ value }) => value === def);
      const safeIndex = Math.max(index, 0);
      this.selected = safeIndex;
      this.rawDefault = safeIndex;
    }

    // Make sure no default is set (so it won't be printed)
    this.opt.default = null;

    const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop;
    this.paginator = new Paginator(undefined, { isInfinite: shouldLoop });
  }

  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.getCurrentValue.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.normalizedUpKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onUpKey.bind(this));
    events.normalizedDownKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onDownKey.bind(this));
    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));
    // Init the prompt
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {RawListPrompt} self
   */

  render(error) {
    // Render question
    let message = this.getQuestion();
    let bottomContent = '';

    if (this.status === 'answered') {
      message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
    } else {
      const choicesStr = renderChoices(this.opt.choices, this.selected);
      message +=
        '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
      message += '\n  Answer: ';
    }
    message += this.rl.line;

    if (error) {
      bottomContent = '\n' + chalk.red('>> ') + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  getCurrentValue(index) {
    if (index == null) {
      index = this.rawDefault;
    } else if (index === '') {
      this.selected = this.selected === undefined ? -1 : this.selected;
      index = this.selected;
    } else {
      index -= 1;
    }

    const choice = this.opt.choices.getChoice(index);
    return choice ? choice.value : null;
  }

  onEnd(state) {
    this.status = 'answered';
    this.answer = state.value;

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError() {
    this.render('Please enter a valid index');
  }

  /**
   * When user press a key
   */

  onKeypress() {
    let index;

    if (this.lastKey === 'arrow') {
      index = this.hiddenLine.length ? Number(this.hiddenLine) - 1 : 0;
    } else {
      index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;
    }
    this.lastKey = '';

    if (this.opt.choices.getChoice(index)) {
      this.selected = index;
    } else {
      this.selected = undefined;
    }
    this.render();
  }

  /**
   * When user press up key
   */

  onUpKey() {
    this.onArrowKey('up');
  }

  /**
   * When user press down key
   */

  onDownKey() {
    this.onArrowKey('down');
  }

  /**
   * When user press up or down key
   * @param {String} type Arrow type: up or down
   */

  onArrowKey(type) {
    this.selected = incrementListIndex(this.selected, type, this.opt);
    this.hiddenLine = String(this.selected + 1);
    this.rl.line = '';
    this.lastKey = 'arrow';
  }
}

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */

function renderChoices(choices, pointer) {
  let output = '';
  let separatorOffset = 0;

  choices.forEach((choice, i) => {
    output += '\n  ';

    if (choice.type === 'separator') {
      separatorOffset++;
      output += ' ' + choice;
      return;
    }

    const index = i - separatorOffset;
    let display = index + 1 + ') ' + choice.name;
    if (index === pointer) {
      display = chalk.cyan(display);
    }

    output += display;
  });

  return output;
}

module.exports = RawListPrompt;
<commit_msg>fix rawlist bugs (#1094)<commit_after>'use strict';
/**
 * `rawlist` type prompt
 */

const chalk = require('chalk');
const { map, takeUntil } = require('rxjs/operators');
const Base = require('./base');
const Separator = require('../objects/separator');
const observe = require('../utils/events');
const Paginator = require('../utils/paginator');
const incrementListIndex = require('../utils/incrementListIndex');

class RawListPrompt extends Base {
  constructor(questions, rl, answers) {
    super(questions, rl, answers);

    this.hiddenLine = '';
    this.lastKey = '';

    if (!this.opt.choices) {
      this.throwParamError('choices');
    }

    this.opt.validChoices = this.opt.choices.filter(Separator.exclude);

    this.selected = 0;
    this.rawDefault = 0;

    Object.assign(this.opt, {
      validate(val) {
        return val != null;
      },
    });

    const def = this.opt.default;
    if (typeof def === 'number' && def >= 0 && def < this.opt.choices.realLength) {
      this.selected = def;
      this.rawDefault = def;
    } else if (typeof def !== 'number' && def != null) {
      const index = this.opt.choices.realChoices.findIndex(({ value }) => value === def);
      const safeIndex = Math.max(index, 0);
      this.selected = safeIndex;
      this.rawDefault = safeIndex;
    }

    // Make sure no default is set (so it won't be printed)
    this.opt.default = null;

    const shouldLoop = this.opt.loop === undefined ? true : this.opt.loop;
    this.paginator = new Paginator(undefined, { isInfinite: shouldLoop });
  }

  /**
   * Start the Inquiry session
   * @param  {Function} cb      Callback when prompt is done
   * @return {this}
   */

  _run(cb) {
    this.done = cb;

    // Once user confirm (enter key)
    const events = observe(this.rl);
    const submit = events.line.pipe(map(this.getCurrentValue.bind(this)));

    const validation = this.handleSubmitEvents(submit);
    validation.success.forEach(this.onEnd.bind(this));
    validation.error.forEach(this.onError.bind(this));

    events.normalizedUpKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onUpKey.bind(this));
    events.normalizedDownKey
      .pipe(takeUntil(validation.success))
      .forEach(this.onDownKey.bind(this));
    events.keypress
      .pipe(takeUntil(validation.success))
      .forEach(this.onKeypress.bind(this));
    // Init the prompt
    this.render();

    return this;
  }

  /**
   * Render the prompt to screen
   * @return {RawListPrompt} self
   */

  render(error) {
    // Render question
    let message = this.getQuestion();
    let bottomContent = '';

    if (this.status === 'answered') {
      message += chalk.cyan(this.opt.choices.getChoice(this.selected).short);
    } else {
      const choicesStr = renderChoices(this.opt.choices, this.selected);
      message +=
        '\n' + this.paginator.paginate(choicesStr, this.selected, this.opt.pageSize);
      message += '\n  Answer: ';
    }
    message += this.rl.line;

    if (error) {
      bottomContent = '\n' + chalk.red('>> ') + error;
    }

    this.screen.render(message, bottomContent);
  }

  /**
   * When user press `enter` key
   */

  getCurrentValue(index) {
    if (index == null) {
      index = this.rawDefault;
    } else if (index === '') {
      this.selected = this.selected === undefined ? -1 : this.selected;
      index = this.selected;
    } else {
      index -= 1;
    }

    const choice = this.opt.choices.getChoice(index);
    return choice ? choice.value : null;
  }

  onEnd(state) {
    this.status = 'answered';
    this.answer = state.value;

    // Re-render prompt
    this.render();

    this.screen.done();
    this.done(state.value);
  }

  onError() {
    this.render('Please enter a valid index');
  }

  /**
   * When user press a key
   */

  onKeypress() {
    let index;

    if (this.lastKey === 'arrow') {
      index = this.hiddenLine.length ? Number(this.hiddenLine) - 1 : 0;
    } else {
      index = this.rl.line.length ? Number(this.rl.line) - 1 : 0;
    }
    this.lastKey = '';

    if (this.opt.choices.getChoice(index)) {
      this.selected = index;
    } else {
      this.selected = undefined;
    }
    this.render();
  }

  /**
   * When user press up key
   */

  onUpKey() {
    this.onArrowKey('up');
  }

  /**
   * When user press down key
   */

  onDownKey() {
    this.onArrowKey('down');
  }

  /**
   * When user press up or down key
   * @param {String} type Arrow type: up or down
   */

  onArrowKey(type) {
    this.selected = incrementListIndex(this.selected, type, this.opt) || 0;
    this.hiddenLine = String(this.selected + 1);
    this.rl.line = '';
    this.lastKey = 'arrow';
  }
}

/**
 * Function for rendering list choices
 * @param  {Number} pointer Position of the pointer
 * @return {String}         Rendered content
 */

function renderChoices(choices, pointer) {
  let output = '';
  let separatorOffset = 0;

  choices.forEach((choice, i) => {
    output += output ? '\n  ' : '  ';

    if (choice.type === 'separator') {
      separatorOffset++;
      output += ' ' + choice;
      return;
    }

    const index = i - separatorOffset;
    let display = index + 1 + ') ' + choice.name;
    if (index === pointer) {
      display = chalk.cyan(display);
    }

    output += display;
  });

  return output;
}

module.exports = RawListPrompt;
<|endoftext|>


<commit_before>const debug = require('ghost-ignition').debug('stripe');
const {retrieve, list, create, del} = require('./api/stripeRequests');
const api = require('./api');

const STRIPE_API_VERSION = '2019-09-09';

module.exports = class StripePaymentProcessor {
    constructor(config, storage, logging) {
        this.logging = logging;
        this.storage = storage;
        this._ready = new Promise((resolve, reject) => {
            this._resolveReady = resolve;
            this._rejectReady = reject;
        });
        this._configure(config);
    }

    async ready() {
        return this._ready;
    }

    async _configure(config) {
        this._stripe = require('stripe')(config.secretKey);
        this._stripe.setAppInfo(config.appInfo);
        this._stripe.setApiVersion(STRIPE_API_VERSION);
        this._stripe.__TEST_MODE__ = config.secretKey.startsWith('sk_test_');
        this._public_token = config.publicKey;
        this._checkoutSuccessUrl = config.checkoutSuccessUrl;
        this._checkoutCancelUrl = config.checkoutCancelUrl;
        this._webhookHandlerUrl = config.webhookHandlerUrl;

        try {
            this._product = await api.products.ensure(this._stripe, config.product);

            this._plans = [];
            for (const planSpec of config.plans) {
                const plan = await api.plans.ensure(this._stripe, planSpec, this._product);
                this._plans.push(plan);
            }

            const webhooks = await list(this._stripe, 'webhookEndpoints', {
                limit: 100
            });

            const webhookToDelete = webhooks.data.find((webhook) => {
                return webhook.url === this._webhookHandlerUrl;
            });

            if (webhookToDelete) {
                await del(this._stripe, 'webhookEndpoints', webhookToDelete.id);
            }

            try {
                const webhook = await create(this._stripe, 'webhookEndpoints', {
                    url: this._webhookHandlerUrl,
                    api_version: STRIPE_API_VERSION,
                    enabled_events: [
                        'checkout.session.completed',
                        'customer.subscription.deleted',
                        'customer.subscription.updated',
                        'invoice.payment_succeeded',
                        'invoice.payment_failed'
                    ]
                });
                this._webhookSecret = webhook.secret;
            } catch (err) {
                this.logging.warn(err);
                this._webhookSecret = process.env.WEBHOOK_SECRET;
            }
            debug(`Webhook secret set to ${this._webhookSecret}`);
        } catch (err) {
            debug(`Error configuring ${err.message}`);
            return this._rejectReady(err);
        }

        return this._resolveReady({
            product: this._product,
            plans: this._plans
        });
    }

    async parseWebhook(body, signature) {
        try {
            const event = await this._stripe.webhooks.constructEvent(body, signature, this._webhookSecret);
            debug(`Parsed webhook event: ${event.type}`);
            return event;
        } catch (err) {
            this.logging.error(`Error verifying webhook signature, using secret ${this._webhookSecret}`);
            throw err;
        }
    }

    async createCheckoutSession(member, planName, options) {
        let customer;
        if (member) {
            try {
                customer = await this._customerForMemberCheckoutSession(member);
            } catch (err) {
                debug(`Ignoring Error getting customer for checkout ${err.message}`);
                customer = null;
            }
        } else {
            customer = null;
        }
        const plan = this._plans.find(plan => plan.nickname === planName);
        const session = await this._stripe.checkout.sessions.create({
            payment_method_types: ['card'],
            success_url: options.successUrl || this._checkoutSuccessUrl,
            cancel_url: options.cancelUrl || this._checkoutCancelUrl,
            customer: customer ? customer.id : undefined,
            subscription_data: {
                items: [{
                    plan: plan.id
                }]
            }
        });

        return {
            sessionId: session.id,
            publicKey: this._public_token
        };
    }

    async cancelAllSubscriptions(member) {
        const subscriptions = await this.getSubscriptions(member);

        const activeSubscriptions = subscriptions.filter((subscription) => {
            return subscription.status !== 'cancelled';
        });

        await Promise.all(activeSubscriptions.map((subscription) => {
            return del(this._stripe, 'subscriptions', subscription.id);
        }));

        return true;
    }

    async getSubscriptions(member) {
        const metadata = await this.storage.get(member);

        const customers = metadata.customers.reduce((customers, customer) => {
            return Object.assign(customers, {
                [customer.customer_id]: {
                    id: customer.customer_id,
                    name: customer.name,
                    email: customer.email
                }
            });
        }, {});

        return metadata.subscriptions.map((subscription) => {
            return {
                id: subscription.subscription_id,
                customer: customers[subscription.customer_id],
                plan: {
                    id: subscription.plan_id,
                    nickname: subscription.plan_nickname,
                    interval: subscription.plan_interval,
                    amount: subscription.plan_amount,
                    currency: subscription.plan_currency
                },
                status: subscription.status,
                start_date: subscription.start_date,
                default_payment_card_last4: subscription.default_payment_card_last4,
                current_period_end: subscription.current_period_end
            };
        });
    }

    async getActiveSubscriptions(member) {
        const subscriptions = await this.getSubscriptions(member);

        return subscriptions.filter((subscription) => {
            return subscription.status === 'active' || subscription.status === 'trialing';
        });
    }

    async handleCheckoutSessionCompletedWebhook(member, customer) {
        await this._updateCustomer(member, customer);
        if (!customer.subscriptions || !customer.subscriptions.data) {
            return;
        }
        for (const subscription of customer.subscriptions.data) {
            await this._updateSubscription(subscription);
        }
    }

    async handleCustomerSubscriptionDeletedWebhook(subscription) {
        await this._updateSubscription(subscription);
    }

    async handleCustomerSubscriptionUpdatedWebhook(subscription) {
        await this._updateSubscription(subscription);
    }

    async handleInvoicePaymentSucceededWebhook(invoice) {
        const subscription = await retrieve(this._stripe, 'subscriptions', invoice.subscription, {
            expand: ['default_payment_method']
        });
        await this._updateSubscription(subscription);
    }

    async handleInvoicePaymentFailedWebhook(invoice) {
        const subscription = await retrieve(this._stripe, 'subscriptions', invoice.subscription, {
            expand: ['default_payment_method']
        });
        await this._updateSubscription(subscription);
    }

    async _updateCustomer(member, customer) {
        debug(`Attaching customer to member ${member.email} ${customer.id}`);
        await this.storage.set({
            customer: {
                customer_id: customer.id,
                member_id: member.id,
                name: customer.name,
                email: customer.email
            }
        });
    }

    async _updateSubscription(subscription) {
        const payment = subscription.default_payment_method;
        if (typeof payment === 'string') {
            debug(`Fetching default_payment_method for subscription ${subscription.id}`);
            const subscriptionWithPayment = await retrieve(this._stripe, 'subscriptions', subscription.id, {
                expand: ['default_payment_method']
            });
            return this._updateSubscription(subscriptionWithPayment);
        }
        debug(`Attaching subscription to customer ${subscription.customer} ${subscription.id}`);
        await this.storage.set({
            subscription: {
                customer_id: subscription.customer,

                subscription_id: subscription.id,
                status: subscription.status,
                current_period_end: new Date(subscription.current_period_end * 1000),
                start_date: new Date(subscription.start_date * 1000),
                default_payment_card_last4: payment && payment.card && payment.card.last4 || null,

                plan_id: subscription.plan.id,
                plan_nickname: subscription.plan.nickname,
                plan_interval: subscription.plan.interval,
                plan_amount: subscription.plan.amount,
                plan_currency: subscription.plan.currency
            }
        });
    }

    async _customerForMemberCheckoutSession(member) {
        const metadata = await this.storage.get(member);

        for (const data in metadata.customers) {
            try {
                const customer = await this.getCustomer(data.customer_id);
                if (!customer.deleted) {
                    return customer;
                }
            } catch (err) {
                debug(`Ignoring Error getting customer for member ${err.message}`);
            }
        }

        debug(`Creating customer for member ${member.email}`);
        const customer = await create(this._stripe, 'customers', {
            email: member.email
        });

        await this._updateCustomer(member, customer);

        return customer;
    }

    async getCustomer(id, options) {
        return retrieve(this._stripe, 'customers', id, options);
    }
};
<commit_msg>Added priority to webhook secret if present in env (#103)<commit_after>const debug = require('ghost-ignition').debug('stripe');
const {retrieve, list, create, del} = require('./api/stripeRequests');
const api = require('./api');

const STRIPE_API_VERSION = '2019-09-09';

module.exports = class StripePaymentProcessor {
    constructor(config, storage, logging) {
        this.logging = logging;
        this.storage = storage;
        this._ready = new Promise((resolve, reject) => {
            this._resolveReady = resolve;
            this._rejectReady = reject;
        });
        this._configure(config);
    }

    async ready() {
        return this._ready;
    }

    async _configure(config) {
        this._stripe = require('stripe')(config.secretKey);
        this._stripe.setAppInfo(config.appInfo);
        this._stripe.setApiVersion(STRIPE_API_VERSION);
        this._stripe.__TEST_MODE__ = config.secretKey.startsWith('sk_test_');
        this._public_token = config.publicKey;
        this._checkoutSuccessUrl = config.checkoutSuccessUrl;
        this._checkoutCancelUrl = config.checkoutCancelUrl;
        this._webhookHandlerUrl = config.webhookHandlerUrl;

        try {
            this._product = await api.products.ensure(this._stripe, config.product);

            this._plans = [];
            for (const planSpec of config.plans) {
                const plan = await api.plans.ensure(this._stripe, planSpec, this._product);
                this._plans.push(plan);
            }

            const webhooks = await list(this._stripe, 'webhookEndpoints', {
                limit: 100
            });

            const webhookToDelete = webhooks.data.find((webhook) => {
                return webhook.url === this._webhookHandlerUrl;
            });

            if (webhookToDelete) {
                await del(this._stripe, 'webhookEndpoints', webhookToDelete.id);
            }

            try {
                const webhook = await create(this._stripe, 'webhookEndpoints', {
                    url: this._webhookHandlerUrl,
                    api_version: STRIPE_API_VERSION,
                    enabled_events: [
                        'checkout.session.completed',
                        'customer.subscription.deleted',
                        'customer.subscription.updated',
                        'invoice.payment_succeeded',
                        'invoice.payment_failed'
                    ]
                });
                this._webhookSecret = process.env.WEBHOOK_SECRET || webhook.secret;
            } catch (err) {
                this.logging.warn(err);
            }
            debug(`Webhook secret set to ${this._webhookSecret}`);
        } catch (err) {
            debug(`Error configuring ${err.message}`);
            return this._rejectReady(err);
        }

        return this._resolveReady({
            product: this._product,
            plans: this._plans
        });
    }

    async parseWebhook(body, signature) {
        try {
            const event = await this._stripe.webhooks.constructEvent(body, signature, this._webhookSecret);
            debug(`Parsed webhook event: ${event.type}`);
            return event;
        } catch (err) {
            this.logging.error(`Error verifying webhook signature, using secret ${this._webhookSecret}`);
            throw err;
        }
    }

    async createCheckoutSession(member, planName, options) {
        let customer;
        if (member) {
            try {
                customer = await this._customerForMemberCheckoutSession(member);
            } catch (err) {
                debug(`Ignoring Error getting customer for checkout ${err.message}`);
                customer = null;
            }
        } else {
            customer = null;
        }
        const plan = this._plans.find(plan => plan.nickname === planName);
        const session = await this._stripe.checkout.sessions.create({
            payment_method_types: ['card'],
            success_url: options.successUrl || this._checkoutSuccessUrl,
            cancel_url: options.cancelUrl || this._checkoutCancelUrl,
            customer: customer ? customer.id : undefined,
            subscription_data: {
                items: [{
                    plan: plan.id
                }]
            }
        });

        return {
            sessionId: session.id,
            publicKey: this._public_token
        };
    }

    async cancelAllSubscriptions(member) {
        const subscriptions = await this.getSubscriptions(member);

        const activeSubscriptions = subscriptions.filter((subscription) => {
            return subscription.status !== 'cancelled';
        });

        await Promise.all(activeSubscriptions.map((subscription) => {
            return del(this._stripe, 'subscriptions', subscription.id);
        }));

        return true;
    }

    async getSubscriptions(member) {
        const metadata = await this.storage.get(member);

        const customers = metadata.customers.reduce((customers, customer) => {
            return Object.assign(customers, {
                [customer.customer_id]: {
                    id: customer.customer_id,
                    name: customer.name,
                    email: customer.email
                }
            });
        }, {});

        return metadata.subscriptions.map((subscription) => {
            return {
                id: subscription.subscription_id,
                customer: customers[subscription.customer_id],
                plan: {
                    id: subscription.plan_id,
                    nickname: subscription.plan_nickname,
                    interval: subscription.plan_interval,
                    amount: subscription.plan_amount,
                    currency: subscription.plan_currency
                },
                status: subscription.status,
                start_date: subscription.start_date,
                default_payment_card_last4: subscription.default_payment_card_last4,
                current_period_end: subscription.current_period_end
            };
        });
    }

    async getActiveSubscriptions(member) {
        const subscriptions = await this.getSubscriptions(member);

        return subscriptions.filter((subscription) => {
            return subscription.status === 'active' || subscription.status === 'trialing';
        });
    }

    async handleCheckoutSessionCompletedWebhook(member, customer) {
        await this._updateCustomer(member, customer);
        if (!customer.subscriptions || !customer.subscriptions.data) {
            return;
        }
        for (const subscription of customer.subscriptions.data) {
            await this._updateSubscription(subscription);
        }
    }

    async handleCustomerSubscriptionDeletedWebhook(subscription) {
        await this._updateSubscription(subscription);
    }

    async handleCustomerSubscriptionUpdatedWebhook(subscription) {
        await this._updateSubscription(subscription);
    }

    async handleInvoicePaymentSucceededWebhook(invoice) {
        const subscription = await retrieve(this._stripe, 'subscriptions', invoice.subscription, {
            expand: ['default_payment_method']
        });
        await this._updateSubscription(subscription);
    }

    async handleInvoicePaymentFailedWebhook(invoice) {
        const subscription = await retrieve(this._stripe, 'subscriptions', invoice.subscription, {
            expand: ['default_payment_method']
        });
        await this._updateSubscription(subscription);
    }

    async _updateCustomer(member, customer) {
        debug(`Attaching customer to member ${member.email} ${customer.id}`);
        await this.storage.set({
            customer: {
                customer_id: customer.id,
                member_id: member.id,
                name: customer.name,
                email: customer.email
            }
        });
    }

    async _updateSubscription(subscription) {
        const payment = subscription.default_payment_method;
        if (typeof payment === 'string') {
            debug(`Fetching default_payment_method for subscription ${subscription.id}`);
            const subscriptionWithPayment = await retrieve(this._stripe, 'subscriptions', subscription.id, {
                expand: ['default_payment_method']
            });
            return this._updateSubscription(subscriptionWithPayment);
        }
        debug(`Attaching subscription to customer ${subscription.customer} ${subscription.id}`);
        await this.storage.set({
            subscription: {
                customer_id: subscription.customer,

                subscription_id: subscription.id,
                status: subscription.status,
                current_period_end: new Date(subscription.current_period_end * 1000),
                start_date: new Date(subscription.start_date * 1000),
                default_payment_card_last4: payment && payment.card && payment.card.last4 || null,

                plan_id: subscription.plan.id,
                plan_nickname: subscription.plan.nickname,
                plan_interval: subscription.plan.interval,
                plan_amount: subscription.plan.amount,
                plan_currency: subscription.plan.currency
            }
        });
    }

    async _customerForMemberCheckoutSession(member) {
        const metadata = await this.storage.get(member);

        for (const data in metadata.customers) {
            try {
                const customer = await this.getCustomer(data.customer_id);
                if (!customer.deleted) {
                    return customer;
                }
            } catch (err) {
                debug(`Ignoring Error getting customer for member ${err.message}`);
            }
        }

        debug(`Creating customer for member ${member.email}`);
        const customer = await create(this._stripe, 'customers', {
            email: member.email
        });

        await this._updateCustomer(member, customer);

        return customer;
    }

    async getCustomer(id, options) {
        return retrieve(this._stripe, 'customers', id, options);
    }
};
<|endoftext|>