<commit_before>/**
 * @license
 * Copyright 2014 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.
 */

/*
 TODO:
   Use MementoMgr.
   destroy() views when removed.
   Switch to use flexbox instead of
.
   Browser history support.
*/
MODEL({
  name:  'StackView',
  extendsModel: 'View',

  properties: [
    {
      name:  'stack',
      factory: function() { return []; }
    },
    {
      name:  'redo',
      factory: function() { return []; }
    },
    {
      name: 'className',
      defaultValue: 'stackView'
    },
    {
      name: 'tagName',
      defaultValue: 'div'
    },
    {
      model_: 'BooleanProperty',
      name: 'sliderOpen'
    }
  ],

  actions: [
    {
      name:  'back',
      label: '<',
      help:  'Go to previous view',

      isEnabled: function() { return this.stack.length > 1 || this.sliderOpen; },
      action: function() {
        if ( this.sliderOpen ) {
          this.sliderOpen = false;
          this.dimmer$().style.zIndex = -1;
          this.dimmer$().style.opacity = -1;
          this.slideArea$().style.transition = 'left 0.2s cubic-bezier(0.4, 0.0, 1, 1)';
          this.slideArea$().style.left = '-304px';
          setTimeout(function() {
            this.slideArea$().style.transition = '';
            this.slideArea$().innerHTML = '';
          }.bind(this), 300);
        } else {
          this.redo.push(this.stack.pop());
          this.pushView(this.stack.pop(), undefined, true, 'fromLeft');
          this.propertyChange('stack', this.stack, this.stack);
        }
      }
    },
    {
      name:  'forth',
      label: '>',
      help:  'Undo the previous back.',

      action: function() {
        this.pushView(this.redo.pop());
        this.propertyChange('stack', this.stack, this.stack);
      }
    }
  ],

  methods: {
    dimmer$:      function() { return this.$.querySelector('.stackview-dimmer'); },
    navBar$:      function() { return this.$.querySelector('.stackview_navbar'); },
    navActions$:  function() { return this.$.querySelector('.stackview_navactions'); },
    slideArea$:   function() { return this.$.querySelector('.stackview-slidearea'); },
    viewArea$:    function() { return this.$.querySelector('.stackview-viewarea'); },
    previewArea$: function() { return this.$.querySelector('.stackview-previewarea'); },

    initHTML: function() {
      this.SUPER();

      this.dimmer$().addEventListener('click', this.back.bind(this));
    },

    setTopView: function(view, opt_label) {
      this.stack = [];
      this.pushView(view, undefined, undefined, 'none');
    },

    updateNavBar: function() {
      var buf = [];

      for ( var i = 0 ; i < this.stack.length ; i++ ) {
        var view = this.stack[i];

        if ( buf.length != 0 ) buf.push(' > ');
        buf.push(view.stackLabel);
      }

      this.navBar$().innerHTML = buf.join('');
    },

    slideView: function (view, opt_label) {
      this.sliderOpen = true;
      this.redo.length = 0;
      this.setPreview(null);
      // view.stackLabel = opt_label || view.stackLabel || view.label;
      // this.stack.push(view);
      this.slideArea$().style.left = -2000;
      var s = this.X.window.getComputedStyle(this.slideArea$());
      this.slideArea$().innerHTML = view.toHTML();
      view.initHTML();
      this.slideArea$().style.transition = '';
      this.slideArea$().style.left = -toNum(s.width);

      setTimeout(function() {
        this.dimmer$().style.zIndex = 3;
        this.dimmer$().style.opacity = 0.4;
        this.slideArea$().style.transition = 'left 0.2s cubic-bezier(0.0, 0.0, 0.2, 1)';
        this.slideArea$().style.left = '0';
        // view.stackView = this;
        // this.propertyChange('stack', this.stack, this.stack);
      }.bind(this), 10);
    },

    pushView: function (view, opt_label, opt_back, opt_transition) {
      if ( this.staleView_ ) try { this.staleView_.remove(); } catch (x) { }

      var transition = opt_transition || 'fromRight';

      if ( ! opt_back ) this.redo.length = 0;
      this.setPreview(null);
      view.stackLabel = opt_label || view.stackLabel || view.label;
      this.stack.push(view);
      if ( transition === 'fromRight' )
        this.installViewFromRight_(view);
      else if ( transition === 'fromLeft' )
        this.installViewFromLeft_(view);
      else
        this.installView_(view);
      this.updateNavBar();
      view.stackView = this;
      view.initHTML();
      this.propertyChange('stack', this.stack, this.stack);
    },

    installView_: function(view) {
      this.viewArea$().innerHTML = view.toHTML();
    },

    installViewFromRight_: function(view) {
      var oldViewArea = this.viewArea$();

      var id = this.nextID();
      var width = toNum(this.X.window.getComputedStyle(oldViewArea).width);
      var newViewHTML = '
' + view.toHTML() + '
';
      oldViewArea.style.display = 'table';
      oldViewArea.style.position = 'absolute';
      oldViewArea.style.left = 0;
      oldViewArea.style.transition = 'left .3s';
      oldViewArea.insertAdjacentHTML('afterend', newViewHTML);

      var newViewArea = this.X.$(id);

      this.X.setTimeout(function() {
        oldViewArea.style.left = -width;
        newViewArea.style.left = 0;
      }.bind(this), 15);

      this.staleView_ = oldViewArea;
      this.X.setTimeout(function() {
        newViewArea.style.position = '';
        newViewArea.style.left = '';
        oldViewArea.remove();
      }, 500);
    },

    installViewFromLeft_: function(view) {
      var oldViewArea = this.viewArea$();

      var id = this.nextID();
      var width = toNum(this.X.window.getComputedStyle(oldViewArea).width);
      var newViewHTML = '
' + view.toHTML() + '
';
      oldViewArea.style.display = 'table';
      oldViewArea.style.position = 'absolute';
      oldViewArea.style.right = 0;
      oldViewArea.style.transition = 'right .3s';
      oldViewArea.insertAdjacentHTML('afterend', newViewHTML);

      var newViewArea = this.X.$(id);

      this.X.setTimeout(function() {
        oldViewArea.style.right = -width;
        newViewArea.style.right = 0;
      }.bind(this), 15);

      this.staleView_ = oldViewArea;
      this.X.setTimeout(function() {
        newViewArea.style.position = '';
        newViewArea.style.right = '';
        oldViewArea.remove();
      }, 500);
    },

    setPreview: function(view) {
      if ( ! view ) {
        this.viewArea$().parentNode.width = '100%';
        this.previewArea$().innerHTML = '';
        return;
      }

      this.viewArea$().parentNode.width = '65%';
      this.previewArea$().innerHTML = view.toHTML();
      view.initHTML();
    }
  },

  templates: [
    function toInnerHTML() {/*

$$back $$forth

    */}
  ]
});
<commit_msg>Add isEnabled to StackView.back<commit_after>/**
 * @license
 * Copyright 2014 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.
 */

/*
 TODO:
   Use MementoMgr.
   destroy() views when removed.
   Switch to use flexbox instead of
.
   Browser history support.
*/
MODEL({
  name:  'StackView',
  extendsModel: 'View',

  properties: [
    {
      name:  'stack',
      factory: function() { return []; }
    },
    {
      name:  'redo',
      factory: function() { return []; }
    },
    {
      name: 'className',
      defaultValue: 'stackView'
    },
    {
      name: 'tagName',
      defaultValue: 'div'
    },
    {
      model_: 'BooleanProperty',
      name: 'sliderOpen'
    }
  ],

  actions: [
    {
      name:  'back',
      label: '<',
      help:  'Go to previous view',

      isEnabled: function() { return this.stack.length > 1 || this.sliderOpen; },
      action: function() {
        if ( this.sliderOpen ) {
          this.sliderOpen = false;
          this.dimmer$().style.zIndex = -1;
          this.dimmer$().style.opacity = -1;
          this.slideArea$().style.transition = 'left 0.2s cubic-bezier(0.4, 0.0, 1, 1)';
          this.slideArea$().style.left = '-304px';
          setTimeout(function() {
            this.slideArea$().style.transition = '';
            this.slideArea$().innerHTML = '';
          }.bind(this), 300);
        } else {
          this.redo.push(this.stack.pop());
          this.pushView(this.stack.pop(), undefined, true, 'fromLeft');
          this.propertyChange('stack', this.stack, this.stack);
        }
      }
    },
    {
      name:  'forth',
      label: '>',
      help:  'Undo the previous back.',
      isEnabled: function() { return this.redo.length > 0; },
      action: function() {
        this.pushView(this.redo.pop());
        this.propertyChange('stack', this.stack, this.stack);
      }
    }
  ],

  methods: {
    dimmer$:      function() { return this.$.querySelector('.stackview-dimmer'); },
    navBar$:      function() { return this.$.querySelector('.stackview_navbar'); },
    navActions$:  function() { return this.$.querySelector('.stackview_navactions'); },
    slideArea$:   function() { return this.$.querySelector('.stackview-slidearea'); },
    viewArea$:    function() { return this.$.querySelector('.stackview-viewarea'); },
    previewArea$: function() { return this.$.querySelector('.stackview-previewarea'); },

    initHTML: function() {
      this.SUPER();

      this.dimmer$().addEventListener('click', this.back.bind(this));
    },

    setTopView: function(view, opt_label) {
      this.stack = [];
      this.pushView(view, undefined, undefined, 'none');
    },

    updateNavBar: function() {
      var buf = [];

      for ( var i = 0 ; i < this.stack.length ; i++ ) {
        var view = this.stack[i];

        if ( buf.length != 0 ) buf.push(' > ');
        buf.push(view.stackLabel);
      }

      this.navBar$().innerHTML = buf.join('');
    },

    slideView: function (view, opt_label) {
      this.sliderOpen = true;
      this.redo.length = 0;
      this.setPreview(null);
      // view.stackLabel = opt_label || view.stackLabel || view.label;
      // this.stack.push(view);
      this.slideArea$().style.left = -2000;
      var s = this.X.window.getComputedStyle(this.slideArea$());
      this.slideArea$().innerHTML = view.toHTML();
      view.initHTML();
      this.slideArea$().style.transition = '';
      this.slideArea$().style.left = -toNum(s.width);

      setTimeout(function() {
        this.dimmer$().style.zIndex = 3;
        this.dimmer$().style.opacity = 0.4;
        this.slideArea$().style.transition = 'left 0.2s cubic-bezier(0.0, 0.0, 0.2, 1)';
        this.slideArea$().style.left = '0';
        // view.stackView = this;
        // this.propertyChange('stack', this.stack, this.stack);
      }.bind(this), 10);
    },

    pushView: function (view, opt_label, opt_back, opt_transition) {
      if ( this.staleView_ ) try { this.staleView_.remove(); } catch (x) { }

      var transition = opt_transition || 'fromRight';

      if ( ! opt_back ) this.redo.length = 0;
      this.setPreview(null);
      view.stackLabel = opt_label || view.stackLabel || view.label;
      this.stack.push(view);
      if ( transition === 'fromRight' )
        this.installViewFromRight_(view);
      else if ( transition === 'fromLeft' )
        this.installViewFromLeft_(view);
      else
        this.installView_(view);
      this.updateNavBar();
      view.stackView = this;
      view.initHTML();
      this.propertyChange('stack', this.stack, this.stack);
    },

    installView_: function(view) {
      this.viewArea$().innerHTML = view.toHTML();
    },

    installViewFromRight_: function(view) {
      var oldViewArea = this.viewArea$();

      var id = this.nextID();
      var width = toNum(this.X.window.getComputedStyle(oldViewArea).width);
      var newViewHTML = '
' + view.toHTML() + '
';
      oldViewArea.style.display = 'table';
      oldViewArea.style.position = 'absolute';
      oldViewArea.style.left = 0;
      oldViewArea.style.transition = 'left .3s';
      oldViewArea.insertAdjacentHTML('afterend', newViewHTML);

      var newViewArea = this.X.$(id);

      this.X.setTimeout(function() {
        oldViewArea.style.left = -width;
        newViewArea.style.left = 0;
      }.bind(this), 15);

      this.staleView_ = oldViewArea;
      this.X.setTimeout(function() {
        newViewArea.style.position = '';
        newViewArea.style.left = '';
        oldViewArea.remove();
      }, 500);
    },

    installViewFromLeft_: function(view) {
      var oldViewArea = this.viewArea$();

      var id = this.nextID();
      var width = toNum(this.X.window.getComputedStyle(oldViewArea).width);
      var newViewHTML = '
' + view.toHTML() + '
';
      oldViewArea.style.display = 'table';
      oldViewArea.style.position = 'absolute';
      oldViewArea.style.right = 0;
      oldViewArea.style.transition = 'right .3s';
      oldViewArea.insertAdjacentHTML('afterend', newViewHTML);

      var newViewArea = this.X.$(id);

      this.X.setTimeout(function() {
        oldViewArea.style.right = -width;
        newViewArea.style.right = 0;
      }.bind(this), 15);

      this.staleView_ = oldViewArea;
      this.X.setTimeout(function() {
        newViewArea.style.position = '';
        newViewArea.style.right = '';
        oldViewArea.remove();
      }, 500);
    },

    setPreview: function(view) {
      if ( ! view ) {
        this.viewArea$().parentNode.width = '100%';
        this.previewArea$().innerHTML = '';
        return;
      }

      this.viewArea$().parentNode.width = '65%';
      this.previewArea$().innerHTML = view.toHTML();
      view.initHTML();
    }
  },

  templates: [
    function toInnerHTML() {/*

$$back $$forth

    */}
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 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.
 */

/*

  onResize:
    resize scrollbar
    repaint

  onDAOUpdate
    reCount size
    repaint

*/
var TableView2 = FOAM({
  model_: 'Model',

  extendsModel: 'AbstractView',

  name: 'TableView2',
  label: 'Table View',

  properties: [
    {
      name:  'model',
      type:  'Model'
    },
    {
      model_: StringArrayProperty,
      name:  'properties',
      preSet: function(a) { return ! a || a.length == 0 ? null : a; },
      postSet: function() { this.repaint(); },
      defaultValue: null
    },
    {
      name:  'hardSelection',
      type:  'Value',
      valueFactory: function() { return SimpleValue.create(); }
    },
    {
      name:  'selection',
      type:  'Value',
      valueFactory: function() { return SimpleValue.create(); }
    },
    {
      name:  'children',
      type:  'Array[View]',
      valueFactory: function() { return []; }
    },
    {
      name:  'sortOrder',
      type:  'Comparator',
      postSet: function() { this.repaint(); },
      defaultValue: undefined
    },
    {
      name:  'dao',
      label: 'DAO',
      type: 'DAO',
      required: true,
      hidden: true,
      postSet: function(oldValue, newValue) {
        if ( this.daoListener ) {
          if ( oldValue ) oldValue.unlisten(this.daoListener);
          if ( newValue ) newValue.listen(this.daoListener);
        }
        this.scrollbar.value = 0;
        this.onDAOUpdate();
      }
    },
    {
      name: 'rows',
      type:  'Integer',
      defaultValue: 50,
      postSet: function(oldValue, newValue) {
        if ( oldValue !== newValue ) this.repaint();
      }
    },
    {
      model_: 'IntegerProperty',
      name: 'height'
    },
    {
      model_: 'BooleanProperty',
      name: 'scrollEnabled',
      defaultValue: false
    },
    {
      model_: 'BooleanProperty',
      name: 'editColumnsEnabled',
      defaultValue: false
    },
    {
      name: 'scrollbar',
      type: 'ScrollCView',
      valueFactory: function() {
        var sb = ScrollCView.create({height:800, width: 24, x: 1, y: 0, size: 200, extent: 10});

//        if ( this.dao ) this.dao.select(COUNT())(function(c) { sb.size = c.count; });

        sb.value$.addListener(this.repaint);

        return sb;
      }
    }
  ],

  listeners: [
    {
      model_: 'Method',

      name: 'onResize',
      isMerged: 200,
      code: function() {
        if ( ! this.$ ) return;

        var h = this.$.parentElement.offsetHeight;
        var rows = Math.ceil((h - 47)/20)+1;
        // TODO: update the extent somehow
//        this.scrollbar.extent = rows;
        this.rows = rows;
        this.scrollbar.height = h-1;
        this.scrollbar.paint();
      }
    },

    {
      model_: 'Method',

      name: 'onDAOUpdate',
      isAnimated: true,
      code: function() {
        this.dao.select(COUNT())(function(c) {
          this.scrollbar.size = c.count;
          this.repaint();
        }.bind(this));
      }
    },

    {
      model_: 'Method',

      name: 'repaint',
      isAnimated: true,
      code: function() {
        this.repaintNow();
      }
    },
    {
      model_: 'Method',

      name: 'onEditColumns',
      code: function(evt) {
        var v = EditColumnsView.create({
          model:               this.model,
          properties:          this.properties || this.model.tableProperties,
          availableProperties: this.model.properties
        });

        v.addPropertyListener('properties', function() {
          v.close();
          this.properties = v.properties;
          v.removePropertyListener('properties', arguments.callee);
          this.repaint();
        }.bind(this));

        this.$.insertAdjacentHTML('beforebegin', v.toHTML());
        v.initHTML();
      }
    }
  ],

  methods: {

    // Not actually a method, but still works
    // TODO: add 'Constants' to Model
    DOUBLE_CLICK: "double-click", // event topic

    toHTML: function() {
      return '
' +
        '' +
        this.tableToHTML() +
        '' +
        '' +
        this.scrollbar.toHTML() +
        '' +
        '
';
    },

    initHTML: function() {
      this.scrollbar.initHTML();

      this.onDAOUpdate();

      this.daoListener = {
        put:    this.onDAOUpdate,
        remove: this.onDAOUpdate
      };

      if ( this.scrollEnabled ) {
        (this.window || window).addEventListener('resize', this.onResize, false);

        var sb = this.scrollbar;

        this.$.parentElement.onmousewheel = function(e) {
          if ( e.wheelDeltaY > 0 && sb.value ) {
            sb.value--;
          } else if ( e.wheelDeltaY < 0 && sb.value < sb.size - sb.extent ) {
            sb.value++;
          }
        };

        this.onResize();
      }

      this.dao.listen(this.daoListener);
    },

    /** Call repaint() instead to repaint on next animation frame. **/
    repaintNow: function() {
      var dao = this.dao;

      if ( ! dao || ! this.$ ) return;

      dao = dao.skip(this.scrollbar.value);

      var self = this;
      var objs = [];
      var selection = this.selection && this.selection.get();
      if ( this.sortOrder ) dao = dao.orderBy(this.sortOrder);

      dao.limit(this.rows).select({
        put: function(o) { if ( ! selection || ( self.selection && o === self.selection.get() ) ) selection = o; objs.push(o); }} )(function() {
          self.objs = objs;
          if ( self.$ ) {
            console.time('TableView2.paint');
            self.$.innerHTML = self.tableToHTML();
            console.timeEnd('TableView2.paint');
//            console.time('TableView2.initHTML');
            self.initHTML_();
//            console.timeEnd('TableView2.initHTML');
          }
//          self.onResize();
          // self.selection && self.selection.set(selection);
        });
    },

    tableToHTML: function() {
      var model = this.model;

      if ( this.callbacks_ ) {
        // console.log('Warning: TableView.tableToHTML called twice without initHTML');
        delete this['callbacks_'];
        this.children = [];
      }

      var str = [];
      var props = [];

      str.push('
');

      //str += '


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 * @fullSyntaxTransform
 */

'use strict';

const RelayQLPrinter = require('./RelayQLPrinter');

const invariant = require('./invariant');
const util = require('util');

const {
  RelayQLDefinition,
  RelayQLFragment,
  RelayQLMutation,
  RelayQLQuery,
  RelayQLSubscription,
} = require('./RelayQLAST');
const {
  formatError,
  parse,
  Source,
  validate,
} = require('graphql');

const GraphQLWrapper = {
  error: require('graphql/error'),
  language: require('graphql/language'),
  language_parser: require('graphql/language/parser'),
  language_source: require('graphql/language/source'),
  type: require('graphql/type'),
  type_definition: require('graphql/type/definition'),
  type_directives: require('graphql/type/directives'),
  type_introspection: require('graphql/type/introspection'),
  type_scalars: require('graphql/type/scalars'),
  utilities: require('graphql/utilities'),
  utilities_buildClientSchema: require('graphql/utilities/buildClientSchema'),
  utilities_buildASTSchema: require('graphql/utilities/buildASTSchema'),
  validation: require('graphql/validation'),
  validation_rules_KnownFragmentNames: require('graphql/validation/rules/KnownFragmentNames'),
  validation_rules_NoUndefinedVariables: require('graphql/validation/rules/NoUndefinedVariables'),
  validation_rules_NoUnusedFragments: require('graphql/validation/rules/NoUnusedFragments'),
  validation_rules_ScalarLeafs: require('graphql/validation/rules/ScalarLeafs'),
  validation_validate: require('graphql/validation/validate'),
};

import type {Printable, Substitution} from './RelayQLPrinter';
import type {
  DocumentNode,
  GraphQLError,
  GraphQLFormattedError,
  GraphQLSchema,
} from 'graphql';

type TemplateLiteral = {
  type: 'TemplateElement',
  quasis: Array<TemplateElement>,
  expressions: Array<Printable>,
  range: [number, number],
  loc: Object,
};
type TemplateElement = {
  type: 'TemplateElement',
  value: {
    raw: string,
    cooked: string,
  },
  tail: boolean,
  range: [number, number],
  loc: Object,
};
export type Validator<T> = (GraphQL: any) => ({
  validate: (schema: GraphQLSchema, ast: T) => Array<GraphQLError>,
});

type TransformerOptions = {
  inputArgumentName: ?string,
  snakeCase: boolean,
  substituteVariables: boolean,
  validator: ?Validator<any>,
};
type TextTransformOptions = {
  documentName: string,
  enableValidation: boolean,
  propName: ?string,
  tagName: string,
};

/**
 * Transforms a TemplateLiteral node into a RelayQLDefinition, which is then
 * transformed into a Babel AST via RelayQLPrinter.
 */
class RelayQLTransformer {
  schema: GraphQLSchema;
  options: TransformerOptions;

  constructor(schema: GraphQLSchema, options: TransformerOptions) {
    this.schema = schema;
    this.options = options;
  }

  transform(
    t: any, // Babel
    node: TemplateLiteral,
    options: TextTransformOptions
  ): Printable {
    const {
      substitutions,
      templateText,
      variableNames,
    } = this.processTemplateLiteral(node, options.documentName);
    const documentText = this.processTemplateText(templateText, options);
    const definition = this.processDocumentText(documentText, options);

    const Printer = RelayQLPrinter(t, this.options);
    return new Printer(options.tagName, variableNames)
      .print(definition, substitutions, options.enableValidation);
  }

  /**
   * Convert TemplateLiteral into a single template string with substitution
   * names, a matching array of substituted values, and a set of substituted
   * variable names.
   */
  processTemplateLiteral(
    node: TemplateLiteral,
    documentName: string
  ): {
    substitutions: Array<Substitution>,
    templateText: string,
    variableNames: {[variableName: string]: void},
  } {
    const chunks = [];
    const variableNames = {};
    const substitutions = [];
    node.quasis.forEach((element, ii) => {
      const chunk = element.value.cooked;
      chunks.push(chunk);
      if (!element.tail) {
        const name = 'RQL_' + ii;
        const value = node.expressions[ii];
        substitutions.push({name, value});
        if (/:\s*$/.test(chunk)) {
          invariant(
            this.options.substituteVariables,
            'You supplied a GraphQL document named `%s` that uses template ' +
            'substitution for an argument value, but variable substitution ' +
            'has not been enabled.',
            documentName
          );
          chunks.push('$' + name);
          variableNames[name] = undefined;
        } else {
          chunks.push('...' + name);
        }
      }
    });
    return {substitutions, templateText: chunks.join('').trim(), variableNames};
  }

  /**
   * Converts the template string into a valid GraphQL document string.
   */
  processTemplateText(
    templateText: string,
    {documentName, propName}: TextTransformOptions
  ): string {
    const pattern = /^(fragment|mutation|query|subscription)\s*(\w*)?([\s\S]*)/;
    const matches = pattern.exec(templateText);
    invariant(
      matches,
      'You supplied a GraphQL document named `%s` with invalid syntax. It ' +
      'must start with `fragment`, `mutation`, `query`, or `subscription`.',
      documentName
    );
    const type = matches[1];
    let name = matches[2] || documentName;
    let rest = matches[3];
    // Allow `fragment on Type {...}`.
    if (type === 'fragment' && name === 'on') {
      name = documentName +
        (propName ? '_' + capitalize(propName) : '') +
        'RelayQL';
      rest = 'on' + rest;
    }
    const definitionName = capitalize(name);
    return type + ' ' + definitionName + ' ' + rest;
  }

  /**
   * Parses the GraphQL document string into a RelayQLDocument.
   */
  processDocumentText(
    documentText: string,
    {documentName, enableValidation}: TextTransformOptions
  ): RelayQLDefinition<any> {
    const document = parse(new Source(documentText, documentName));
    const validationErrors = enableValidation ?
      this.validateDocument(document, documentName) :
      null;
    if (validationErrors) {
      const error = new Error(util.format(
        'You supplied a GraphQL document named `%s` with validation errors.',
        documentName
      ));
      (error: any).validationErrors = validationErrors;
      (error: any).sourceText = documentText;
      throw error;
    }
    const definition = document.definitions[0];

    const context = {
      definitionName: capitalize(documentName),
      isPattern: false,
      generateID: createIDGenerator(),
      schema: this.schema,
    };
    if (definition.kind === 'FragmentDefinition') {
      return new RelayQLFragment(context, definition);
    } else if (definition.kind === 'OperationDefinition') {
      if (definition.operation === 'mutation') {
        return new RelayQLMutation(context, definition);
      } else if (definition.operation === 'query') {
        return new RelayQLQuery(context, definition);
      } else if (definition.operation === 'subscription') {
        return new RelayQLSubscription(context, definition);
      } else {
        invariant(false, 'Unsupported operation: %s', definition.operation);
      }
    } else {
      invariant(false, 'Unsupported definition kind: %s', definition.kind);
    }
  }

  validateDocument(
    document: DocumentNode,
    documentName: string,
  ): ?Array<GraphQLFormattedError> {
    invariant(
      document.definitions.length === 1,
      'You supplied a GraphQL document named `%s` with %d definitions, but ' +
      'it must have exactly one definition.',
      documentName,
      document.definitions.length
    );
    const definition = document.definitions[0];
    const isMutation =
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'mutation';

    const validator = this.options.validator;
    let validationErrors;
    if (validator) {
      validationErrors = validator(GraphQLWrapper).validate(this.schema, document);
    } else {
      const rules = [
        require(
          'graphql/validation/rules/ArgumentsOfCorrectType'
        ).ArgumentsOfCorrectType,
        require(
          'graphql/validation/rules/DefaultValuesOfCorrectType'
        ).DefaultValuesOfCorrectType,
        require(
          'graphql/validation/rules/FieldsOnCorrectType'
        ).FieldsOnCorrectType,
        require(
          'graphql/validation/rules/FragmentsOnCompositeTypes'
        ).FragmentsOnCompositeTypes,
        require(
          'graphql/validation/rules/KnownArgumentNames'
        ).KnownArgumentNames,
        require(
          'graphql/validation/rules/KnownTypeNames'
        ).KnownTypeNames,
        require(
          'graphql/validation/rules/PossibleFragmentSpreads'
        ).PossibleFragmentSpreads,
        require(
          'graphql/validation/rules/VariablesInAllowedPosition'
        ).VariablesInAllowedPosition,
      ];
      if (!isMutation) {
        rules.push(
          require(
            'graphql/validation/rules/ProvidedNonNullArguments'
          ).ProvidedNonNullArguments
        );
      }
      validationErrors = validate(this.schema, document, rules);
    }

    if (validationErrors && validationErrors.length > 0) {
      return validationErrors.map(formatError);
    }
    return null;
  }

}

function capitalize(string: string): string {
  return string[0].toUpperCase() + string.slice(1);
}

/**
 * Utility to generate locally scoped auto-incrementing IDs.
 */
function createIDGenerator(): () => string {
  let _id = 0;
  return () => (_id++).toString(32);
}

module.exports = RelayQLTransformer;
<commit_msg>Remove GraphQLWrapper<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @flow
 * @fullSyntaxTransform
 */

'use strict';

const RelayQLPrinter = require('./RelayQLPrinter');

const invariant = require('./invariant');
const util = require('util');

const {
  RelayQLDefinition,
  RelayQLFragment,
  RelayQLMutation,
  RelayQLQuery,
  RelayQLSubscription,
} = require('./RelayQLAST');
const {
  formatError,
  parse,
  Source,
  validate,
  ArgumentsOfCorrectTypeRule,
  DefaultValuesOfCorrectTypeRule,
  FieldsOnCorrectTypeRule,
  FragmentsOnCompositeTypesRule,
  KnownArgumentNamesRule,
  KnownTypeNamesRule,
  PossibleFragmentSpreadsRule,
  VariablesInAllowedPositionRule,
  ProvidedNonNullArgumentsRule,
} = require('graphql');

import type {Printable, Substitution} from './RelayQLPrinter';
import type {
  DocumentNode,
  GraphQLError,
  GraphQLFormattedError,
  GraphQLSchema,
} from 'graphql';

type TemplateLiteral = {
  type: 'TemplateElement',
  quasis: Array<TemplateElement>,
  expressions: Array<Printable>,
  range: [number, number],
  loc: Object,
};
type TemplateElement = {
  type: 'TemplateElement',
  value: {
    raw: string,
    cooked: string,
  },
  tail: boolean,
  range: [number, number],
  loc: Object,
};
export type Validator<T> = () => ({
  validate: (schema: GraphQLSchema, ast: T) => Array<GraphQLError>,
});

type TransformerOptions = {
  inputArgumentName: ?string,
  snakeCase: boolean,
  substituteVariables: boolean,
  validator: ?Validator<any>,
};
type TextTransformOptions = {
  documentName: string,
  enableValidation: boolean,
  propName: ?string,
  tagName: string,
};

/**
 * Transforms a TemplateLiteral node into a RelayQLDefinition, which is then
 * transformed into a Babel AST via RelayQLPrinter.
 */
class RelayQLTransformer {
  schema: GraphQLSchema;
  options: TransformerOptions;

  constructor(schema: GraphQLSchema, options: TransformerOptions) {
    this.schema = schema;
    this.options = options;
  }

  transform(
    t: any, // Babel
    node: TemplateLiteral,
    options: TextTransformOptions
  ): Printable {
    const {
      substitutions,
      templateText,
      variableNames,
    } = this.processTemplateLiteral(node, options.documentName);
    const documentText = this.processTemplateText(templateText, options);
    const definition = this.processDocumentText(documentText, options);

    const Printer = RelayQLPrinter(t, this.options);
    return new Printer(options.tagName, variableNames)
      .print(definition, substitutions, options.enableValidation);
  }

  /**
   * Convert TemplateLiteral into a single template string with substitution
   * names, a matching array of substituted values, and a set of substituted
   * variable names.
   */
  processTemplateLiteral(
    node: TemplateLiteral,
    documentName: string
  ): {
    substitutions: Array<Substitution>,
    templateText: string,
    variableNames: {[variableName: string]: void},
  } {
    const chunks = [];
    const variableNames = {};
    const substitutions = [];
    node.quasis.forEach((element, ii) => {
      const chunk = element.value.cooked;
      chunks.push(chunk);
      if (!element.tail) {
        const name = 'RQL_' + ii;
        const value = node.expressions[ii];
        substitutions.push({name, value});
        if (/:\s*$/.test(chunk)) {
          invariant(
            this.options.substituteVariables,
            'You supplied a GraphQL document named `%s` that uses template ' +
            'substitution for an argument value, but variable substitution ' +
            'has not been enabled.',
            documentName
          );
          chunks.push('$' + name);
          variableNames[name] = undefined;
        } else {
          chunks.push('...' + name);
        }
      }
    });
    return {substitutions, templateText: chunks.join('').trim(), variableNames};
  }

  /**
   * Converts the template string into a valid GraphQL document string.
   */
  processTemplateText(
    templateText: string,
    {documentName, propName}: TextTransformOptions
  ): string {
    const pattern = /^(fragment|mutation|query|subscription)\s*(\w*)?([\s\S]*)/;
    const matches = pattern.exec(templateText);
    invariant(
      matches,
      'You supplied a GraphQL document named `%s` with invalid syntax. It ' +
      'must start with `fragment`, `mutation`, `query`, or `subscription`.',
      documentName
    );
    const type = matches[1];
    let name = matches[2] || documentName;
    let rest = matches[3];
    // Allow `fragment on Type {...}`.
    if (type === 'fragment' && name === 'on') {
      name = documentName +
        (propName ? '_' + capitalize(propName) : '') +
        'RelayQL';
      rest = 'on' + rest;
    }
    const definitionName = capitalize(name);
    return type + ' ' + definitionName + ' ' + rest;
  }

  /**
   * Parses the GraphQL document string into a RelayQLDocument.
   */
  processDocumentText(
    documentText: string,
    {documentName, enableValidation}: TextTransformOptions
  ): RelayQLDefinition<any> {
    const document = parse(new Source(documentText, documentName));
    const validationErrors = enableValidation ?
      this.validateDocument(document, documentName) :
      null;
    if (validationErrors) {
      const error = new Error(util.format(
        'You supplied a GraphQL document named `%s` with validation errors.',
        documentName
      ));
      (error: any).validationErrors = validationErrors;
      (error: any).sourceText = documentText;
      throw error;
    }
    const definition = document.definitions[0];

    const context = {
      definitionName: capitalize(documentName),
      isPattern: false,
      generateID: createIDGenerator(),
      schema: this.schema,
    };
    if (definition.kind === 'FragmentDefinition') {
      return new RelayQLFragment(context, definition);
    } else if (definition.kind === 'OperationDefinition') {
      if (definition.operation === 'mutation') {
        return new RelayQLMutation(context, definition);
      } else if (definition.operation === 'query') {
        return new RelayQLQuery(context, definition);
      } else if (definition.operation === 'subscription') {
        return new RelayQLSubscription(context, definition);
      } else {
        invariant(false, 'Unsupported operation: %s', definition.operation);
      }
    } else {
      invariant(false, 'Unsupported definition kind: %s', definition.kind);
    }
  }

  validateDocument(
    document: DocumentNode,
    documentName: string,
  ): ?Array<GraphQLFormattedError> {
    invariant(
      document.definitions.length === 1,
      'You supplied a GraphQL document named `%s` with %d definitions, but ' +
      'it must have exactly one definition.',
      documentName,
      document.definitions.length
    );
    const definition = document.definitions[0];
    const isMutation =
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'mutation';

    const validator = this.options.validator;
    let validationErrors;
    if (validator) {
      validationErrors = validator().validate(this.schema, document);
    } else {
      const rules = [
        ArgumentsOfCorrectTypeRule,
        DefaultValuesOfCorrectTypeRule,
        FieldsOnCorrectTypeRule,
        FragmentsOnCompositeTypesRule,
        KnownArgumentNamesRule,
        KnownTypeNamesRule,
        PossibleFragmentSpreadsRule,
        VariablesInAllowedPositionRule,
      ];
      if (!isMutation) {
        rules.push(ProvidedNonNullArgumentsRule);
      }
      validationErrors = validate(this.schema, document, rules);
    }

    if (validationErrors && validationErrors.length > 0) {
      return validationErrors.map(formatError);
    }
    return null;
  }

}

function capitalize(string: string): string {
  return string[0].toUpperCase() + string.slice(1);
}

/**
 * Utility to generate locally scoped auto-incrementing IDs.
 */
function createIDGenerator(): () => string {
  let _id = 0;
  return () => (_id++).toString(32);
}

module.exports = RelayQLTransformer;
<|endoftext|>


<commit_before>/* By Arjan Eising, 2011. Licence: MIT. See <https://github.com/arjaneising/TypeSequence> */
;(function($){$.fn.typesequence=function(options){var elms=this,executed=false,codedSequence=[],goingWell=0,defaults={},keycodes={'BACKSPACE':8,'TAB':9,'ENTER':13,'↵':13,'SHIFT':16,'⇧':16,'CONTROL':17,'ALT':18,'CAPSLOCK':20,'ESC':27,'SPACE':32,' ':32,'PAGEUP':33,'PAGEDOWN':34,'END':35,'HOME':36,'LEFT':37,'←':37,'⇐':37,'UP':38,'↑':38,'⇑':38,'RIGHT':39,'→':39,'⇒':39,'DOWN':40,'↓':40,'⇓':40,'INSERT':45,'DELETE':46,'DEL':46,'NUMLOCK':144};$.extend(defaults,$.fn.typesequence.defaults,options);elms.keydown(testForSequence);for(var i=0,l=defaults.sequence.length,key,codedKey=null;i<l;++i){key=defaults.sequence[i];if(typeof key=='number')codedKey=key;else if(/^[0-9]$/.test(key))codedKey=parseInt(key,10)+48;else if(/^F[0-9]{1,2}$/.test(key))codedKey=parseInt(key.substring(1),10)+111;else if(/^[a-zA-Z]$/.test(key))codedKey=parseInt(key,36)+55;else if(keycodes[key])codedKey=keycodes[key];if(codedKey){codedSequence.push(codedKey);codedKey=null}}if(defaults.enter){codedSequence.push(13)}function testForSequence(e){if(defaults.once&&executed){return}if(e.which==codedSequence[goingWell]){++goingWell}else{goingWell=0}if(goingWell==codedSequence.length){goingWell=0;if(defaults.once=='depends'){executed=defaults.callback(elms)}else{executed=true;defaults.callback(elms)}}}return elms}$.fn.typesequence.defaults={callback:null,enter:false,once:false,sequence:[]}})(jQuery);<commit_msg>Several speed optimizations.<commit_after>/* By Arjan Eising, 2011. Licence: MIT. See <https://github.com/arjaneising/TypeSequence> */
;(function($){$.fn.typesequence=function(options){var elms=this,executed=false,codedSequence=[],goingWell=0,defaults={};$.extend(defaults,$.fn.typesequence.defaults,options);elms.keydown(testForSequence);for(var i=0,l=defaults.sequence.length,key,codedKey=null;i<l;++i){key=defaults.sequence[i];if(typeof key=='number')codedKey=key;else if(/^[0-9]$/.test(key))codedKey=parseInt(key,10)+48;else if(/^F[0-9]{1,2}$/.test(key))codedKey=parseInt(key.substring(1),10)+111;else if(/^[a-zA-Z]$/.test(key))codedKey=parseInt(key,36)+55;else if($.fn.typesequence.keycodes[key])codedKey=$.fn.typesequence.keycodes[key];if(codedKey){codedSequence.push(codedKey);codedKey=null}}if(defaults.enter){codedSequence.push(13)}function testForSequence(e){if(defaults.once&&executed){return}if(e.which==codedSequence[goingWell]){++goingWell}else{goingWell=0}if(goingWell==codedSequence.length){goingWell=0;if(defaults.once=='depends'){executed=defaults.callback(elms)}else{executed=true;defaults.callback(elms)}}}return elms}$.fn.typesequence.defaults={callback:null,enter:false,once:false,sequence:[]}$.fn.typesequence.keycodes={'BACKSPACE':8,'TAB':9,'ENTER':13,'↵':13,'SHIFT':16,'⇧':16,'CONTROL':17,'ALT':18,'CAPSLOCK':20,'ESC':27,'SPACE':32,' ':32,'PAGEUP':33,'PAGEDOWN':34,'END':35,'HOME':36,'LEFT':37,'←':37,'⇐':37,'UP':38,'↑':38,'⇑':38,'RIGHT':39,'→':39,'⇒':39,'DOWN':40,'↓':40,'⇓':40,'INSERT':45,'DELETE':46,'DEL':46,'NUMLOCK':144}})(jQuery);<|endoftext|>


<commit_before>// Copyright 2015, University of Colorado Boulder

/**
 * Spring described by a parametric equation. This implementation is a variation of the cycloid equation.
 * A prolate cycloid (see http://mathworld.wolfram.com/ProlateCycloid.html) comes closest to this implementation,
 * although it doesn't include aspect ratio and delta phase.
 *
 * The origin (0, 0) of this node is at its left center.
 * The front and back of the spring are drawn as separate paths to provide pseudo-3D visual cues.
 * Performance can be improved dramatically by setting options.pathBoundsMethod to 'none', at
 * the expense of layout accuracy. If you use this option, you can only rely on Node.x and Node.y for
 * doing layout.  See Path.boundsMethod for additional details.
 *
 * The "Experimental" screen provides an extensive test harness for ParametricSpringNode.
 * Run with query parameter "exp" to add the "Experimental" screen to the sim.
 *
 * @author Martin Veillette (Berea College)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var PropertySet = require( 'AXON/PropertySet' );
  var Shape = require( 'KITE/Shape' );
  var Vector2 = require( 'DOT/Vector2' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  // constants
  var SHOW_ORIGIN = false; // {boolean} draws a red circle at the origin, for layout debugging

  /**
   * @param {Object} [options]
   * @constructor
   */
  function ParametricSpringNode( options ) {

    options = _.extend( {

      // {Color|string} colors used for the gradient strokes. middleColor is the dominant color.
      frontColor: 'lightGray',
      middleColor: 'gray',
      backColor: 'black',

      // {number} length of the horizontal line added to the left end of the coil
      leftEndLength: 15,

      // {number} length of the horizontal line added to the right end of the coil
      rightEndLength: 25,

      // {number} number of loops in the coil
      loops: 10,

      // {number} number of points used to approximate 1 loop of the coil
      pointsPerLoop: 40,

      // {number} radius of a loop with aspect ratio of 1:1
      radius: 10,

      // {number} y:x aspect ratio of the loop radius
      aspectRatio: 4,

      // {number} lineWidth used to stroke the Paths
      lineWidth: 3,

      // {number} phase angle of where the loop starts, period is (0,2*PI) radians, counterclockwise
      phase: Math.PI,

      // {number} responsible for the leaning of the coil, variation on a Lissjoue curve, period is (0,2*PI) radians
      deltaPhase: Math.PI / 2,

      // {number} multiplier for radius in the x dimensions, makes the coil appear to get longer
      xScale: 2.5,

      // {string} method used to compute bounds for scenery.Path components, see Path.boundsMethod
      pathBoundsMethod: 'accurate'
    }, options );

    var thisNode = this;

    this.model = new PropertySet( {
      // @public
      loops: options.loops,
      radius: options.radius,
      aspectRatio: options.aspectRatio,
      pointsPerLoop: options.pointsPerLoop,
      lineWidth: options.lineWidth,
      phase: options.phase,
      deltaPhase: options.deltaPhase,
      xScale: options.xScale
    }, options );

    // Paths for the front (foreground) and back (background) parts of the spring
    var pathOptions = {
      boundsMethod: options.pathBoundsMethod,
      lineCap: 'round'
    };
    var frontPath = new Path( null, pathOptions );
    var backPath = new Path( null, pathOptions );

    // Update the line width
    this.model.lineWidthProperty.link( function( lineWidth ) {
      frontPath.lineWidth = backPath.lineWidth = lineWidth;
    } );

    // Mutate these to improve performance
    var springPoints = []; // {Vector2[]} points in the spring (includes the horizontal ends)
    var frontShape; // {Shape}
    var backShape; // {Shape}

    // Changes to these properties require new points (Vector2) and Shapes, because they change
    // the number of points and/or how the points are allocated to frontShape and backShape.
    Property.multilink( [
        this.model.loopsProperty, this.model.pointsPerLoopProperty,
        this.model.aspectRatioProperty, this.model.phaseProperty, this.model.deltaPhaseProperty
      ],
      function( loops, pointsPerLoop, aspectRatio, phase, deltaPhase ) {

        // new points and Shapes
        springPoints.length = 0;
        frontShape = new Shape();
        backShape = new Shape();

        // Values of other properties, to improve readability
        var radius = thisNode.model.radiusProperty.get();
        var xScale = thisNode.model.xScaleProperty.get();

        // compute the points for the coil
        var coilPoints = []; // {Vector2[]}
        var numberOfCoilPoints = computeNumberOfCoilPoints( loops, pointsPerLoop );
        var index;
        for ( index = 0; index < numberOfCoilPoints; index++ ) {
          var coilX = computeCoilX( index, radius, pointsPerLoop, phase, xScale, options.leftEndLength );
          var coilY = computeCoilY( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio );
          coilPoints.push( new Vector2( coilX, coilY ) );
        }

        var p; // {Vector2} reusable point, hoisted explicitly
        var wasFront = true; // was the previous point on the front path?

        // Add points to Shapes
        for ( index = 0; index < numberOfCoilPoints; index++ ) {

          // is the current point on the front path?
          var isFront = ( ( 2 * Math.PI * index / pointsPerLoop + phase + deltaPhase ) % ( 2 * Math.PI ) > Math.PI );

          // horizontal line at left end
          if ( index === 0 ) {
            p = new Vector2( 0, coilPoints[ 0 ].y );
            springPoints.push( p );
            if ( isFront ) {
              frontShape.moveToPoint( p );
            }
            else {
              backShape.moveToPoint( p );
            }
          }

          // coil point
          springPoints.push( coilPoints[ index ] );
          if ( isFront ) {
            // we're in the front
            if ( !wasFront && index !== 0 ) {
              // ... and we've just moved to the front
              frontShape.moveToPoint( coilPoints[ index - 1 ] );
            }
            frontShape.lineToPoint( coilPoints[ index ] );
          }
          else {
            // we're in the back
            if ( wasFront && index !== 0 ) {
              // ... and we've just moved to the back
              backShape.moveToPoint( coilPoints[ index - 1 ] );
            }
            backShape.lineToPoint( coilPoints[ index ] );
          }

          wasFront = isFront;
        }

        // horizontal line at right end
        var lastCoilPoint = coilPoints[ numberOfCoilPoints - 1 ];
        p = new Vector2( lastCoilPoint.x + options.rightEndLength, lastCoilPoint.y );
        springPoints.push( p );
        if ( wasFront ) {
          frontShape.lineToPoint( p );
        }
        else {
          backShape.lineToPoint( p );
        }
        assert && assert( springPoints.length === coilPoints.length + 2,
          'missing some points, have ' + springPoints.length + ', expected ' + coilPoints.length + 2 ); // +2 for horizontal ends

        frontPath.shape = frontShape;
        backPath.shape = backShape;
      } );

    // Changes to these properties can be accomplished by mutating existing points (Vector2) and Shapes,
    // because the number of points remains the same, as does their allocation to frontShape and backShape.
    Property.lazyMultilink( [ this.model.radiusProperty, this.model.xScaleProperty ],
      function( radius, xScale ) {

        // Values of other properties, to improve readability
        var loops = thisNode.model.loopsProperty.get();
        var pointsPerLoop = thisNode.model.pointsPerLoopProperty.get();
        var aspectRatio = thisNode.model.aspectRatioProperty.get();
        var phase = thisNode.model.phaseProperty.get();
        var deltaPhase = thisNode.model.deltaPhaseProperty.get();

        // number of points in the coil
        var numberOfCoilPoints = computeNumberOfCoilPoints( loops, pointsPerLoop );
        assert && assert( numberOfCoilPoints === springPoints.length - 2,
          'unexpected number of coil points: ' + numberOfCoilPoints + ', expected ' + ( springPoints.length - 2 ) ); // -2 for horizontal ends

        // mutate the coil points
        for ( var index = 0; index < numberOfCoilPoints; index++ ) {
          var coilX = computeCoilX( index, radius, pointsPerLoop, phase, xScale, options.leftEndLength );
          var coilY = computeCoilY( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio );
          springPoints[ index + 1 ].setXY( coilX, coilY );
        }

        // mutate horizontal line at left end
        var firstCoilPoint = springPoints[ 1 ];
        springPoints[ 0 ].setXY( 0, firstCoilPoint.y );

        // mutate horizontal line at right end
        var lastCoilPoint = springPoints[ springPoints.length - 2 ];
        springPoints[ springPoints.length - 1 ].setXY( lastCoilPoint.x + options.rightEndLength, lastCoilPoint.y );

        // Tell shapes that their points have changed.
        frontShape.invalidatePoints();
        backShape.invalidatePoints();
      } );

    // Update the stroke gradients
    Property.multilink( [ this.model.radiusProperty, this.model.aspectRatioProperty ],
      function( radius, aspectRatio ) {

        var yRadius = radius * aspectRatio;

        frontPath.stroke = new LinearGradient( 0, -yRadius, 0, yRadius )
          .addColorStop( 0, options.middleColor )
          .addColorStop( 0.35, options.frontColor )
          .addColorStop( 0.65, options.frontColor )
          .addColorStop( 1, options.middleColor );

        backPath.stroke = new LinearGradient( 0, -yRadius, 0, yRadius )
          .addColorStop( 0, options.middleColor )
          .addColorStop( 0.5, options.backColor )
          .addColorStop( 1, options.middleColor );
      } );

    options.children = [ backPath, frontPath ];
    Node.call( this, options );

    if ( SHOW_ORIGIN ) {
      this.addChild( new Circle( 3, { fill: 'red' } ) );
    }
  }

  sceneryPhet.register( 'ParametricSpringNode', ParametricSpringNode );

  // Gets the number of points in the coil part of the spring.
  var computeNumberOfCoilPoints = function( loops, pointsPerLoop ) {
    return loops * pointsPerLoop + 1;
  };

  // Computes the x coordinate for a point on the coil.
  var computeCoilX = function( index, radius, pointsPerLoop, phase, xScale, leftEndLength ) {
    return ( leftEndLength + radius ) + radius * Math.cos( 2 * Math.PI * index / pointsPerLoop + phase ) + xScale * (index / pointsPerLoop) * radius;
  };

  // Computes the y coordinate for a point on the coil.
  var computeCoilY = function( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio ) {
    return aspectRatio * radius * Math.cos( 2 * Math.PI * index / pointsPerLoop + deltaPhase + phase );
  };

  return inherit( Node, ParametricSpringNode, {

    // @public
    reset: function() {
      this.model.reset();
    }
  } );
} );
<commit_msg>Fixed lineJoin issue for PSN #220<commit_after>// Copyright 2015, University of Colorado Boulder

/**
 * Spring described by a parametric equation. This implementation is a variation of the cycloid equation.
 * A prolate cycloid (see http://mathworld.wolfram.com/ProlateCycloid.html) comes closest to this implementation,
 * although it doesn't include aspect ratio and delta phase.
 *
 * The origin (0, 0) of this node is at its left center.
 * The front and back of the spring are drawn as separate paths to provide pseudo-3D visual cues.
 * Performance can be improved dramatically by setting options.pathBoundsMethod to 'none', at
 * the expense of layout accuracy. If you use this option, you can only rely on Node.x and Node.y for
 * doing layout.  See Path.boundsMethod for additional details.
 *
 * The "Experimental" screen provides an extensive test harness for ParametricSpringNode.
 * Run with query parameter "exp" to add the "Experimental" screen to the sim.
 *
 * @author Martin Veillette (Berea College)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Circle = require( 'SCENERY/nodes/Circle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var PropertySet = require( 'AXON/PropertySet' );
  var Shape = require( 'KITE/Shape' );
  var Vector2 = require( 'DOT/Vector2' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  // constants
  var SHOW_ORIGIN = false; // {boolean} draws a red circle at the origin, for layout debugging

  /**
   * @param {Object} [options]
   * @constructor
   */
  function ParametricSpringNode( options ) {

    options = _.extend( {

      // {Color|string} colors used for the gradient strokes. middleColor is the dominant color.
      frontColor: 'lightGray',
      middleColor: 'gray',
      backColor: 'black',

      // {number} length of the horizontal line added to the left end of the coil
      leftEndLength: 15,

      // {number} length of the horizontal line added to the right end of the coil
      rightEndLength: 25,

      // {number} number of loops in the coil
      loops: 10,

      // {number} number of points used to approximate 1 loop of the coil
      pointsPerLoop: 40,

      // {number} radius of a loop with aspect ratio of 1:1
      radius: 10,

      // {number} y:x aspect ratio of the loop radius
      aspectRatio: 4,

      // {number} lineWidth used to stroke the Paths
      lineWidth: 3,

      // {number} phase angle of where the loop starts, period is (0,2*PI) radians, counterclockwise
      phase: Math.PI,

      // {number} responsible for the leaning of the coil, variation on a Lissjoue curve, period is (0,2*PI) radians
      deltaPhase: Math.PI / 2,

      // {number} multiplier for radius in the x dimensions, makes the coil appear to get longer
      xScale: 2.5,

      // {string} method used to compute bounds for scenery.Path components, see Path.boundsMethod
      pathBoundsMethod: 'accurate'
    }, options );

    var thisNode = this;

    this.model = new PropertySet( {
      // @public
      loops: options.loops,
      radius: options.radius,
      aspectRatio: options.aspectRatio,
      pointsPerLoop: options.pointsPerLoop,
      lineWidth: options.lineWidth,
      phase: options.phase,
      deltaPhase: options.deltaPhase,
      xScale: options.xScale
    }, options );

    // Paths for the front (foreground) and back (background) parts of the spring
    var pathOptions = {
      boundsMethod: options.pathBoundsMethod,
      lineCap: 'round',
      lineJoin: 'round'
    };
    var frontPath = new Path( null, pathOptions );
    var backPath = new Path( null, pathOptions );

    // Update the line width
    this.model.lineWidthProperty.link( function( lineWidth ) {
      frontPath.lineWidth = backPath.lineWidth = lineWidth;
    } );

    // Mutate these to improve performance
    var springPoints = []; // {Vector2[]} points in the spring (includes the horizontal ends)
    var frontShape; // {Shape}
    var backShape; // {Shape}

    // Changes to these properties require new points (Vector2) and Shapes, because they change
    // the number of points and/or how the points are allocated to frontShape and backShape.
    Property.multilink( [
        this.model.loopsProperty, this.model.pointsPerLoopProperty,
        this.model.aspectRatioProperty, this.model.phaseProperty, this.model.deltaPhaseProperty
      ],
      function( loops, pointsPerLoop, aspectRatio, phase, deltaPhase ) {

        // new points and Shapes
        springPoints.length = 0;
        frontShape = new Shape();
        backShape = new Shape();

        // Values of other properties, to improve readability
        var radius = thisNode.model.radiusProperty.get();
        var xScale = thisNode.model.xScaleProperty.get();

        // compute the points for the coil
        var coilPoints = []; // {Vector2[]}
        var numberOfCoilPoints = computeNumberOfCoilPoints( loops, pointsPerLoop );
        var index;
        for ( index = 0; index < numberOfCoilPoints; index++ ) {
          var coilX = computeCoilX( index, radius, pointsPerLoop, phase, xScale, options.leftEndLength );
          var coilY = computeCoilY( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio );
          coilPoints.push( new Vector2( coilX, coilY ) );
        }

        var p; // {Vector2} reusable point, hoisted explicitly
        var wasFront = true; // was the previous point on the front path?

        // Add points to Shapes
        for ( index = 0; index < numberOfCoilPoints; index++ ) {

          // is the current point on the front path?
          var isFront = ( ( 2 * Math.PI * index / pointsPerLoop + phase + deltaPhase ) % ( 2 * Math.PI ) > Math.PI );

          // horizontal line at left end
          if ( index === 0 ) {
            p = new Vector2( 0, coilPoints[ 0 ].y );
            springPoints.push( p );
            if ( isFront ) {
              frontShape.moveToPoint( p );
            }
            else {
              backShape.moveToPoint( p );
            }
          }

          // coil point
          springPoints.push( coilPoints[ index ] );
          if ( isFront ) {
            // we're in the front
            if ( !wasFront && index !== 0 ) {
              // ... and we've just moved to the front
              frontShape.moveToPoint( coilPoints[ index - 1 ] );
            }
            frontShape.lineToPoint( coilPoints[ index ] );
          }
          else {
            // we're in the back
            if ( wasFront && index !== 0 ) {
              // ... and we've just moved to the back
              backShape.moveToPoint( coilPoints[ index - 1 ] );
            }
            backShape.lineToPoint( coilPoints[ index ] );
          }

          wasFront = isFront;
        }

        // horizontal line at right end
        var lastCoilPoint = coilPoints[ numberOfCoilPoints - 1 ];
        p = new Vector2( lastCoilPoint.x + options.rightEndLength, lastCoilPoint.y );
        springPoints.push( p );
        if ( wasFront ) {
          frontShape.lineToPoint( p );
        }
        else {
          backShape.lineToPoint( p );
        }
        assert && assert( springPoints.length === coilPoints.length + 2,
          'missing some points, have ' + springPoints.length + ', expected ' + coilPoints.length + 2 ); // +2 for horizontal ends

        frontPath.shape = frontShape;
        backPath.shape = backShape;
      } );

    // Changes to these properties can be accomplished by mutating existing points (Vector2) and Shapes,
    // because the number of points remains the same, as does their allocation to frontShape and backShape.
    Property.lazyMultilink( [ this.model.radiusProperty, this.model.xScaleProperty ],
      function( radius, xScale ) {

        // Values of other properties, to improve readability
        var loops = thisNode.model.loopsProperty.get();
        var pointsPerLoop = thisNode.model.pointsPerLoopProperty.get();
        var aspectRatio = thisNode.model.aspectRatioProperty.get();
        var phase = thisNode.model.phaseProperty.get();
        var deltaPhase = thisNode.model.deltaPhaseProperty.get();

        // number of points in the coil
        var numberOfCoilPoints = computeNumberOfCoilPoints( loops, pointsPerLoop );
        assert && assert( numberOfCoilPoints === springPoints.length - 2,
          'unexpected number of coil points: ' + numberOfCoilPoints + ', expected ' + ( springPoints.length - 2 ) ); // -2 for horizontal ends

        // mutate the coil points
        for ( var index = 0; index < numberOfCoilPoints; index++ ) {
          var coilX = computeCoilX( index, radius, pointsPerLoop, phase, xScale, options.leftEndLength );
          var coilY = computeCoilY( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio );
          springPoints[ index + 1 ].setXY( coilX, coilY );
        }

        // mutate horizontal line at left end
        var firstCoilPoint = springPoints[ 1 ];
        springPoints[ 0 ].setXY( 0, firstCoilPoint.y );

        // mutate horizontal line at right end
        var lastCoilPoint = springPoints[ springPoints.length - 2 ];
        springPoints[ springPoints.length - 1 ].setXY( lastCoilPoint.x + options.rightEndLength, lastCoilPoint.y );

        // Tell shapes that their points have changed.
        frontShape.invalidatePoints();
        backShape.invalidatePoints();
      } );

    // Update the stroke gradients
    Property.multilink( [ this.model.radiusProperty, this.model.aspectRatioProperty ],
      function( radius, aspectRatio ) {

        var yRadius = radius * aspectRatio;

        frontPath.stroke = new LinearGradient( 0, -yRadius, 0, yRadius )
          .addColorStop( 0, options.middleColor )
          .addColorStop( 0.35, options.frontColor )
          .addColorStop( 0.65, options.frontColor )
          .addColorStop( 1, options.middleColor );

        backPath.stroke = new LinearGradient( 0, -yRadius, 0, yRadius )
          .addColorStop( 0, options.middleColor )
          .addColorStop( 0.5, options.backColor )
          .addColorStop( 1, options.middleColor );
      } );

    options.children = [ backPath, frontPath ];
    Node.call( this, options );

    if ( SHOW_ORIGIN ) {
      this.addChild( new Circle( 3, { fill: 'red' } ) );
    }
  }

  sceneryPhet.register( 'ParametricSpringNode', ParametricSpringNode );

  // Gets the number of points in the coil part of the spring.
  var computeNumberOfCoilPoints = function( loops, pointsPerLoop ) {
    return loops * pointsPerLoop + 1;
  };

  // Computes the x coordinate for a point on the coil.
  var computeCoilX = function( index, radius, pointsPerLoop, phase, xScale, leftEndLength ) {
    return ( leftEndLength + radius ) + radius * Math.cos( 2 * Math.PI * index / pointsPerLoop + phase ) + xScale * (index / pointsPerLoop) * radius;
  };

  // Computes the y coordinate for a point on the coil.
  var computeCoilY = function( index, radius, pointsPerLoop, phase, deltaPhase, aspectRatio ) {
    return aspectRatio * radius * Math.cos( 2 * Math.PI * index / pointsPerLoop + deltaPhase + phase );
  };

  return inherit( Node, ParametricSpringNode, {

    // @public
    reset: function() {
      this.model.reset();
    }
  } );
} );
<|endoftext|>


<commit_before>System.register('flarum/auth/github/main', ['flarum/app', 'flarum/auth/github/components/GithubSettingsModal'], function (_export) {
  'use strict';

  var app, GithubSettingsModal;
  return {
    setters: [function (_flarumApp) {
      app = _flarumApp['default'];
    }, function (_flarumAuthGithubComponentsGithubSettingsModal) {
      GithubSettingsModal = _flarumAuthGithubComponentsGithubSettingsModal['default'];
    }],
    execute: function () {

      app.initializers.add('flarum-auth-github', function () {
        app.extensionSettings['flarum-auth-github'] = function () {
          return app.modal.show(new GithubSettingsModal());
        };
      });
    }
  };
});;System.register('flarum/auth/github/components/GithubSettingsModal', ['flarum/components/SettingsModal'], function (_export) {
  'use strict';

  var SettingsModal, GithubSettingsModal;
  return {
    setters: [function (_flarumComponentsSettingsModal) {
      SettingsModal = _flarumComponentsSettingsModal['default'];
    }],
    execute: function () {
      GithubSettingsModal = (function (_SettingsModal) {
        babelHelpers.inherits(GithubSettingsModal, _SettingsModal);

        function GithubSettingsModal() {
          babelHelpers.classCallCheck(this, GithubSettingsModal);
          babelHelpers.get(Object.getPrototypeOf(GithubSettingsModal.prototype), 'constructor', this).apply(this, arguments);
        }

        babelHelpers.createClass(GithubSettingsModal, [{
          key: 'className',
          value: function className() {
            return 'GithubSettingsModal Modal--small';
          }
        }, {
          key: 'title',
          value: function title() {
            return 'GitHub Settings';
          }
        }, {
          key: 'form',
          value: function form() {
            return [m(
              'div',
              { className: 'Form-group' },
              m(
                'label',
                null,
                'Client ID'
              ),
              m('input', { className: 'FormControl', bidi: this.setting('flarum-auth-github.client_id') })
            ), m(
              'div',
              { className: 'Form-group' },
              m(
                'label',
                null,
                'Client Secret'
              ),
              m('input', { className: 'FormControl', bidi: this.setting('flarum-auth-github.client_secret') })
            )];
          }
        }]);
        return GithubSettingsModal;
      })(SettingsModal);

      _export('default', GithubSettingsModal);
    }
  };
});<commit_msg>Release 0.1.0-beta.3<commit_after>System.register('flarum/auth/github/components/GithubSettingsModal', ['flarum/components/SettingsModal'], function (_export) {
  'use strict';

  var SettingsModal, GithubSettingsModal;
  return {
    setters: [function (_flarumComponentsSettingsModal) {
      SettingsModal = _flarumComponentsSettingsModal['default'];
    }],
    execute: function () {
      GithubSettingsModal = (function (_SettingsModal) {
        babelHelpers.inherits(GithubSettingsModal, _SettingsModal);

        function GithubSettingsModal() {
          babelHelpers.classCallCheck(this, GithubSettingsModal);
          babelHelpers.get(Object.getPrototypeOf(GithubSettingsModal.prototype), 'constructor', this).apply(this, arguments);
        }

        babelHelpers.createClass(GithubSettingsModal, [{
          key: 'className',
          value: function className() {
            return 'GithubSettingsModal Modal--small';
          }
        }, {
          key: 'title',
          value: function title() {
            return 'GitHub Settings';
          }
        }, {
          key: 'form',
          value: function form() {
            return [m(
              'div',
              { className: 'Form-group' },
              m(
                'label',
                null,
                'Client ID'
              ),
              m('input', { className: 'FormControl', bidi: this.setting('flarum-auth-github.client_id') })
            ), m(
              'div',
              { className: 'Form-group' },
              m(
                'label',
                null,
                'Client Secret'
              ),
              m('input', { className: 'FormControl', bidi: this.setting('flarum-auth-github.client_secret') })
            )];
          }
        }]);
        return GithubSettingsModal;
      })(SettingsModal);

      _export('default', GithubSettingsModal);
    }
  };
});;
System.register('flarum/auth/github/main', ['flarum/app', 'flarum/auth/github/components/GithubSettingsModal'], function (_export) {
  'use strict';

  var app, GithubSettingsModal;
  return {
    setters: [function (_flarumApp) {
      app = _flarumApp['default'];
    }, function (_flarumAuthGithubComponentsGithubSettingsModal) {
      GithubSettingsModal = _flarumAuthGithubComponentsGithubSettingsModal['default'];
    }],
    execute: function () {

      app.initializers.add('flarum-auth-github', function () {
        app.extensionSettings['flarum-auth-github'] = function () {
          return app.modal.show(new GithubSettingsModal());
        };
      });
    }
  };
});<|endoftext|>


<commit_before>import { Field, Formik } from 'formik'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  ControlLabel,
  FormControl,
  FormGroup,
  HelpBlock,
  Label,
  ToggleButton,
  ToggleButtonGroup
} from 'react-bootstrap'
import { formatPhoneNumber, isPossiblePhoneNumber } from 'react-phone-number-input'
import Input from 'react-phone-number-input/input'
import styled, { css } from 'styled-components'
import * as yup from 'yup'

import { isBlank } from '../../util/ui'

const allowedNotificationChannels = [
  {
    type: 'email',
    text: 'Email'
  },
  {
    type: 'sms',
    text: 'SMS'
  },
  {
    type: 'none',
    text: 'Don\'t notify me'
  }
]

// Styles
// HACK: Preverve container height.
const Details = styled.div`
  min-height: 150px;
  margin-bottom: 15px;
`
const ControlStrip = styled.div`
  > * {
    margin-right: 4px;
  }
`
const phoneFieldCss = css`
  display: inline-block;
  vertical-align: middle;
  width: 14em;
`
const InlineTextInput = styled(FormControl)`
  ${phoneFieldCss}
`
const InlineStatic = styled(FormControl.Static)`
  ${phoneFieldCss}
`
const InlinePhoneInput = styled(Input)`
  ${phoneFieldCss}
`

const FlushLink = styled(Button)`
  padding-left: 0;
  padding-right: 0;
`

const INVALID_PHONE_MSG = 'Please enter a valid phone number.'

/**
 * @param {*} props The props from which to extract the Formik state to test.
 * @param {*} field THe field name to test.
 * @returns One of the Bootstrao validationState values.
 */
function getErrorState (props, field) {
  const { errors, touched, values } = props
  const value = values[field]
  const isInvalid = isBlank(value) || !!errors[field]

  // one of the Bootstrap validationState values.
  return isInvalid && touched[field]
    ? 'error'
    : null
}

const codeValidationSchema = yup.object({
  validationCode: yup.string().required('Please enter a validation code.')
})

/**
 * User notification preferences pane.
 */
class NotificationPrefsPane extends Component {
  _handleOnPhoneVerified = ({ isPhoneNumberVerified, phoneNumber }) => {
    const { setFieldValue } = this.props

    // Update the parent's Formik state (so when saving, it sends the updated numbers)
    setFieldValue('isPhoneNumberVerified', isPhoneNumberVerified)
    setFieldValue('phoneNumber', phoneNumber)
  }

  render () {
    // All props below are Formik props (https://formik.org/docs/api/formik#props-1)
    // except * below which indicates the prop is provided by UserAccountScreen.
    const {
      handleBlur,
      handleChange,
      onRequestPhoneVerificationCode, // *
      onSendPhoneVerificationCode, // *
      phoneFormatOptions, // *
      values: userData
    } = this.props

    const {
      isPhoneNumberVerified,
      notificationChannel,
      phoneNumber
    } = userData

    const initialFormikValues = {
      validationCode: ''
    }

    return (

          You can receive notifications about trips you frequently take.

        <FormGroup>
          <ControlLabel>How would you like to receive notifications?</ControlLabel>
          <ButtonToolbar>
            <ToggleButtonGroup
              name='notificationChannel'
              type='radio'
              defaultValue={notificationChannel}
            >
              {allowedNotificationChannels.map(({ type, text }, index) => (
                <ToggleButton
                  bsStyle={notificationChannel === type ? 'primary' : 'default'}
                  key={index}
                  // onBlur and onChange have to be set on individual controls instead of the control group
                  // in order for Formik to correcly process the changes.
                  onBlur={handleBlur}
                  onChange={handleChange}
                  value={type}
                >
                  {text}
                </ToggleButton>
              ))}
            </ToggleButtonGroup>
          </ButtonToolbar>
        </FormGroup>

          {notificationChannel === 'email' && (
            <FormGroup>
              <ControlLabel>Notification emails will be sent to:</ControlLabel>
              <Field as={FormControl} name='email' readOnly />
            </FormGroup>
          )}
          {notificationChannel === 'sms' && (
            <Formik
              // Phone number needs to be validated on change.
              validateOnChange
              validationSchema={codeValidationSchema}
              initialValues={initialFormikValues}
            >
              {
                // Pass Formik props to the component rendered so Formik can manage its validation.
                // (The validation for this component is independent of the validation set in UserAccountScreen.)
                innerProps => {
                  return (
                    <PhoneNumberEditor
                      {...innerProps}
                      initialPhoneNumber={phoneNumber}
                      initialPhoneNumberVerified={isPhoneNumberVerified}
                      onRequestCode={onRequestPhoneVerificationCode}
                      onSubmitCode={onSendPhoneVerificationCode}
                      onVerified={this._handleOnPhoneVerified}
                      phoneFormatOptions={phoneFormatOptions}
                    />
                  )
                }
              }
            </Formik>
          )}

    )
  }
}

export default NotificationPrefsPane

/**
 * Sub-component that handles phone number and validation code editing and validation intricacies.
 */
class PhoneNumberEditor extends Component {
  constructor (props) {
    super(props)

    const { initialPhoneNumber, initialPhoneNumberVerified } = props
    this.state = {
      // Holds the last known verified phone number.
      currentNumber: initialPhoneNumber,

      // The verified status of the above number.
      currentNumberVerified: initialPhoneNumberVerified,

      // If true, phone number is being edited.
      // For new users, render component in editing state.
      isEditing: isBlank(initialPhoneNumber),

      // If true, a phone verification request has been sent and the UI is awaiting the user sending the code.
      isPending: false,

      // Holds the new phone number (+15555550123 format) entered by the user
      // (outside of Formik because (Phone)Input does not have a standard onChange event or simple valitity test).
      newPhoneNumber: ''
    }
  }

  _handleEditNumber = () => {
    this.setState({
      isEditing: true,
      isPending: false,
      newPhoneNumber: ''
    })
  }

  _handleNewPhoneNumberChange = newPhoneNumber => {
    this.setState({
      newPhoneNumber
    })
  }

  _handleCancelEditNumber = () => {
    this.setState({
      isEditing: false,
      isPending: false
    })
  }

  _handlePhoneNumberKeyDown = e => {
    if (e.keyCode === 13) {
      // On the user pressing enter (keyCode 13) on the phone number field,
      // prevent form submission and request the code.
      e.preventDefault()
      this._handleRequestCode()
    }
  }

  _handleValidationCodeKeyDown = e => {
    if (e.keyCode === 13) {
      // On the user pressing enter (keyCode 13) on the validation code field,
      // prevent form submission and send the validation code.
      e.preventDefault()
      this._handleSubmitCode()
    }
  }

  _handleRequestCode = async () => {
    // Send phone verification request with the entered values.
    const { onRequestCode, setFieldValue, setFieldTouched } = this.props
    const { newPhoneNumber } = this.state

    // Empty the input box (and disable the Send text button).
    this.setState({
      newPhoneNumber: ''
    })

    const result = await onRequestCode(newPhoneNumber)

    if (result && result.phoneNumber === newPhoneNumber && !result.isPhoneNumberVerified) {
      // On success (phoneNumber is updated and verified status is false), update the phone number in component state.
      this.setState({
        newPhoneNumber: result.phoneNumber
      })

      // Set empty code field in Formik's state and prompt for code.
      setFieldValue('validationCode', '')
      setFieldTouched('validationCode', false)
      this.setState({
        isEditing: false,
        isPending: true
      })
    }
  }

  _handleSubmitCode = async () => {
    const { onSubmitCode, onVerified, setFieldValue, values } = this.props
    const { validationCode } = values

    // Clear the code field and disable the verify button.
    // (The code to send is already captured in const above.)
    setFieldValue('validationCode', '')

    const result = await onSubmitCode(validationCode)

    if (result && result.isPhoneNumberVerified) {
      // On success (status is changed to verified), update the phone number in component state and hide the code field.
      this.setState({
        currentNumber: result.phoneNumber,
        currentNumberVerified: true,
        isPending: false,
        newPhoneNumber: ''
      })

      onVerified(result)
    }
  }

  render () {
    // All props below are Formik props (https://formik.org/docs/api/formik#props-1)
    // except where indicated.
    const {
      errors,
      phoneFormatOptions, // Provided by parent.
      touched
    } = this.props
    const { currentNumber, currentNumberVerified, isEditing, isPending, newPhoneNumber } = this.state

    // Here are the states we are dealing with:
    // - First time entering a phone number/validation code (blank value, not modified)
    //   => no color, no feedback indication.
    // - Typing backspace all the way to erase a number/code (blank value, modified)
    //   => red error.
    // - Typing a phone number that doesn't match the configured phoneNumberRegEx
    //   => red error.
    const isPhoneInvalid = !isBlank(newPhoneNumber) && !isPossiblePhoneNumber(newPhoneNumber)
    const phoneErrorState = isPhoneInvalid ? 'error' : null
    const codeErrorState = getErrorState(this.props, 'validationCode')

    return (
      <>
        {isEditing
          ? (
            <FormGroup validationState={phoneErrorState}>
              <ControlLabel>Enter your phone number for SMS notifications:</ControlLabel>
              <ControlStrip>
                <InlinePhoneInput
                  className='form-control'
                  country={phoneFormatOptions.regionCode}
                  onChange={this._handleNewPhoneNumberChange}
                  onKeyDown={this._handlePhoneNumberKeyDown}
                  placeholder='Enter phone number'
                  readOnly={!isEditing}
                  type='tel'
                  value={newPhoneNumber}
                />
                <Button
                  bsStyle='primary'
                  disabled={isPhoneInvalid}
                  onClick={this._handleRequestCode}
                >
                  Send verification text
                </Button>
                { // Show cancel button only if a phone number is already recorded.
                  currentNumber && <Button onClick={this._handleCancelEditNumber}>Cancel</Button>}
                {isPhoneInvalid && <HelpBlock>{INVALID_PHONE_MSG}</HelpBlock>}
              </ControlStrip>
            </FormGroup>
          ) : (
            <FormGroup>
              <ControlLabel>SMS notifications will be sent to:</ControlLabel>
              <ControlStrip>
                <InlineStatic>
                  {isPending
                    // eslint-disable-next-line jsx-a11y/label-has-for
                    ? <>{formatPhoneNumber(newPhoneNumber)} Pending</>
                    // eslint-disable-next-line jsx-a11y/label-has-for
                    : <>{formatPhoneNumber(currentNumber)} {currentNumberVerified && Verified}</>
                  }
                </InlineStatic>
                <Button onClick={this._handleEditNumber}>Change number</Button>
              </ControlStrip>
            </FormGroup>
          )}

        {isPending && (
          <FormGroup validationState={codeErrorState}>

              Please check the SMS messaging app on your mobile phone
              for a text message with a verification code, and enter the code below.

            <ControlLabel>Verification code:</ControlLabel>
            <ControlStrip>
              <Field as={InlineTextInput}
                name='validationCode'
                onKeyDown={this._handleValidationCodeKeyDown}
                placeholder='123456'
                // onBlur, onChange, and value are passed automatically by Formik
              />
              <Button
                bsStyle='primary'
                disabled={!!errors.validationCode}
                onClick={this._handleSubmitCode}
              >
                Verify
              </Button>
              {touched.validationCode && errors.validationCode && (
                <HelpBlock>{errors.validationCode}</HelpBlock>
              )}
            </ControlStrip>
            <FlushLink bsStyle='link' onClick={this._handleRequestCode}>Request a new code</FlushLink>
          </FormGroup>
        )}
      </>
    )
  }
}
<commit_msg>refactor(NotificationPrefsPane): Use static text for email (same as verified phone)<commit_after>import { Field, Formik } from 'formik'
import React, { Component } from 'react'
import {
  Button,
  ButtonToolbar,
  ControlLabel,
  FormControl,
  FormGroup,
  HelpBlock,
  Label,
  ToggleButton,
  ToggleButtonGroup
} from 'react-bootstrap'
import { formatPhoneNumber, isPossiblePhoneNumber } from 'react-phone-number-input'
import Input from 'react-phone-number-input/input'
import styled, { css } from 'styled-components'
import * as yup from 'yup'

import { isBlank } from '../../util/ui'

const allowedNotificationChannels = [
  {
    type: 'email',
    text: 'Email'
  },
  {
    type: 'sms',
    text: 'SMS'
  },
  {
    type: 'none',
    text: 'Don\'t notify me'
  }
]

// Styles
// HACK: Preverve container height.
const Details = styled.div`
  min-height: 150px;
  margin-bottom: 15px;
`
const ControlStrip = styled.div`
  > * {
    margin-right: 4px;
  }
`
const phoneFieldCss = css`
  display: inline-block;
  vertical-align: middle;
  width: 14em;
`
const InlineTextInput = styled(FormControl)`
  ${phoneFieldCss}
`
const InlineStatic = styled(FormControl.Static)`
  ${phoneFieldCss}
`
const InlinePhoneInput = styled(Input)`
  ${phoneFieldCss}
`

const FlushLink = styled(Button)`
  padding-left: 0;
  padding-right: 0;
`

const INVALID_PHONE_MSG = 'Please enter a valid phone number.'

/**
 * @param {*} props The props from which to extract the Formik state to test.
 * @param {*} field THe field name to test.
 * @returns One of the Bootstrao validationState values.
 */
function getErrorState (props, field) {
  const { errors, touched, values } = props
  const value = values[field]
  const isInvalid = isBlank(value) || !!errors[field]

  // one of the Bootstrap validationState values.
  return isInvalid && touched[field]
    ? 'error'
    : null
}

const codeValidationSchema = yup.object({
  validationCode: yup.string().required('Please enter a validation code.')
})

/**
 * User notification preferences pane.
 */
class NotificationPrefsPane extends Component {
  _handleOnPhoneVerified = ({ isPhoneNumberVerified, phoneNumber }) => {
    const { setFieldValue } = this.props

    // Update the parent's Formik state (so when saving, it sends the updated numbers)
    setFieldValue('isPhoneNumberVerified', isPhoneNumberVerified)
    setFieldValue('phoneNumber', phoneNumber)
  }

  render () {
    // All props below are Formik props (https://formik.org/docs/api/formik#props-1)
    // except * below which indicates the prop is provided by UserAccountScreen.
    const {
      handleBlur,
      handleChange,
      onRequestPhoneVerificationCode, // *
      onSendPhoneVerificationCode, // *
      phoneFormatOptions, // *
      values: userData
    } = this.props

    const {
      email,
      isPhoneNumberVerified,
      notificationChannel,
      phoneNumber
    } = userData

    const initialFormikValues = {
      validationCode: ''
    }

    return (

          You can receive notifications about trips you frequently take.

        <FormGroup>
          <ControlLabel>How would you like to receive notifications?</ControlLabel>
          <ButtonToolbar>
            <ToggleButtonGroup
              name='notificationChannel'
              type='radio'
              defaultValue={notificationChannel}
            >
              {allowedNotificationChannels.map(({ type, text }, index) => (
                <ToggleButton
                  bsStyle={notificationChannel === type ? 'primary' : 'default'}
                  key={index}
                  // onBlur and onChange have to be set on individual controls instead of the control group
                  // in order for Formik to correcly process the changes.
                  onBlur={handleBlur}
                  onChange={handleChange}
                  value={type}
                >
                  {text}
                </ToggleButton>
              ))}
            </ToggleButtonGroup>
          </ButtonToolbar>
        </FormGroup>

          {notificationChannel === 'email' && (
            <FormGroup>
              <ControlLabel>Notification emails will be sent to:</ControlLabel>
              <FormControl.Static>{email}</FormControl.Static>
            </FormGroup>
          )}
          {notificationChannel === 'sms' && (
            <Formik
              // Phone number needs to be validated on change.
              validateOnChange
              validationSchema={codeValidationSchema}
              initialValues={initialFormikValues}
            >
              {
                // Pass Formik props to the component rendered so Formik can manage its validation.
                // (The validation for this component is independent of the validation set in UserAccountScreen.)
                innerProps => {
                  return (
                    <PhoneNumberEditor
                      {...innerProps}
                      initialPhoneNumber={phoneNumber}
                      initialPhoneNumberVerified={isPhoneNumberVerified}
                      onRequestCode={onRequestPhoneVerificationCode}
                      onSubmitCode={onSendPhoneVerificationCode}
                      onVerified={this._handleOnPhoneVerified}
                      phoneFormatOptions={phoneFormatOptions}
                    />
                  )
                }
              }
            </Formik>
          )}

    )
  }
}

export default NotificationPrefsPane

/**
 * Sub-component that handles phone number and validation code editing and validation intricacies.
 */
class PhoneNumberEditor extends Component {
  constructor (props) {
    super(props)

    const { initialPhoneNumber, initialPhoneNumberVerified } = props
    this.state = {
      // Holds the last known verified phone number.
      currentNumber: initialPhoneNumber,

      // The verified status of the above number.
      currentNumberVerified: initialPhoneNumberVerified,

      // If true, phone number is being edited.
      // For new users, render component in editing state.
      isEditing: isBlank(initialPhoneNumber),

      // If true, a phone verification request has been sent and the UI is awaiting the user sending the code.
      isPending: false,

      // Holds the new phone number (+15555550123 format) entered by the user
      // (outside of Formik because (Phone)Input does not have a standard onChange event or simple valitity test).
      newPhoneNumber: ''
    }
  }

  _handleEditNumber = () => {
    this.setState({
      isEditing: true,
      isPending: false,
      newPhoneNumber: ''
    })
  }

  _handleNewPhoneNumberChange = newPhoneNumber => {
    this.setState({
      newPhoneNumber
    })
  }

  _handleCancelEditNumber = () => {
    this.setState({
      isEditing: false,
      isPending: false
    })
  }

  _handlePhoneNumberKeyDown = e => {
    if (e.keyCode === 13) {
      // On the user pressing enter (keyCode 13) on the phone number field,
      // prevent form submission and request the code.
      e.preventDefault()
      this._handleRequestCode()
    }
  }

  _handleValidationCodeKeyDown = e => {
    if (e.keyCode === 13) {
      // On the user pressing enter (keyCode 13) on the validation code field,
      // prevent form submission and send the validation code.
      e.preventDefault()
      this._handleSubmitCode()
    }
  }

  _handleRequestCode = async () => {
    // Send phone verification request with the entered values.
    const { onRequestCode, setFieldValue, setFieldTouched } = this.props
    const { newPhoneNumber } = this.state

    // Empty the input box (and disable the Send text button).
    this.setState({
      newPhoneNumber: ''
    })

    const result = await onRequestCode(newPhoneNumber)

    if (result && result.phoneNumber === newPhoneNumber && !result.isPhoneNumberVerified) {
      // On success (phoneNumber is updated and verified status is false), update the phone number in component state.
      this.setState({
        newPhoneNumber: result.phoneNumber
      })

      // Set empty code field in Formik's state and prompt for code.
      setFieldValue('validationCode', '')
      setFieldTouched('validationCode', false)
      this.setState({
        isEditing: false,
        isPending: true
      })
    }
  }

  _handleSubmitCode = async () => {
    const { onSubmitCode, onVerified, setFieldValue, values } = this.props
    const { validationCode } = values

    // Clear the code field and disable the verify button.
    // (The code to send is already captured in const above.)
    setFieldValue('validationCode', '')

    const result = await onSubmitCode(validationCode)

    if (result && result.isPhoneNumberVerified) {
      // On success (status is changed to verified), update the phone number in component state and hide the code field.
      this.setState({
        currentNumber: result.phoneNumber,
        currentNumberVerified: true,
        isPending: false,
        newPhoneNumber: ''
      })

      onVerified(result)
    }
  }

  render () {
    // All props below are Formik props (https://formik.org/docs/api/formik#props-1)
    // except where indicated.
    const {
      errors,
      phoneFormatOptions, // Provided by parent.
      touched
    } = this.props
    const { currentNumber, currentNumberVerified, isEditing, isPending, newPhoneNumber } = this.state

    // Here are the states we are dealing with:
    // - First time entering a phone number/validation code (blank value, not modified)
    //   => no color, no feedback indication.
    // - Typing backspace all the way to erase a number/code (blank value, modified)
    //   => red error.
    // - Typing a phone number that doesn't match the configured phoneNumberRegEx
    //   => red error.
    const isPhoneInvalid = !isBlank(newPhoneNumber) && !isPossiblePhoneNumber(newPhoneNumber)
    const phoneErrorState = isPhoneInvalid ? 'error' : null
    const codeErrorState = getErrorState(this.props, 'validationCode')

    return (
      <>
        {isEditing
          ? (
            <FormGroup validationState={phoneErrorState}>
              <ControlLabel>Enter your phone number for SMS notifications:</ControlLabel>
              <ControlStrip>
                <InlinePhoneInput
                  className='form-control'
                  country={phoneFormatOptions.regionCode}
                  onChange={this._handleNewPhoneNumberChange}
                  onKeyDown={this._handlePhoneNumberKeyDown}
                  placeholder='Enter phone number'
                  readOnly={!isEditing}
                  type='tel'
                  value={newPhoneNumber}
                />
                <Button
                  bsStyle='primary'
                  disabled={isPhoneInvalid}
                  onClick={this._handleRequestCode}
                >
                  Send verification text
                </Button>
                { // Show cancel button only if a phone number is already recorded.
                  currentNumber && <Button onClick={this._handleCancelEditNumber}>Cancel</Button>}
                {isPhoneInvalid && <HelpBlock>{INVALID_PHONE_MSG}</HelpBlock>}
              </ControlStrip>
            </FormGroup>
          ) : (
            <FormGroup>
              <ControlLabel>SMS notifications will be sent to:</ControlLabel>
              <ControlStrip>
                <InlineStatic>
                  {isPending
                    // eslint-disable-next-line jsx-a11y/label-has-for
                    ? <>{formatPhoneNumber(newPhoneNumber)} Pending</>
                    // eslint-disable-next-line jsx-a11y/label-has-for
                    : <>{formatPhoneNumber(currentNumber)} {currentNumberVerified && Verified}</>
                  }
                </InlineStatic>
                <Button onClick={this._handleEditNumber}>Change number</Button>
              </ControlStrip>
            </FormGroup>
          )}

        {isPending && (
          <FormGroup validationState={codeErrorState}>

              Please check the SMS messaging app on your mobile phone
              for a text message with a verification code, and enter the code below.

            <ControlLabel>Verification code:</ControlLabel>
            <ControlStrip>
              <Field as={InlineTextInput}
                name='validationCode'
                onKeyDown={this._handleValidationCodeKeyDown}
                placeholder='123456'
                // onBlur, onChange, and value are passed automatically by Formik
              />
              <Button
                bsStyle='primary'
                disabled={!!errors.validationCode}
                onClick={this._handleSubmitCode}
              >
                Verify
              </Button>
              {touched.validationCode && errors.validationCode && (
                <HelpBlock>{errors.validationCode}</HelpBlock>
              )}
            </ControlStrip>
            <FlushLink bsStyle='link' onClick={this._handleRequestCode}>Request a new code</FlushLink>
          </FormGroup>
        )}
      </>
    )
  }
}
<|endoftext|>


<commit_before>$(document).ready(function(){
  var createLink = function(root, path) {
    $(root).parent().append("skip ->");
    $(root).attr("processing", null);
  }

  var grabTree = function(root, current) {

    $.ajax({
      url: $(current).attr("href"),
      success: function(result) {
        objects = $('table.files tr td.content a', result);
        if(objects.length < 1)
        {
          objects = $('#browser table tr td.content a', result);
        }

        if(objects.length == 1)
        {
          grabTree(root, objects.first());
        }
        else if(root != current && ($(current).parent().parent().find('.icon span').hasClass('octicon-file-directory') || $(current).parent().parent().find('.icon img').attr("alt") == 'directory'))
        {
          createLink(root, $(current).attr("href"));
        }
      }
    });
  }

  var findLinks = function() {
    // GitHub
    $('table.files tr td.content a').each(function() {
      // Processing flag to make sure we don't traverse this tree more than once
      if($(this).parent().find('.skipper-link').length < 1 && $(this).attr("processing") != "1")
      {
        $(this).attr("processing", "1");
        grabTree(this, this);
      }
    });

    // GitHub:FI
    $('#browser table tr td.content a').each(function() {
      grabTree(this, this);
    });
  }

  $(this).bind('DOMNodeInserted', findLinks);
  findLinks();
});
<commit_msg>Only use DOM refresh magic on GitHub.com<commit_after>$(document).ready(function(){
  var createLink = function(root, path) {
    $(root).parent().append("skip ->");
    $(root).attr("processing", null);
  }

  var grabTree = function(root, current) {

    $.ajax({
      url: $(current).attr("href"),
      success: function(result) {
        objects = $('table.files tr td.content a', result);
        if(objects.length < 1)
        {
          objects = $('#browser table tr td.content a', result);
        }

        if(objects.length == 1)
        {
          grabTree(root, objects.first());
        }
        else if(root != current && ($(current).parent().parent().find('.icon span').hasClass('octicon-file-directory') || $(current).parent().parent().find('.icon img').attr("alt") == 'directory'))
        {
          createLink(root, $(current).attr("href"));
        }
      }
    });
  }

  var findLinks = function() {
    // GitHub
    $('table.files tr td.content a').each(function() {
      // Processing flag to make sure we don't traverse this tree more than once
      if($(this).parent().find('.skipper-link').length < 1 && $(this).attr("processing") != "1")
      {
        $(this).attr("processing", "1");
        grabTree(this, this);
      }
    });

    // GitHub:FI
    $('#browser table tr td.content a').each(function() {
      grabTree(this, this);
    });
  }

  if($('table.files').length > 0)
  {
    $(this).bind('DOMNodeInserted', findLinks);
  }

  findLinks();
});
<|endoftext|>