<commit_before>import Model from "./model";

/**
  @module ember-data
*/

var get = Ember.get;

/**
  @class Model
  @namespace DS
*/
Model.reopenClass({
  /**
    A map whose keys are the attributes of the model (properties
    described by DS.attr) and whose values are the meta object for the
    property.

    Example

    ```javascript

    App.Person = DS.Model.extend({
      firstName: attr('string'),
      lastName: attr('string'),
      birthday: attr('date')
    });

    var attributes = Ember.get(App.Person, 'attributes')

    attributes.forEach(function(name, meta) {
      console.log(name, meta);
    });

    // prints:
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
    ```

    @property attributes
    @static
    @type {Ember.Map}
    @readOnly
  */
  attributes: Ember.computed(function() {
    var map = Ember.Map.create();

    this.eachComputedProperty(function(name, meta) {
      if (meta.isAttribute) {
        Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.toString(), name !== 'id');

        meta.name = name;
        map.set(name, meta);
      }
    });

    return map;
  }),

  /**
    A map whose keys are the attributes of the model (properties
    described by DS.attr) and whose values are type of transformation
    applied to each attribute. This map does not include any
    attributes that do not have an transformation type.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr(),
      lastName: attr('string'),
      birthday: attr('date')
    });

    var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')

    transformedAttributes.forEach(function(field, type) {
      console.log(field, type);
    });

    // prints:
    // lastName string
    // birthday date
    ```

    @property transformedAttributes
    @static
    @type {Ember.Map}
    @readOnly
  */
  transformedAttributes: Ember.computed(function() {
    var map = Ember.Map.create();

    this.eachAttribute(function(key, meta) {
      if (meta.type) {
        map.set(key, meta.type);
      }
    });

    return map;
  }),

  /**
    Iterates through the attributes of the model, calling the passed function on each
    attribute.

    The callback method you provide should have the following signature (all
    parameters are optional):

    ```javascript
    function(name, meta);
    ```

    - `name` the name of the current property in the iteration
    - `meta` the meta object for the attribute property in the iteration

    Note that in addition to a callback, you can also pass an optional target
    object that will be set as `this` on the context.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr('string'),
      lastName: attr('string'),
      birthday: attr('date')
    });

    App.Person.eachAttribute(function(name, meta) {
      console.log(name, meta);
    });

    // prints:
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
   ```

    @method eachAttribute
    @param {Function} callback The callback to execute
    @param {Object} [target] The target object to use
    @static
  */
  eachAttribute: function(callback, binding) {
    get(this, 'attributes').forEach(function(name, meta) {
      callback.call(binding, name, meta);
    }, binding);
  },

  /**
    Iterates through the transformedAttributes of the model, calling
    the passed function on each attribute. Note the callback will not be
    called for any attributes that do not have an transformation type.

    The callback method you provide should have the following signature (all
    parameters are optional):

    ```javascript
    function(name, type);
    ```

    - `name` the name of the current property in the iteration
    - `type` a string containing the name of the type of transformed
      applied to the attribute

    Note that in addition to a callback, you can also pass an optional target
    object that will be set as `this` on the context.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr(),
      lastName: attr('string'),
      birthday: attr('date')
    });

    App.Person.eachTransformedAttribute(function(name, type) {
      console.log(name, type);
    });

    // prints:
    // lastName string
    // birthday date
   ```

    @method eachTransformedAttribute
    @param {Function} callback The callback to execute
    @param {Object} [target] The target object to use
    @static
  */
  eachTransformedAttribute: function(callback, binding) {
    get(this, 'transformedAttributes').forEach(function(name, type) {
      callback.call(binding, name, type);
    });
  }
});

Model.reopen({
  eachAttribute: function(callback, binding) {
    this.constructor.eachAttribute(callback, binding);
  }
});

function getDefaultValue(record, options, key) {
  if (typeof options.defaultValue === "function") {
    return options.defaultValue.apply(null, arguments);
  } else {
    return options.defaultValue;
  }
}

function hasValue(record, key) {
  return record._attributes.hasOwnProperty(key) ||
         record._inFlightAttributes.hasOwnProperty(key) ||
         record._data.hasOwnProperty(key);
}

function getValue(record, key) {
  if (record._attributes.hasOwnProperty(key)) {
    return record._attributes[key];
  } else if (record._inFlightAttributes.hasOwnProperty(key)) {
    return record._inFlightAttributes[key];
  } else {
    return record._data[key];
  }
}

/**
  `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html).
  By default, attributes are passed through as-is, however you can specify an
  optional type to have the value automatically transformed.
  Ember Data ships with four basic transform types: `string`, `number`,
  `boolean` and `date`. You can define your own transforms by subclassing
  [DS.Transform](/api/data/classes/DS.Transform.html).

  `DS.attr` takes an optional hash as a second parameter, currently
  supported options are:

  - `defaultValue`: Pass a string or a function to be called to set the attribute
                    to a default value if none is supplied.

  Example

  ```javascript
  var attr = DS.attr;

  App.User = DS.Model.extend({
    username: attr('string'),
    email: attr('string'),
    verified: attr('boolean', {defaultValue: false})
  });
  ```

  @namespace
  @method attr
  @for DS
  @param {String} type the attribute type
  @param {Object} options a hash of options
  @return {Attribute}
*/

function attr(type, options) {
  options = options || {};

  var meta = {
    type: type,
    isAttribute: true,
    options: options
  };

  return Ember.computed('data', function(key, value) {
    if (arguments.length > 1) {
      Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id');
      var oldValue = getValue(this, key);

      if (value !== oldValue) {
        // Add the new value to the changed attributes hash; it will get deleted by
        // the 'didSetProperty' handler if it is no different from the original value
        this._attributes[key] = value;

        this.send('didSetProperty', {
          name: key,
          oldValue: oldValue,
          originalValue: this._data[key],
          value: value
        });
      }

      return value;
    } else if (hasValue(this, key)) {
      return getValue(this, key);
    } else {
      return getDefaultValue(this, options, key);
    }

  // `data` is never set directly. However, it may be
  // invalidated from the state manager's setData
  // event.
  }).meta(meta);
}

export default attr;
<commit_msg>Documents invalid use of `attr` for attribute of `id`<commit_after>import Model from "./model";

/**
  @module ember-data
*/

var get = Ember.get;

/**
  @class Model
  @namespace DS
*/
Model.reopenClass({
  /**
    A map whose keys are the attributes of the model (properties
    described by DS.attr) and whose values are the meta object for the
    property.

    Example

    ```javascript

    App.Person = DS.Model.extend({
      firstName: attr('string'),
      lastName: attr('string'),
      birthday: attr('date')
    });

    var attributes = Ember.get(App.Person, 'attributes')

    attributes.forEach(function(name, meta) {
      console.log(name, meta);
    });

    // prints:
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
    ```

    @property attributes
    @static
    @type {Ember.Map}
    @readOnly
  */
  attributes: Ember.computed(function() {
    var map = Ember.Map.create();

    this.eachComputedProperty(function(name, meta) {
      if (meta.isAttribute) {
        Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.toString(), name !== 'id');

        meta.name = name;
        map.set(name, meta);
      }
    });

    return map;
  }),

  /**
    A map whose keys are the attributes of the model (properties
    described by DS.attr) and whose values are type of transformation
    applied to each attribute. This map does not include any
    attributes that do not have an transformation type.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr(),
      lastName: attr('string'),
      birthday: attr('date')
    });

    var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')

    transformedAttributes.forEach(function(field, type) {
      console.log(field, type);
    });

    // prints:
    // lastName string
    // birthday date
    ```

    @property transformedAttributes
    @static
    @type {Ember.Map}
    @readOnly
  */
  transformedAttributes: Ember.computed(function() {
    var map = Ember.Map.create();

    this.eachAttribute(function(key, meta) {
      if (meta.type) {
        map.set(key, meta.type);
      }
    });

    return map;
  }),

  /**
    Iterates through the attributes of the model, calling the passed function on each
    attribute.

    The callback method you provide should have the following signature (all
    parameters are optional):

    ```javascript
    function(name, meta);
    ```

    - `name` the name of the current property in the iteration
    - `meta` the meta object for the attribute property in the iteration

    Note that in addition to a callback, you can also pass an optional target
    object that will be set as `this` on the context.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr('string'),
      lastName: attr('string'),
      birthday: attr('date')
    });

    App.Person.eachAttribute(function(name, meta) {
      console.log(name, meta);
    });

    // prints:
    // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
    // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
    // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
   ```

    @method eachAttribute
    @param {Function} callback The callback to execute
    @param {Object} [target] The target object to use
    @static
  */
  eachAttribute: function(callback, binding) {
    get(this, 'attributes').forEach(function(name, meta) {
      callback.call(binding, name, meta);
    }, binding);
  },

  /**
    Iterates through the transformedAttributes of the model, calling
    the passed function on each attribute. Note the callback will not be
    called for any attributes that do not have an transformation type.

    The callback method you provide should have the following signature (all
    parameters are optional):

    ```javascript
    function(name, type);
    ```

    - `name` the name of the current property in the iteration
    - `type` a string containing the name of the type of transformed
      applied to the attribute

    Note that in addition to a callback, you can also pass an optional target
    object that will be set as `this` on the context.

    Example

    ```javascript
    App.Person = DS.Model.extend({
      firstName: attr(),
      lastName: attr('string'),
      birthday: attr('date')
    });

    App.Person.eachTransformedAttribute(function(name, type) {
      console.log(name, type);
    });

    // prints:
    // lastName string
    // birthday date
   ```

    @method eachTransformedAttribute
    @param {Function} callback The callback to execute
    @param {Object} [target] The target object to use
    @static
  */
  eachTransformedAttribute: function(callback, binding) {
    get(this, 'transformedAttributes').forEach(function(name, type) {
      callback.call(binding, name, type);
    });
  }
});

Model.reopen({
  eachAttribute: function(callback, binding) {
    this.constructor.eachAttribute(callback, binding);
  }
});

function getDefaultValue(record, options, key) {
  if (typeof options.defaultValue === "function") {
    return options.defaultValue.apply(null, arguments);
  } else {
    return options.defaultValue;
  }
}

function hasValue(record, key) {
  return record._attributes.hasOwnProperty(key) ||
         record._inFlightAttributes.hasOwnProperty(key) ||
         record._data.hasOwnProperty(key);
}

function getValue(record, key) {
  if (record._attributes.hasOwnProperty(key)) {
    return record._attributes[key];
  } else if (record._inFlightAttributes.hasOwnProperty(key)) {
    return record._inFlightAttributes[key];
  } else {
    return record._data[key];
  }
}

/**
  `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html).
  By default, attributes are passed through as-is, however you can specify an
  optional type to have the value automatically transformed.
  Ember Data ships with four basic transform types: `string`, `number`,
  `boolean` and `date`. You can define your own transforms by subclassing
  [DS.Transform](/api/data/classes/DS.Transform.html).

  Note that you cannot use `attr` to define an attribute of `id`.

  `DS.attr` takes an optional hash as a second parameter, currently
  supported options are:

  - `defaultValue`: Pass a string or a function to be called to set the attribute
                    to a default value if none is supplied.

  Example

  ```javascript
  var attr = DS.attr;

  App.User = DS.Model.extend({
    username: attr('string'),
    email: attr('string'),
    verified: attr('boolean', {defaultValue: false})
  });
  ```

  @namespace
  @method attr
  @for DS
  @param {String} type the attribute type
  @param {Object} options a hash of options
  @return {Attribute}
*/

function attr(type, options) {
  options = options || {};

  var meta = {
    type: type,
    isAttribute: true,
    options: options
  };

  return Ember.computed('data', function(key, value) {
    if (arguments.length > 1) {
      Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('<type>')` from " + this.constructor.toString(), key !== 'id');
      var oldValue = getValue(this, key);

      if (value !== oldValue) {
        // Add the new value to the changed attributes hash; it will get deleted by
        // the 'didSetProperty' handler if it is no different from the original value
        this._attributes[key] = value;

        this.send('didSetProperty', {
          name: key,
          oldValue: oldValue,
          originalValue: this._data[key],
          value: value
        });
      }

      return value;
    } else if (hasValue(this, key)) {
      return getValue(this, key);
    } else {
      return getDefaultValue(this, options, key);
    }

  // `data` is never set directly. However, it may be
  // invalidated from the state manager's setData
  // event.
  }).meta(meta);
}

export default attr;
<|endoftext|>


<commit_before>import React from 'react'
import { Price, CurrentPrice, OldPrice } from 'fyndiq-component-price'
import { Stars } from 'fyndiq-component-stars'
import styles from '../styles.less'

const Productcard = ({ price, title, url, oldprice, rating }) => (

    <Price>
      <OldPrice>{ oldprice }</OldPrice>
      <CurrentPrice>{ price }</CurrentPrice>
    </Price>

{ title }

    <Stars rating={ rating }></Stars>

)

export default Productcard
<commit_msg>Rating on product card<commit_after>import React from 'react'
import { Price, CurrentPrice, OldPrice } from 'fyndiq-component-price'
import Stars from 'fyndiq-component-stars'
import styles from '../styles.less'

const Productcard = ({ price, title, url, oldprice, rating }) => (

    <Price>
      <OldPrice>{ oldprice }</OldPrice>
      <CurrentPrice>{ price }</CurrentPrice>
    </Price>

{ title }

    <Stars rating={ rating }></Stars>

)

export default Productcard
<|endoftext|>


<commit_before>'use strict';

(function() {

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', []).

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$localStorage', _storageFactory('localStorage')).

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$browser',
            '$window',

            function(
                $rootScope,
                $browser,
                $window
            ){
                var webStorage = $window[storageType],
                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || delete $storage[k];
                            }

                            return $storage.$default(items);
                        }
                    },
                    _last$storage;

                // (#8) `i < webStorage.length` is needed for IE9
                for (var i = 0, k; i < webStorage.length && (k = webStorage.key(i)); i++) {
                    'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                }

                _last$storage = angular.copy($storage);

                $browser.addPollFn(function() {
                    if (!angular.equals($storage, _last$storage)) {
                        angular.forEach($storage, function(v, k) {
                            if (angular.isDefined(v) && '$' !== k[0]) {

                                // Remove $$hashKey and other things that cannot be stringified
                                $storage[k] = angular.fromJson(angular.toJson(v));

                                webStorage.setItem('ngStorage-' + k, angular.toJson(v));
                            }

                            delete _last$storage[k];
                        });

                        for (var k in _last$storage) {
                            webStorage.removeItem('ngStorage-' + k);
                        }

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                // (#6) Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

})();
<commit_msg>Fix #9: Better handling when Web Storage is not available<commit_after>'use strict';

(function() {

    /**
     * @ngdoc overview
     * @name ngStorage
     */

    angular.module('ngStorage', []).

    /**
     * @ngdoc object
     * @name ngStorage.$localStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$localStorage', _storageFactory('localStorage')).

    /**
     * @ngdoc object
     * @name ngStorage.$sessionStorage
     * @requires $rootScope
     * @requires $browser
     * @requires $window
     */

    factory('$sessionStorage', _storageFactory('sessionStorage'));

    function _storageFactory(storageType) {
        return [
            '$rootScope',
            '$browser',
            '$window',

            function(
                $rootScope,
                $browser,
                $window
            ){
                var webStorage = $window[storageType] || (console.warn('This browser does not support Web Storage!'), {}),

                    $storage = {
                        $default: function(items) {
                            for (var k in items) {
                                angular.isDefined($storage[k]) || ($storage[k] = items[k]);
                            }

                            return $storage;
                        },
                        $reset: function(items) {
                            for (var k in $storage) {
                                '$' === k[0] || delete $storage[k];
                            }

                            return $storage.$default(items);
                        }
                    },

                    _last$storage;

                // (#8) `i < webStorage.length` is needed for IE9
                for (var i = 0, k; i < webStorage.length && (k = webStorage.key(i)); i++) {
                    'ngStorage-' === k.slice(0, 10) && ($storage[k.slice(10)] = angular.fromJson(webStorage.getItem(k)));
                }

                _last$storage = angular.copy($storage);

                $browser.addPollFn(function() {
                    if (!angular.equals($storage, _last$storage)) {
                        angular.forEach($storage, function(v, k) {
                            if (angular.isDefined(v) && '$' !== k[0]) {

                                // Remove $$hashKey and other things that cannot be stringified
                                $storage[k] = angular.fromJson(angular.toJson(v));

                                webStorage.setItem('ngStorage-' + k, angular.toJson(v));
                            }

                            delete _last$storage[k];
                        });

                        for (var k in _last$storage) {
                            webStorage.removeItem('ngStorage-' + k);
                        }

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                // (#6) Use `$window.addEventListener` instead of `angular.element` to avoid the jQuery-specific `event.originalEvent`
                'localStorage' === storageType && $window.addEventListener('storage', function(event) {
                    if ('ngStorage-' === event.key.slice(0, 10)) {
                        event.newValue ? $storage[event.key.slice(10)] = angular.fromJson(event.newValue) : delete $storage[event.key.slice(10)];

                        _last$storage = angular.copy($storage);

                        $rootScope.$apply();
                    }
                });

                return $storage;
            }
        ];
    }

})();
<|endoftext|>


<commit_before><commit_msg>fix(gatsby-source-contentful): Remove whitespace from default long text value (#21412)<commit_after><|endoftext|>


<commit_before>( function( tiy, $, moment ) {
    'use strict';

    tiy.loadModule( {
        name: 'gradebook',
        navIcon: 'fa-book',
        render: main
    } );

    let uiBuilt = false;

    const TABLE_TEMPLATE = `

        <button type="button" class="btn btn-secondary btn-sm" id="generate-score-card">
           Refresh Grades
        </button>

          as of
          Never

      `;

    const SHORT_GRADE_NAMES = {
        'Exceeds expectations': 'E',
        'Complete and satisfactory': 'S',
        'Complete and unsatisfactory': 'U',
        'Incomplete': 'I',
        'Not graded': 'G',
        'Retracted': 'R'
    };

    const OK_GRADES = [
        'Complete and satisfactory',
        'Exceeds expectations',
        'Not graded'
    ];

    const IGNORED_GRADES = [
        'Retracted'
    ];

    const PARSING_TIME_FORMAT = "MMM DD, YYYY hh:mm A";
    const slice = c => [].slice.call( c );
    const qs = ( el, s ) => el.querySelector( s );
    const qsa = ( el, s ) => slice( el.querySelectorAll( s ) );

    // Behavior ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    function main( sessionData, $el ) {
        $( $el ).on( 'showing', function() {
            show( sessionData, $el );
        } );
    }

    function loadCachedGradebooks() {
      let gradebooks;
      try {
          gradebooks = JSON.parse(
              localStorage.getItem( 'cachedGradeBookData' )
          );

      } catch ( e ) {}
      gradebooks = gradebooks || {};
      return gradebooks;
    }

    function loadCachedSession() {
      let session;
      try {
          session = JSON.parse(
              localStorage.getItem( 'cachedGradeBookDataSession' )
          );
      } catch ( e ) {}
      session = session || { path: null };
      return session;
    }

    function show( sessionData, $el ) {
        if ( uiBuilt ) {
            return;
        }

        let cachedSession = loadCachedSession();
        console.log(sessionData, cachedSession);
        if ( sessionData.path === null && cachedSession.path === null ) {
            // Provide feedback to the user that we can't handle this.
            $el.text( 'Have you visited a path recently, we don\'t know what gradebook you want to see, go to a path you own and try again?!' );
            return;
        } else if ( sessionData.path === null && cachedSession.path !== null  ) {
          sessionData = cachedSession;
        } else {
          localStorage.setItem( 'cachedGradeBookDataSession', JSON.stringify(sessionData) );
        }

        let gradebooks = loadCachedGradebooks();
        let gradebook_data = gradebooks[ sessionData.path.id ];

        resetUI( sessionData, $el );

        if ( gradebook_data ) {
            try {
                buildUI(
                    sessionData,
                    $el,
                    gradebook_data
                );
            } catch ( e ) {
                localStorage.removeItem( 'cachedGradeBookData' );
            }
        } else {
            getGradebook( sessionData, $el );
        }

        uiBuilt = true;
    }

    function getGradebook( sessionData, $el ) {
        console.info( "DDOSsing  TIYO for path: ", sessionData.path.id );

        $( '#generate-score-card' ).text( "Processing" ).attr( "disabled", true );

        try {
            scrape( sessionData, function( gradebook ) {
                resetUI( sessionData, $el );
                buildUI( sessionData, $el, gradebook );
            } );
        } catch ( e ) {
            console.warn( "it blewup", e );
            // Wrap in try catch to show UI to user that something went wrong ( user permissions? )
            $el.find( '.tiyo-assistant-notice' ).text( 'There was a problem getting all the data for this gradebooks, do you own this path?!' );
        }
    }

    // UI ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    function resetUI( sessionData, $el ) {
        $el.html( '' );
        $el.append( TABLE_TEMPLATE );

        $( '#generate-score-card' ).click( function() {
            getGradebook( sessionData, $el );
        } );
    }

    function buildUI( sessionData, $el, gradebook ) {
      let students = gradebook.students;
      let assignments = gradebook.assignments;
        const $table = $el.find( 'table' );
        $table.find( 'thead' )
            .append( buildAssignmentsHeader( assignments ) );

        const orderedStudents = Object.keys( students ).sort();
        for ( let studentId of orderedStudents ) {
            // Never display the instructor in the gradebook
            if ( sessionData.user.user_id.toString() === studentId ) {
                continue;
            }

            let student = students[ studentId ];
            $table.append( buildStudentRow( student, assignments ) );
        }

        $el.find('#path-title').html(
          `
          ${gradebook.title}
          `);
        $el.find('#last-scraped').text(moment(gradebook.scraped_at).fromNow());

    }

    function buildAssignmentsHeader( assignments ) {
        const row = $( '
' );
        row.append( $( '' ).append( "Student" ) );
        row.append( $( '' ).append( "Grade" ) );

        for ( let assignment of assignments ) {
            row.append( `

                  ${assignment.title.slice( 0, 1 )}

            ` );
        }

        return row;
    }

    function buildStudentRow( student, assignments ) {
        const studentRow = $( '
' );

        studentRow.append(
            `

                ${student.name}

            `
        );

        studentRow.append(
            `<td>${student.percentage}%</td>`
        );

        for ( let assignment of assignments ) {
            let submission = student.submissions[ assignment.id ];

            if ( submission ) {
                let gradeClass = SHORT_GRADE_NAMES[ submission.grade ].toLowerCase();
                studentRow.append(
                    `

                      ${SHORT_GRADE_NAMES[ submission.grade ]}

                  `
                );
            } else {
                studentRow.append( '	' );
            }
        }

        return studentRow;
    }

    // Data Management +++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    let idFromUrl = uri => Number( uri.substr( uri.lastIndexOf( '/' ) + 1 ) );

    function calculateGrades( students, assignments ) {
        if ( !students ) {
            return;
        }

        // Question, What is the the best practice for building an uniq array
        // of objects in JS.
        Object.keys( students ).map( ( studentName ) => {
            var student = students[ studentName ];

            var submissions = Object.keys( student.submissions );

            var okCount = submissions.filter( assignmentId => {
                return OK_GRADES.includes( student.submissions[ assignmentId ].grade );
            } );

            var grade = okCount.length / assignments.length * 100;
            student.percentage = grade.toFixed( 0 );
        } );
        return students;
    }

    function getPathContent(path) {
      const pathURI = id => `https://online.theironyard.com/admin/paths/${ id }`;

      return new Promise( ( res, rej ) => {
        if (/\/admin\/paths\/[0-9]+/.test(window.location.pathname)) {
          path.content = scrapePathContent(document)
          res(path);
        } else {
          $.get( pathURI( path.id ) ).then( html => {
            var dom = document.createElement( 'html' );
            dom.innerHTML = html;
            path.content = scrapePathContent(dom)
            res( path );
          } ).fail( err => rej(err));
        }
      } );
    }

    function scrapePathContent(dom) {
      let allContent = {
        assignments: [],
        lessons: [],
        units: []
      };

      qsa( dom, '.path-tree-level' ).forEach( x => {
        // Anything that has a GID has been persisted by Rails.
        let gid = x.getAttribute('data-id');

        if (gid) {
          let title = qs( x, "a.text-body" ).innerText;
          let href = qs( x, "a.text-body" ).getAttribute('href');
          let hidden = qs( x, "#hidden-state, #unit-hidden-state" ).getAttribute("checked");
          let type = gid.split("/")[3]
          let id = gid.split("/")[4]

          let content = {
            id: Number(id),
            type: type,
            title: title,
            href: href,
            hidden_state: !!hidden,
            gid: gid,
            first_submission_at: null
          };

          if (type === 'Assignment') {
            allContent.assignments.push(content);
          } else if (type === 'Lesson') {
            allContent.lessons.push(content);
          } else if (type === 'Unit') {
            allContent.units.push(content);
          }
        }
      } );
      console.log("Path Content:", allContent);
      return allContent;
    }

    function scrape( sessionData, callback ) {
        const userURI = id => `https://online.theironyard.com/admin/users/${id}`;

        function scrapeStudentPage( students, assignments, url, html ) {
            let studentPage = document.createElement( 'html' );
            studentPage.innerHTML = html;

            let name = qs( studentPage, 'h1 strong' ).innerText;
            let studentId = idFromUrl( url );

            students[ studentId ] = {
                id: studentId,
                name: name,
                percentage: null,
                submissions: {}
            };

            qsa( studentPage, '#assignments table tbody tr' ).map( row => {

                let studentSubmissionPath = qs( qsa( row, 'td' )[ 1 ], 'a' ).getAttribute( 'href' );
                let assignmentPath = qs( row, 'td a' ).getAttribute( 'href' );
                let submission = {
                    id: idFromUrl( studentSubmissionPath ),
                    grade: qs( qsa( row, 'td' )[ 2 ], 'label' ).innerText.trim(),
                    href: studentSubmissionPath,
                    submitted_at: moment( qsa( row, 'td' )[ 3 ].innerText.trim(), PARSING_TIME_FORMAT ),
                    assignment: null
                };

                let assignmentId = idFromUrl( assignmentPath );
                let assignment = assignments.find( function( a ) {
                    return a.id === assignmentId;
                } );

                if ( assignment ) {
                    if (assignment.first_submission_at === null || submission.submitted_at < assignment.first_submission_at ) {
                        assignment.first_submission_at = submission.submitted_at;
                    }

                  if ( !IGNORED_GRADES.includes( submission.grade ) ) {
                      submission.assignment = assignment;
                      let existingSubmission = students[ studentId ].submissions[ assignment.id ];
                      if ( !( existingSubmission && existingSubmission.submitted_at > submission.submitted_at ) ) {
                        students[ studentId ].submissions[ assignment.id ] = submission;
                      }
                  }
                } else {
                  console.log("Submission from other path", submission, students[ studentId ])
                }
            } );

        }
        Promise.all( [getPathContent(sessionData.path)] ).then( ([pathWithContent]) => {
          sessionData.path = pathWithContent;

          Promise.all( [sessionData.students] ).then( ( [ s ] ) => {
              let students = {};
              let assignments = sessionData.path.content.assignments;

              Promise.all( s.map( s => new Promise( ( res ) => {
                  $.get( userURI(s.id) ).then( html => res( scrapeStudentPage( students, assignments, userURI(s.id), html ) ) );
              } ) ) ).then( () => {

                  // Reject any assingments that have nothing turned in?
                  // Thoughts, this could use hidden state?
                  let submittedAssingments = assignments.filter( function( el ) {
                      return el.first_submission_at !== null;
                  } );

                  submittedAssingments.sort( function( a, b ) {
                      return new Date( b.first_submission_at ) - new Date( a.first_submission_at );
                  } );

                  students = calculateGrades( students, submittedAssingments );
                  const gradebook = {
                      id: sessionData.path.id,
                      title: sessionData.path.title,
                      students: students,
                      assignments: submittedAssingments,
                      scraped_at:  moment()
                  };
                  let gradebooks = loadCachedGradebooks();

                  gradebooks[sessionData.path.id] = gradebook;

                  localStorage.setItem( 'cachedGradeBookData', JSON.stringify( gradebooks ) );
                  callback( gradebook );
              } );
          } );
        });
    }

} )( window.tiy || {}, window.jQuery, window.moment );
<commit_msg>Update column headers to include hidden full assignment name<commit_after>( function( tiy, $, moment ) {
    'use strict';

    tiy.loadModule( {
        name: 'gradebook',
        navIcon: 'fa-book',
        render: main
    } );

    let uiBuilt = false;

    const TABLE_TEMPLATE = `

        <button type="button" class="btn btn-secondary btn-sm" id="generate-score-card">
           Refresh Grades
        </button>

          as of
          Never

      `;

    const SHORT_GRADE_NAMES = {
        'Exceeds expectations': 'E',
        'Complete and satisfactory': 'S',
        'Complete and unsatisfactory': 'U',
        'Incomplete': 'I',
        'Not graded': 'G',
        'Retracted': 'R'
    };

    const OK_GRADES = [
        'Complete and satisfactory',
        'Exceeds expectations',
        'Not graded'
    ];

    const IGNORED_GRADES = [
        'Retracted'
    ];

    const PARSING_TIME_FORMAT = "MMM DD, YYYY hh:mm A";
    const slice = c => [].slice.call( c );
    const qs = ( el, s ) => el.querySelector( s );
    const qsa = ( el, s ) => slice( el.querySelectorAll( s ) );

    // Behavior ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    function main( sessionData, $el ) {
        $( $el ).on( 'showing', function() {
            show( sessionData, $el );
        } );
    }

    function loadCachedGradebooks() {
      let gradebooks;
      try {
          gradebooks = JSON.parse(
              localStorage.getItem( 'cachedGradeBookData' )
          );

      } catch ( e ) {}
      gradebooks = gradebooks || {};
      return gradebooks;
    }

    function loadCachedSession() {
      let session;
      try {
          session = JSON.parse(
              localStorage.getItem( 'cachedGradeBookDataSession' )
          );
      } catch ( e ) {}
      session = session || { path: null };
      return session;
    }

    function show( sessionData, $el ) {
        if ( uiBuilt ) {
            return;
        }

        let cachedSession = loadCachedSession();
        console.log(sessionData, cachedSession);
        if ( sessionData.path === null && cachedSession.path === null ) {
            // Provide feedback to the user that we can't handle this.
            $el.text( 'Have you visited a path recently, we don\'t know what gradebook you want to see, go to a path you own and try again?!' );
            return;
        } else if ( sessionData.path === null && cachedSession.path !== null  ) {
          sessionData = cachedSession;
        } else {
          localStorage.setItem( 'cachedGradeBookDataSession', JSON.stringify(sessionData) );
        }

        let gradebooks = loadCachedGradebooks();
        let gradebook_data = gradebooks[ sessionData.path.id ];

        resetUI( sessionData, $el );

        if ( gradebook_data ) {
            try {
                buildUI(
                    sessionData,
                    $el,
                    gradebook_data
                );
            } catch ( e ) {
                localStorage.removeItem( 'cachedGradeBookData' );
            }
        } else {
            getGradebook( sessionData, $el );
        }

        uiBuilt = true;
    }

    function getGradebook( sessionData, $el ) {
        console.info( "DDOSsing  TIYO for path: ", sessionData.path.id );

        $( '#generate-score-card' ).text( "Processing" ).attr( "disabled", true );

        try {
            scrape( sessionData, function( gradebook ) {
                resetUI( sessionData, $el );
                buildUI( sessionData, $el, gradebook );
            } );
        } catch ( e ) {
            console.warn( "it blewup", e );
            // Wrap in try catch to show UI to user that something went wrong ( user permissions? )
            $el.find( '.tiyo-assistant-notice' ).text( 'There was a problem getting all the data for this gradebooks, do you own this path?!' );
        }
    }

    // UI ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    function resetUI( sessionData, $el ) {
        $el.html( '' );
        $el.append( TABLE_TEMPLATE );

        $( '#generate-score-card' ).click( function() {
            getGradebook( sessionData, $el );
        } );
    }

    function buildUI( sessionData, $el, gradebook ) {
      let students = gradebook.students;
      let assignments = gradebook.assignments;
        const $table = $el.find( 'table' );
        $table.find( 'thead' )
            .append( buildAssignmentsHeader( assignments ) );

        const orderedStudents = Object.keys( students ).sort();
        for ( let studentId of orderedStudents ) {
            // Never display the instructor in the gradebook
            if ( sessionData.user.user_id.toString() === studentId ) {
                continue;
            }

            let student = students[ studentId ];
            $table.append( buildStudentRow( student, assignments ) );
        }

        $el.find('#path-title').html(
          `
          ${gradebook.title}
          `);
        $el.find('#last-scraped').text(moment(gradebook.scraped_at).fromNow());

    }

    function buildAssignmentsHeader( assignments ) {
        const row = $( '
' );
        row.append( $( '' ).append( "Student" ) );
        row.append( $( '' ).append( "Grade" ) );

        for ( let assignment of assignments ) {
            row.append( `

                  ${assignment.title}

            ` );
        }

        return row;
    }

    function buildStudentRow( student, assignments ) {
        const studentRow = $( '
' );

        studentRow.append(
            `

                ${student.name}

            `
        );

        studentRow.append(
            `<td>${student.percentage}%</td>`
        );

        for ( let assignment of assignments ) {
            let submission = student.submissions[ assignment.id ];

            if ( submission ) {
                let gradeClass = SHORT_GRADE_NAMES[ submission.grade ].toLowerCase();
                studentRow.append(
                    `

                      ${SHORT_GRADE_NAMES[ submission.grade ]}

                  `
                );
            } else {
                studentRow.append( '	' );
            }
        }

        return studentRow;
    }

    // Data Management +++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    let idFromUrl = uri => Number( uri.substr( uri.lastIndexOf( '/' ) + 1 ) );

    function calculateGrades( students, assignments ) {
        if ( !students ) {
            return;
        }

        // Question, What is the the best practice for building an uniq array
        // of objects in JS.
        Object.keys( students ).map( ( studentName ) => {
            var student = students[ studentName ];

            var submissions = Object.keys( student.submissions );

            var okCount = submissions.filter( assignmentId => {
                return OK_GRADES.includes( student.submissions[ assignmentId ].grade );
            } );

            var grade = okCount.length / assignments.length * 100;
            student.percentage = grade.toFixed( 0 );
        } );
        return students;
    }

    function getPathContent(path) {
      const pathURI = id => `https://online.theironyard.com/admin/paths/${ id }`;

      return new Promise( ( res, rej ) => {
        if (/\/admin\/paths\/[0-9]+/.test(window.location.pathname)) {
          path.content = scrapePathContent(document)
          res(path);
        } else {
          $.get( pathURI( path.id ) ).then( html => {
            var dom = document.createElement( 'html' );
            dom.innerHTML = html;
            path.content = scrapePathContent(dom)
            res( path );
          } ).fail( err => rej(err));
        }
      } );
    }

    function scrapePathContent(dom) {
      let allContent = {
        assignments: [],
        lessons: [],
        units: []
      };

      qsa( dom, '.path-tree-level' ).forEach( x => {
        // Anything that has a GID has been persisted by Rails.
        let gid = x.getAttribute('data-id');

        if (gid) {
          let title = qs( x, "a.text-body" ).innerText;
          let href = qs( x, "a.text-body" ).getAttribute('href');
          let hidden = qs( x, "#hidden-state, #unit-hidden-state" ).getAttribute("checked");
          let type = gid.split("/")[3]
          let id = gid.split("/")[4]

          let content = {
            id: Number(id),
            type: type,
            title: title,
            href: href,
            hidden_state: !!hidden,
            gid: gid,
            first_submission_at: null
          };

          if (type === 'Assignment') {
            allContent.assignments.push(content);
          } else if (type === 'Lesson') {
            allContent.lessons.push(content);
          } else if (type === 'Unit') {
            allContent.units.push(content);
          }
        }
      } );
      console.log("Path Content:", allContent);
      return allContent;
    }

    function scrape( sessionData, callback ) {
        const userURI = id => `https://online.theironyard.com/admin/users/${id}`;

        function scrapeStudentPage( students, assignments, url, html ) {
            let studentPage = document.createElement( 'html' );
            studentPage.innerHTML = html;

            let name = qs( studentPage, 'h1 strong' ).innerText;
            let studentId = idFromUrl( url );

            students[ studentId ] = {
                id: studentId,
                name: name,
                percentage: null,
                submissions: {}
            };

            qsa( studentPage, '#assignments table tbody tr' ).map( row => {

                let studentSubmissionPath = qs( qsa( row, 'td' )[ 1 ], 'a' ).getAttribute( 'href' );
                let assignmentPath = qs( row, 'td a' ).getAttribute( 'href' );
                let submission = {
                    id: idFromUrl( studentSubmissionPath ),
                    grade: qs( qsa( row, 'td' )[ 2 ], 'label' ).innerText.trim(),
                    href: studentSubmissionPath,
                    submitted_at: moment( qsa( row, 'td' )[ 3 ].innerText.trim(), PARSING_TIME_FORMAT ),
                    assignment: null
                };

                let assignmentId = idFromUrl( assignmentPath );
                let assignment = assignments.find( function( a ) {
                    return a.id === assignmentId;
                } );

                if ( assignment ) {
                    if (assignment.first_submission_at === null || submission.submitted_at < assignment.first_submission_at ) {
                        assignment.first_submission_at = submission.submitted_at;
                    }

                  if ( !IGNORED_GRADES.includes( submission.grade ) ) {
                      submission.assignment = assignment;
                      let existingSubmission = students[ studentId ].submissions[ assignment.id ];
                      if ( !( existingSubmission && existingSubmission.submitted_at > submission.submitted_at ) ) {
                        students[ studentId ].submissions[ assignment.id ] = submission;
                      }
                  }
                } else {
                  console.log("Submission from other path", submission, students[ studentId ])
                }
            } );

        }
        Promise.all( [getPathContent(sessionData.path)] ).then( ([pathWithContent]) => {
          sessionData.path = pathWithContent;

          Promise.all( [sessionData.students] ).then( ( [ s ] ) => {
              let students = {};
              let assignments = sessionData.path.content.assignments;

              Promise.all( s.map( s => new Promise( ( res ) => {
                  $.get( userURI(s.id) ).then( html => res( scrapeStudentPage( students, assignments, userURI(s.id), html ) ) );
              } ) ) ).then( () => {

                  // Reject any assingments that have nothing turned in?
                  // Thoughts, this could use hidden state?
                  let submittedAssingments = assignments.filter( function( el ) {
                      return el.first_submission_at !== null;
                  } );

                  submittedAssingments.sort( function( a, b ) {
                      return new Date( b.first_submission_at ) - new Date( a.first_submission_at );
                  } );

                  students = calculateGrades( students, submittedAssingments );
                  const gradebook = {
                      id: sessionData.path.id,
                      title: sessionData.path.title,
                      students: students,
                      assignments: submittedAssingments,
                      scraped_at:  moment()
                  };
                  let gradebooks = loadCachedGradebooks();

                  gradebooks[sessionData.path.id] = gradebook;

                  localStorage.setItem( 'cachedGradeBookData', JSON.stringify( gradebooks ) );
                  callback( gradebook );
              } );
          } );
        });
    }

} )( window.tiy || {}, window.jQuery, window.moment );
<|endoftext|>


<commit_before>'use strict'

let co = require('co')
let cli = require('heroku-cli-util')
let time = require('../lib/time')
let _ = require('lodash')

let empty = (o) => Object.keys(o).length === 0

function displayFormation (formation) {
  formation = _.groupBy(formation, 'size')
  formation = _.map(formation, (p, size) => `${bold(_.sumBy(p, 'quantity'))} | ${size}`)
  cli.log(`  ${label('Dynos:')} ${formation.join(', ')}`)
}

function displayErrors (metrics) {
  let errors = []
  if (metrics.routerErrors) {
    errors = errors.concat(_.toPairs(metrics.routerErrors.data).map((e) => cli.color.red(`${_.sum(e[1])} ${e[0]}`)))
  }
  if (metrics.dynoErrors) {
    metrics.dynoErrors.filter((d) => d).forEach((dynoErrors) => {
      errors = errors.concat(_.toPairs(dynoErrors.data).map((e) => cli.color.red(`${_.sum(e[1])} ${e[0]}`)))
    })
  }
  if (errors.length > 0) cli.log(`  ${label('Errors:')} ${errors.join(dim(', '))} (see details with ${cli.color.cmd('heroku apps:errors')})`)
}

function displayMetrics (metrics) {
  function rpmSparkline () {
    if (process.platform === 'win32') return
    let sparkline = require('sparkline')
    let points = []
    _.values(metrics.routerStatus.data).forEach((cur) => {
      for (let i = 0; i < cur.length; i++) {
        let j = Math.floor(i / 3)
        points[j] = (points[j] || 0) + cur[i]
      }
    })
    points.pop()
    return dim(sparkline(points)) + ' last 24 hours rpm'
  }
  let ms = ''
  let rpm = ''
  if (metrics.routerLatency && !empty(metrics.routerLatency.data)) {
    ms = _.pad(`${_.round(_.mean(metrics.routerLatency.data.latency_p50))} ms`, 6)
  }
  if (metrics.routerStatus && !empty(metrics.routerStatus.data)) {
    rpm = `${_.round(_.sum(_.flatten(_.values(metrics.routerStatus.data))) / 24 / 60)} rpm ${rpmSparkline()}`
  }
  if (rpm || ms) cli.log(`  ${label('Metrics:')} ${ms}${rpm}`)
}

function displayNotifications (notifications) {
  notifications = notifications.filter((n) => !n.read)
  if (notifications.length > 0) {
    cli.log(cli.color.yellow(`
You have ${notifications.length} unread notifications. Read them with ${cli.color.cmd('heroku notifications')}`))
  }
}

let dim = (s) => cli.color.gray.bold(s)
let bold = (s) => cli.color.bold(s)
let label = (s) => cli.color.blue(s)

function displayApps (apps, appsMetrics) {
  let owner = (owner) => owner.email.endsWith('@herokumanager.com') ? owner.email.split('@')[0] : owner.email

  for (let a of _.zip(apps, appsMetrics)) {
    cli.log()
    let app = a[0]
    let metrics = a[1]
    cli.log(cli.color.app(app.app.name))
    cli.log(`  ${label('Owner:')} ${owner(app.app.owner)}`)
    if (app.pipelin) {
      cli.log(`  ${label('Pipeline:')} ${app.pipeline.pipeline.name}`)
    }
    displayFormation(app.formation)
    cli.log(`  ${label('Last release:')} ${time.ago(new Date(app.app.released_at))}`)
    displayMetrics(metrics)
    displayErrors(metrics)
  }
}

function * run (context, heroku) {
  function favoriteApps () {
    return heroku.request({
      host: 'longboard.heroku.com',
      path: '/favorites',
      headers: {Range: ''}
    }).then((apps) => apps.map((app) => app.app_name))
  }

  function fetchMetrics (apps) {
    const NOW = new Date().toISOString()
    const YESTERDAY = new Date(new Date().getTime() - (24 * 60 * 60 * 1000)).toISOString()
    let date = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`
    return apps.map((app) => {
      let types = app.formation.map((p) => p.type)
      return {
        dynoErrors: types.map((type) => heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/formation/${type}/metrics/errors?${date}`, headers: {Range: ''}}).catch(() => null)),
        routerLatency: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/latency?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerErrors: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/errors?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerStatus: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/status?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null)
      }
    })
  }

  let apps, data, metrics

  yield cli.action('Loading', {success: false}, co(function * () {
    apps = yield favoriteApps()

    data = yield {
      orgs: heroku.request({path: '/organizations'}),
      notifications: heroku.request({host: 'telex.heroku.com', path: '/user/notifications'}),
      apps: apps.map((app) => ({
        app: heroku.get(`/apps/${app}`),
        formation: heroku.get(`/apps/${app}/formation`),
        pipeline: heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => null)
      }))
    }
    metrics = yield fetchMetrics(data.apps)
  }))

  process.stderr.write('\r')
  if (process.stderr.clearLine) process.stderr.clearLine()
  else cli.console.error('\n')

  if (apps.length > 0) displayApps(data.apps, metrics)
  else cli.warn(`Add apps to this dashboard by favoriting them with ${cli.color.cmd('heroku apps:favorites:add')}`)

  cli.log(`
See all add-ons with ${cli.color.cmd('heroku addons')}`)
  let sampleOrg = _.sortBy(data.orgs.filter((o) => o.role !== 'collaborator'), (o) => new Date(o.created_at))[0]
  if (sampleOrg) cli.log(`See all apps in ${cli.color.yellow.dim(sampleOrg.name)} with ${cli.color.cmd('heroku apps --org ' + sampleOrg.name)}`)
  cli.log(`See all apps with ${cli.color.cmd('heroku apps --all')}`)
  displayNotifications(data.notifications)
  cli.log(`
See other CLI commands with ${cli.color.cmd('heroku help')}
`)
}

module.exports = {
  topic: 'dashboard',
  description: 'display information about favorite apps',
  hidden: true,
  needsAuth: true,
  wantsOrg: true,
  run: cli.command(co.wrap(run))
}
<commit_msg>less yellow<commit_after>'use strict'

let co = require('co')
let cli = require('heroku-cli-util')
let time = require('../lib/time')
let _ = require('lodash')

let empty = (o) => Object.keys(o).length === 0

function displayFormation (formation) {
  formation = _.groupBy(formation, 'size')
  formation = _.map(formation, (p, size) => `${bold(_.sumBy(p, 'quantity'))} | ${size}`)
  cli.log(`  ${label('Dynos:')} ${formation.join(', ')}`)
}

function displayErrors (metrics) {
  let errors = []
  if (metrics.routerErrors) {
    errors = errors.concat(_.toPairs(metrics.routerErrors.data).map((e) => cli.color.red(`${_.sum(e[1])} ${e[0]}`)))
  }
  if (metrics.dynoErrors) {
    metrics.dynoErrors.filter((d) => d).forEach((dynoErrors) => {
      errors = errors.concat(_.toPairs(dynoErrors.data).map((e) => cli.color.red(`${_.sum(e[1])} ${e[0]}`)))
    })
  }
  if (errors.length > 0) cli.log(`  ${label('Errors:')} ${errors.join(dim(', '))} (see details with ${cli.color.cmd('heroku apps:errors')})`)
}

function displayMetrics (metrics) {
  function rpmSparkline () {
    if (process.platform === 'win32') return
    let sparkline = require('sparkline')
    let points = []
    _.values(metrics.routerStatus.data).forEach((cur) => {
      for (let i = 0; i < cur.length; i++) {
        let j = Math.floor(i / 3)
        points[j] = (points[j] || 0) + cur[i]
      }
    })
    points.pop()
    return dim(sparkline(points)) + ' last 24 hours rpm'
  }
  let ms = ''
  let rpm = ''
  if (metrics.routerLatency && !empty(metrics.routerLatency.data)) {
    ms = _.pad(`${_.round(_.mean(metrics.routerLatency.data.latency_p50))} ms`, 6)
  }
  if (metrics.routerStatus && !empty(metrics.routerStatus.data)) {
    rpm = `${_.round(_.sum(_.flatten(_.values(metrics.routerStatus.data))) / 24 / 60)} rpm ${rpmSparkline()}`
  }
  if (rpm || ms) cli.log(`  ${label('Metrics:')} ${ms}${rpm}`)
}

function displayNotifications (notifications) {
  notifications = notifications.filter((n) => !n.read)
  if (notifications.length > 0) {
    cli.log(`
You have ${cli.color.yellow(notifications.length)} unread notifications. Read them with ${cli.color.cmd('heroku notifications')}`)
  }
}

let dim = (s) => cli.color.gray.bold(s)
let bold = (s) => cli.color.bold(s)
let label = (s) => cli.color.blue(s)

function displayApps (apps, appsMetrics) {
  let owner = (owner) => owner.email.endsWith('@herokumanager.com') ? owner.email.split('@')[0] : owner.email

  for (let a of _.zip(apps, appsMetrics)) {
    cli.log()
    let app = a[0]
    let metrics = a[1]
    cli.log(cli.color.app(app.app.name))
    cli.log(`  ${label('Owner:')} ${owner(app.app.owner)}`)
    if (app.pipelin) {
      cli.log(`  ${label('Pipeline:')} ${app.pipeline.pipeline.name}`)
    }
    displayFormation(app.formation)
    cli.log(`  ${label('Last release:')} ${time.ago(new Date(app.app.released_at))}`)
    displayMetrics(metrics)
    displayErrors(metrics)
  }
}

function * run (context, heroku) {
  function favoriteApps () {
    return heroku.request({
      host: 'longboard.heroku.com',
      path: '/favorites',
      headers: {Range: ''}
    }).then((apps) => apps.map((app) => app.app_name))
  }

  function fetchMetrics (apps) {
    const NOW = new Date().toISOString()
    const YESTERDAY = new Date(new Date().getTime() - (24 * 60 * 60 * 1000)).toISOString()
    let date = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`
    return apps.map((app) => {
      let types = app.formation.map((p) => p.type)
      return {
        dynoErrors: types.map((type) => heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/formation/${type}/metrics/errors?${date}`, headers: {Range: ''}}).catch(() => null)),
        routerLatency: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/latency?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerErrors: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/errors?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null),
        routerStatus: heroku.request({host: 'api.metrics.herokai.com', path: `/apps/${app.app.name}/router-metrics/status?${date}&process_type=${types[0]}`, headers: {Range: ''}}).catch(() => null)
      }
    })
  }

  let apps, data, metrics

  yield cli.action('Loading', {success: false}, co(function * () {
    apps = yield favoriteApps()

    data = yield {
      orgs: heroku.request({path: '/organizations'}),
      notifications: heroku.request({host: 'telex.heroku.com', path: '/user/notifications'}),
      apps: apps.map((app) => ({
        app: heroku.get(`/apps/${app}`),
        formation: heroku.get(`/apps/${app}/formation`),
        pipeline: heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => null)
      }))
    }
    metrics = yield fetchMetrics(data.apps)
  }))

  process.stderr.write('\r')
  if (process.stderr.clearLine) process.stderr.clearLine()
  else cli.console.error('\n')

  if (apps.length > 0) displayApps(data.apps, metrics)
  else cli.warn(`Add apps to this dashboard by favoriting them with ${cli.color.cmd('heroku apps:favorites:add')}`)

  cli.log(`
See all add-ons with ${cli.color.cmd('heroku addons')}`)
  let sampleOrg = _.sortBy(data.orgs.filter((o) => o.role !== 'collaborator'), (o) => new Date(o.created_at))[0]
  if (sampleOrg) cli.log(`See all apps in ${cli.color.yellow.dim(sampleOrg.name)} with ${cli.color.cmd('heroku apps --org ' + sampleOrg.name)}`)
  cli.log(`See all apps with ${cli.color.cmd('heroku apps --all')}`)
  displayNotifications(data.notifications)
  cli.log(`
See other CLI commands with ${cli.color.cmd('heroku help')}
`)
}

module.exports = {
  topic: 'dashboard',
  description: 'display information about favorite apps',
  hidden: true,
  needsAuth: true,
  wantsOrg: true,
  run: cli.command(co.wrap(run))
}
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('dpSearchResults')
        .constant('SEARCH_CONFIG', {
            QUERY_ENDPOINTS: [
                {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'straatnamen',
                    label_singular: 'Straatnamen',
                    label_plural: 'Straatnamen',
                    uri: 'atlas/search/openbareruimte/',
                    options: { subtype: 'weg' }
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'adres',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    uri: 'atlas/search/adres/'
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'openbareruimte',
                    label_singular: 'Openbare ruimtea',
                    label_plural: 'Openbare ruimtes',
                    uri: 'atlas/search/openbareruimte/',
                    options: { subtype: 'not_weg' }
                }, {
                    slug: 'vestiging',
                    label_singular: 'Vestiging',
                    label_plural: 'Vestigingen',
                    uri: 'handelsregister/search/vestiging/',
                    authScope: 'HR/R'
                }, {
                    slug: 'mac',
                    label_singular: 'Maatschappelijke activiteit',
                    label_plural: 'Maatschappelijke activiteiten',
                    uri: 'handelsregister/search/maatschappelijkeactiviteit/',
                    authScope: 'HR/R'
                }, {
                    slug: 'object',
                    label_singular: 'Kadastraal object',
                    label_plural: 'Kadastrale objecten',
                    uri: 'atlas/search/kadastraalobject/'
                }, {
                    slug: 'subject',
                    label_singular: 'Kadastraal subject',
                    label_plural: 'Kadastrale subjecten',
                    uri: 'atlas/search/kadastraalsubject/',
                    authScope: 'BRK/RS'
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'gebied',
                    label_singular: 'Gebied',
                    label_plural: 'Gebieden',
                    uri: 'atlas/search/gebied/',
                    subtypes: {
                        grootstedelijk: 'grootstedelijk gebied',
                        gebiedsgerichtwerken: 'gebiedsgericht werken'
                    }
                }, {
                    slug: 'meetbout',
                    label_singular: 'Meetbout',
                    label_plural: 'Meetbouten',
                    uri: 'meetbouten/search/'
                }, {
                    slug: 'monument',
                    label_singular: 'Monument',
                    label_plural: 'Monumenten',
                    uri: 'monumenten/search/'
                }
            ],
            COORDINATES_ENDPOINTS: [
                {
                    uri: 'geosearch/nap/',
                    radius: 25
                }, {
                    uri: 'geosearch/atlas/',
                    radius: null
                }, {
                    uri: 'geosearch/munitie/',
                    radius: null
                }, {
                    uri: 'geosearch/bominslag/',
                    radius: 25
                }, {
                    uri: 'geosearch/monumenten/',
                    radius: 25,
                    extra_params: {
                        monumenttype: 'isnot_pand_bouwblok'
                    }
                },
                { uri: 'geosearch/grondexploitatie/' },
                { uri: 'geosearch/biz/' }
            ],
            COORDINATES_HIERARCHY: [
                {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'openbareruimte',
                    label_singular: 'Openbare ruimte',
                    label_plural: 'Openbare ruimtes',
                    features: ['bag/openbareruimte']
                }, {
                    slug: 'pand',
                    label_singular: 'Pand',
                    label_plural: 'Panden',
                    features: ['bag/pand']
                }, {
                    slug: 'standplaats',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    features: ['bag/standplaats']
                }, {
                    slug: 'ligplaats',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    features: ['bag/ligplaats']
                }, {
                    slug: 'kadastraal-object',
                    label_singular: 'Kadastraal object',
                    label_plural: 'Kadastrale objecten',
                    features: ['kadaster/kadastraal_object']
                }, {
                    slug: 'gemeentelijke-beperking',
                    label_singular: 'Gemeentelijke beperking',
                    label_plural: 'Gemeentelijke beperkingen',
                    features: ['wkpb/beperking']
                }, {
                    slug: 'gebied',
                    label_singular: 'Gebied',
                    label_plural: 'Gebieden',
                    features: [
                        'gebieden/stadsdeel',
                        'gebieden/gebiedsgerichtwerken',
                        'gebieden/buurtcombinatie',
                        'gebieden/buurt',
                        'gebieden/bouwblok',
                        'gebieden/grootstedelijkgebied',
                        'gebieden/unesco'
                    ],
                    subtypes: {
                        grootstedelijkgebied: 'grootstedelijk gebied',
                        gebiedsgerichtwerken: 'gebiedsgericht werken'
                    }
                }, {
                    slug: 'meetbout',
                    label_singular: 'Meetbout',
                    label_plural: 'Meetbouten',
                    features: ['meetbouten/meetbout']
                }, {
                    slug: 'nap-peilmerk',
                    label_singular: 'NAP Peilmerk',
                    label_plural: 'NAP Peilmerken',
                    features: ['nap/peilmerk']
                }, {
                    slug: 'explosief',
                    label_singular: 'Explosief',
                    label_plural: 'Explosieven',
                    features: [
                        'bommenkaart/bominslag',
                        'bommenkaart/verdachtgebied',
                        'bommenkaart/gevrijwaardgebied',
                        'bommenkaart/uitgevoerdonderzoek'
                    ],
                    subtypes: {
                        bominslag: 'inslag',
                        gevrijwaardgebied: 'gevrijwaard gebied',
                        uitgevoerdonderzoek: 'reeds uitgevoerd CE onderzoek',
                        verdachtgebied: 'verdacht gebied'
                    }
                }, {
                    slug: 'monument',
                    label_singular: 'Monument',
                    label_plural: 'Monumenten',
                    features: [
                        'monumenten/monument'
                    ]
                }, {
                    slug: 'grondexploitatie',
                    label_singular: 'Grondexploitatie',
                    label_plural: 'Grondexploitaties',
                    features: ['grex/grondexploitatie']
                }, {
                    slug: 'bedrijfsinvesteringszone',
                    label_singular: 'Bedrijfsinvesteringszone',
                    label_plural: 'Bedrijfsinvesteringszones',
                    features: ['vsd/biz']
                }
            ]
        });
})();
<commit_msg>Fix spelling error<commit_after>(function () {
    'use strict';

    angular
        .module('dpSearchResults')
        .constant('SEARCH_CONFIG', {
            QUERY_ENDPOINTS: [
                {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'straatnamen',
                    label_singular: 'Straatnamen',
                    label_plural: 'Straatnamen',
                    uri: 'atlas/search/openbareruimte/',
                    options: { subtype: 'weg' }
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'adres',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    uri: 'atlas/search/adres/'
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'openbareruimte',
                    label_singular: 'Openbare ruimte',
                    label_plural: 'Openbare ruimtes',
                    uri: 'atlas/search/openbareruimte/',
                    options: { subtype: 'not_weg' }
                }, {
                    slug: 'vestiging',
                    label_singular: 'Vestiging',
                    label_plural: 'Vestigingen',
                    uri: 'handelsregister/search/vestiging/',
                    authScope: 'HR/R'
                }, {
                    slug: 'mac',
                    label_singular: 'Maatschappelijke activiteit',
                    label_plural: 'Maatschappelijke activiteiten',
                    uri: 'handelsregister/search/maatschappelijkeactiviteit/',
                    authScope: 'HR/R'
                }, {
                    slug: 'object',
                    label_singular: 'Kadastraal object',
                    label_plural: 'Kadastrale objecten',
                    uri: 'atlas/search/kadastraalobject/'
                }, {
                    slug: 'subject',
                    label_singular: 'Kadastraal subject',
                    label_plural: 'Kadastrale subjecten',
                    uri: 'atlas/search/kadastraalsubject/',
                    authScope: 'BRK/RS'
                }, {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'gebied',
                    label_singular: 'Gebied',
                    label_plural: 'Gebieden',
                    uri: 'atlas/search/gebied/',
                    subtypes: {
                        grootstedelijk: 'grootstedelijk gebied',
                        gebiedsgerichtwerken: 'gebiedsgericht werken'
                    }
                }, {
                    slug: 'meetbout',
                    label_singular: 'Meetbout',
                    label_plural: 'Meetbouten',
                    uri: 'meetbouten/search/'
                }, {
                    slug: 'monument',
                    label_singular: 'Monument',
                    label_plural: 'Monumenten',
                    uri: 'monumenten/search/'
                }
            ],
            COORDINATES_ENDPOINTS: [
                {
                    uri: 'geosearch/nap/',
                    radius: 25
                }, {
                    uri: 'geosearch/atlas/',
                    radius: null
                }, {
                    uri: 'geosearch/munitie/',
                    radius: null
                }, {
                    uri: 'geosearch/bominslag/',
                    radius: 25
                }, {
                    uri: 'geosearch/monumenten/',
                    radius: 25,
                    extra_params: {
                        monumenttype: 'isnot_pand_bouwblok'
                    }
                },
                { uri: 'geosearch/grondexploitatie/' },
                { uri: 'geosearch/biz/' }
            ],
            COORDINATES_HIERARCHY: [
                {
                    // The slug variable is used to identify this category in list.component.js
                    slug: 'openbareruimte',
                    label_singular: 'Openbare ruimte',
                    label_plural: 'Openbare ruimtes',
                    features: ['bag/openbareruimte']
                }, {
                    slug: 'pand',
                    label_singular: 'Pand',
                    label_plural: 'Panden',
                    features: ['bag/pand']
                }, {
                    slug: 'standplaats',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    features: ['bag/standplaats']
                }, {
                    slug: 'ligplaats',
                    label_singular: 'Adres',
                    label_plural: 'Adressen',
                    features: ['bag/ligplaats']
                }, {
                    slug: 'kadastraal-object',
                    label_singular: 'Kadastraal object',
                    label_plural: 'Kadastrale objecten',
                    features: ['kadaster/kadastraal_object']
                }, {
                    slug: 'gemeentelijke-beperking',
                    label_singular: 'Gemeentelijke beperking',
                    label_plural: 'Gemeentelijke beperkingen',
                    features: ['wkpb/beperking']
                }, {
                    slug: 'gebied',
                    label_singular: 'Gebied',
                    label_plural: 'Gebieden',
                    features: [
                        'gebieden/stadsdeel',
                        'gebieden/gebiedsgerichtwerken',
                        'gebieden/buurtcombinatie',
                        'gebieden/buurt',
                        'gebieden/bouwblok',
                        'gebieden/grootstedelijkgebied',
                        'gebieden/unesco'
                    ],
                    subtypes: {
                        grootstedelijkgebied: 'grootstedelijk gebied',
                        gebiedsgerichtwerken: 'gebiedsgericht werken'
                    }
                }, {
                    slug: 'meetbout',
                    label_singular: 'Meetbout',
                    label_plural: 'Meetbouten',
                    features: ['meetbouten/meetbout']
                }, {
                    slug: 'nap-peilmerk',
                    label_singular: 'NAP Peilmerk',
                    label_plural: 'NAP Peilmerken',
                    features: ['nap/peilmerk']
                }, {
                    slug: 'explosief',
                    label_singular: 'Explosief',
                    label_plural: 'Explosieven',
                    features: [
                        'bommenkaart/bominslag',
                        'bommenkaart/verdachtgebied',
                        'bommenkaart/gevrijwaardgebied',
                        'bommenkaart/uitgevoerdonderzoek'
                    ],
                    subtypes: {
                        bominslag: 'inslag',
                        gevrijwaardgebied: 'gevrijwaard gebied',
                        uitgevoerdonderzoek: 'reeds uitgevoerd CE onderzoek',
                        verdachtgebied: 'verdacht gebied'
                    }
                }, {
                    slug: 'monument',
                    label_singular: 'Monument',
                    label_plural: 'Monumenten',
                    features: [
                        'monumenten/monument'
                    ]
                }, {
                    slug: 'grondexploitatie',
                    label_singular: 'Grondexploitatie',
                    label_plural: 'Grondexploitaties',
                    features: ['grex/grondexploitatie']
                }, {
                    slug: 'bedrijfsinvesteringszone',
                    label_singular: 'Bedrijfsinvesteringszone',
                    label_plural: 'Bedrijfsinvesteringszones',
                    features: ['vsd/biz']
                }
            ]
        });
})();
<|endoftext|>


<commit_before>javascript:!function() {
	var L = document.location.href,
		m = document.createElement('div'),
		d = 'v20131231',
		ver = '' + d + '';
	switch (true) {
//いつもの放送ページの処理
	case /live\.nicovideo\.jp\/watch/.test(L): {
	//ここ仕様変更に弱そう
		var h0 = document.getElementsByTagName('meta')[14].outerHTML,
	//放送IDを取得
			h = h0.replace(/.+?watch\/(lv\d+).*/, '$1'),
			Aries = '',
			del = 'var f = "flvplayer_container",flv = document.getElementById(f);flv.parentNode.removeChild(flv);';
//放送主かどうか
		var u0 = document.getElementById('siteHeaderNotification').outerHTML,
	//ログインしてるユーザーIDを取得
			u = u0.replace(/.+?nico-userid="(\d+)"[\s\S]+/, '$1'),
			n0 = document.getElementsByClassName('nicopedia_nushi');
	//放送主のユーザーIDを取得(表示がない放送は0)
			if (n0.length != '0') {
				var n1 = n0[0].innerHTML,
				n = n1.replace(/[\s\S]+nicovideo.jp\/user\/(\d+)"[\s\S]+/, '$1');
			} else {
				n = 0;
			}
	//ログインしてるユーザーIDと放送主のユーザーIDを比較
			if (u != n) {
				nushi = 'N';
			} else {
				nushi = 'Y';
			}
		break;
//新しい放送ページ(？)の処理
	} case /live2\.nicovideo\.jp\/watch/.test(L): {
		var h = L.replace(/.+?watch\/(lv\d+).*/, '$1'),
			s = document.getElementById('player-container').innerHTML,
			p = s.replace(/[\s\u2028\u2029]/g,'')
			a = p.replace(/.*?"(audienceToken)".+?"(\w+)".*/,'$1=$2'),
			b1 = p.replace(/.*?"(beginTime)".+?"(\d+)".*/,'&$1=$2'),
			b2 = p.replace(/.*?"(broadcastId)".+?"(\d+)".*/,'&$1=$2'),
			w = '&webSocketUrl=ws://a.live2.nicovideo.jp/wsapi/v1/watch/';
			Aries = '<input type="button" value="Aries" onClick="window.open(\'http://nl.nimg.jp/public/relive/1.2.10/assets/web/r1/swfs/v1/AriesPlayer.swf?' + a + b1 + b2 + w + '\',\'_blank\',\'width=960,height=512\');var f = "playerswf",flv = document.getElementById(f);flv.parentNode.removeChild(flv);">
',
			del = 'var f = "playerswf",flv = document.getElementById(f);flv.parentNode.removeChild(flv);',
			nushi = 'N';
		break;
//それ以外のページの処理
	} default: {
		var h = prompt('放送IDかURL', '');
		if (h.match(/lv[0-9]+/)) {
			h = h.replace(/.+?(lv\d+).*/, '$1'),
			Aries = '',
			del = '';
		} else {
			alert('放送IDが入ってないよ！\n「' + h + '」\n連絡先とか　http://nico.ms/ar303976');
			return false;
		}
		break;
	}}
//放送主だったら追加
	if (nushi == 'Y') {
		nama1 = '放送主用
<input type="button" value="配信ツール" onClick="window.open(\'http://live.nicovideo.jp/nicolivebroadcaster.swf?dicfilename=NicoliveBroadcasterDictionaryJAJP.swf&v=' + h + '\',\'_blank\',\'width=900,height=250\')"><input type="button" value="放送情報" onClick="window.open(\'http://live.nicovideo.jp/editstream/' + h + '\',\'_blank\',\'width=960,height=600,scrollbars=1\')">
<input type="button" value="配信ツール(原宿)" onClick="window.open(\'http://live.nicovideo.jp/utility_v1.swf?v=' + h + '\',\'_blank\',\'width=950,height=200\')"> ',
		nama2 = '
※配信ツールはニコ電とセッションが使えません
※配信ツール(原宿)はセッションがないです
※かんたん配信タブは1番最初に操作したウィンドウの
配信ツールでいじってください
※放送情報変更の反映は少し時間がかかります
';
	} else {
		nama1 = '',
		nama2 = '';
	}
//ページに追加
		m.innerHTML = '
×プレイヤー変更
' + Aries + '<input type="button" value="QWatch" onClick="window.open(\'http://live.nicovideo.jp/nicoliveplayer.swf?v=' + h + '&languagecode=ja-jp\',\'_blank\',\'width=960,height=490\');' + del + '"><input type="button" value="ニコファーレ" onClick="window.open(\'http://live.nicovideo.jp/liveplayer_farre.swf?v=' + h + '\',\'_blank\',\'width=960,height=520\');' + del + '">
<input type="button" value="原宿" onClick="window.open(\'http://live.nicovideo.jp/liveplayer.swf?v=' + h + '\',\'_blank\',\'width=950,height=520\');' + del + '"><input type="button" value="ページ更新" onClick="window.location.reload(true)">
' + nama1 + nama2 + ver;
		m.style.color = 'rgb(250,250,250)';
		m.style.textAlign = 'center';
		m.style.padding = '10px';
		m.style.position = 'fixed';
		m.style.zIndex = '9999';
		m.style.fontSize = '12px';
		m.style.border = '2px solid black';
		m.style.right = '20px';
		m.style.bottom = '20px';
		m.style.background = 'rgba(0,128,255,0.5)';
		m.setAttribute("id", "nicoLivew");
		document.body.appendChild(m);
}();
<commit_msg>Update nicoLivew.js<commit_after>javascript:!function() {
	var L = document.location.href,
		m = document.createElement('div'),
		d = 'v20131231',
		ver = '' + d + '';
	switch (true) {
//いつもの放送ページの処理
	case /live\.nicovideo\.jp\/watch/.test(L): {
	//ここ仕様変更に弱そう
		var h0 = document.getElementsByTagName('meta')[14].outerHTML,
	//放送IDを取得
			h = h0.replace(/.+?watch\/(lv\d+).*/, '$1'),
			Aries = '',
			del = 'var f = "flvplayer_container",flv = document.getElementById(f);flv.parentNode.removeChild(flv);';
//放送主かどうか
		var u0 = document.getElementById('siteHeaderNotification').outerHTML,
	//ログインしてるユーザーIDを取得
			u = u0.replace(/.+?nico-userid="(\d+)"[\s\S]+/, '$1'),
			n0 = document.getElementsByClassName('nicopedia_nushi');
	//放送主のユーザーIDを取得(表示がない放送は0)
			if (n0.length != '0') {
				var n1 = n0[0].innerHTML,
				n = n1.replace(/[\s\S]+nicovideo.jp\/user\/(\d+)"[\s\S]+/, '$1');
			} else {
				n = 0;
			}
	//ログインしてるユーザーIDと放送主のユーザーIDを比較
			if (u != n) {
				nushi = 'N';
			} else {
				nushi = 'Y';
			}
		break;
//新しい放送ページ(？)の処理
	} case /live2\.nicovideo\.jp\/watch/.test(L): {
		var h = L.replace(/.+?watch\/(lv\d+).*/, '$1'),
			s = document.getElementById('player-container').innerHTML,
			p = s.replace(/[\s\u2028\u2029]/g,'')
			a = p.replace(/.*?"(audienceToken)".+?"(\w+)".*/,'$1=$2'),
			b1 = p.replace(/.*?"(beginTime)".+?"(\d+)".*/,'&$1=$2'),
			b2 = p.replace(/.*?"(broadcastId)".+?"(\d+)".*/,'&$1=$2'),
			w = '&webSocketUrl=ws://a.live2.nicovideo.jp/wsapi/v1/watch/';
			Aries = '<input type="button" value="Aries" onClick="window.open(\'http://nl.nimg.jp/public/relive/1.2.10/assets/web/r1/swfs/v1/AriesPlayer.swf?' + a + b1 + b2 + w + '\',\'_blank\',\'width=960,height=512\');var f = "playerswf",flv = document.getElementById(f);flv.parentNode.removeChild(flv);">
',
			del = 'var f = "playerswf",flv = document.getElementById(f);flv.parentNode.removeChild(flv);',
			nushi = 'N';
		break;
//それ以外のページの処理
	} default: {
		var h = prompt('放送IDかURL', '');
		if (h.match(/lv[0-9]+/)) {
			h = h.replace(/.+?(lv\d+).*/, '$1'),
			Aries = '',
			del = '';
		} else {
			alert('放送IDが入ってないよ！\n「' + h + '」\n連絡先とか　http://nico.ms/ar303976');
			return false;
		}
		break;
	}}
//放送主だったら追加
	if (nushi == 'Y') {
		nama1 = '放送主用
<input type="button" value="配信ツール" onClick="window.open(\'http://live.nicovideo.jp/nicolivebroadcaster.swf?dicfilename=NicoliveBroadcasterDictionaryJAJP.swf&v=' + h + '\',\'_blank\',\'width=900,height=250\')"><input type="button" value="放送情報" onClick="window.open(\'http://live.nicovideo.jp/editstream/' + h + '\',\'_blank\',\'width=960,height=600,scrollbars=1\')">
<input type="button" value="配信ツール(原宿)" onClick="window.open(\'http://live.nicovideo.jp/utility_v1.swf?v=' + h + '\',\'_blank\',\'width=950,height=200\')"> ',
		nama2 = '
※配信ツールはニコ電とセッションが使えません
※配信ツール(原宿)はセッションがないです
※かんたん配信タブは1番最初に操作したウィンドウの
配信ツールでいじってください
※放送情報変更の反映は少し時間がかかります
';
	} else {
		nama1 = '',
		nama2 = '';
	}
//ページに追加
		m.innerHTML = '
×プレイヤー変更
' + Aries + '<input type="button" value="GINZA" onClick="window.open(\'http://live.nicovideo.jp/nicoliveplayer.swf?v=' + h + '&languagecode=ja-jp\',\'_blank\',\'width=960,height=490\');' + del + '"><input type="button" value="ニコファーレ" onClick="window.open(\'http://live.nicovideo.jp/liveplayer_farre.swf?v=' + h + '\',\'_blank\',\'width=960,height=520\');' + del + '">
<input type="button" value="原宿" onClick="window.open(\'http://live.nicovideo.jp/liveplayer.swf?v=' + h + '\',\'_blank\',\'width=950,height=520\');' + del + '"><input type="button" value="ページ更新" onClick="window.location.reload(true)">
' + nama1 + nama2 + ver;
		m.style.color = 'rgb(250,250,250)';
		m.style.textAlign = 'center';
		m.style.padding = '10px';
		m.style.position = 'fixed';
		m.style.zIndex = '9999';
		m.style.fontSize = '12px';
		m.style.border = '2px solid black';
		m.style.right = '20px';
		m.style.bottom = '20px';
		m.style.background = 'rgba(0,128,255,0.5)';
		m.setAttribute("id", "nicoLivew");
		document.body.appendChild(m);
}();
<|endoftext|>


<commit_before>const match_embed = require("../embeds/match");

function check_if_registered(client, dota_id, hero_id) {
    return new Promise((resolve) => {
        client.pg.query({
            "text": "SELECT * FROM public.users WHERE dotaid = $1",
            "values": [dota_id]
        }).then(res => {
            if (res.rowCount != 0) {
                resolve({
                    "discord_id": res.rows[0].id,
                    "hero_id": hero_id
                });
            } else {
                resolve(undefined);
            }
        });
    });
}

function send_message(message, client, helper, match_data, origin) {
    let queries = [];

    match_data.players.forEach(player => {
        queries.push(check_if_registered(client, player.account_id, player.hero_id));
    });

    Promise.all(queries).then(results => {
        let filtered = results.filter(result => result && message.channel.guild.members.find(member => member.id == result.discord_id));
        message.channel.createMessage({
            "embed": match_embed(match_data, filtered),
            "content": message.header || ""
        }).then(() => {
            helper.log(message, `  sent match data from ${origin}`);
        }).catch(err => {
            helper.log(message, "  something went wrong posting a match");
            helper.log(message, err);
        });
    }).catch(err => {
        helper.log(message, "  something went wrong selecting from the database");
        helper.log(message, err);
    });
}

function fix_scores(match_data, mika) {
    match_data.radiant_score = match_data.players.slice(0, 5).map(player => player.kills).reduce((a, b) => a + b, 0);
    match_data.dire_score = match_data.players.slice(5, 10).map(player => player.kills).reduce((a, b) => a + b, 0);

    if (mika) mika.postRequestByMatchId(match_data.match_id);
}

module.exports = (message, client, helper) => {
    let match_id = message.content.split(" ")[1];
    if (!match_id) return;

    if (match_id.match("dotabuff") || match_id.match("opendota")) {
        let url = match_id.split("/");
        match_id = url[url.length - 1];
    }

    if (isNaN(match_id)) {
        message.channel.createMessage("I couldn't find a match ID in your message!");
        return;
    }

    helper.log(message, `matchinfo: ${match_id}`);

    message.channel.sendTyping().then(() => {
        client.redis.get(`matchinfo:${match_id}`, (err, reply) => {
            if (err) helper.log(message, err);
            if (reply) {
                reply = JSON.parse(reply)
                if (!reply.radiant_score && !reply.dire_score) fix_scores(reply);
                send_message(message, client, helper, reply, "redis");
            } else {
                client.mika.getMatch(match_id).then(match_data => {
                    if (!match_data.radiant_score && !match_data.dire_score) fix_scores(match_data, mika);
                    send_message(message, client, helper, match_data, "api");
                    client.redis.set(`matchinfo:${match_id}`, JSON.stringify(match_data), (err) => {
                        if (err) helper.log(message, err);
                        client.redis.expire(`matchinfo:${match_id}`, 604800);
                    });
                }).catch(err => {
                    message.channel.createMessage("Something went wrong.");
                    helper.log(message, "  something went wrong with mika");
                    helper.log(message, err);
                });
            }
        });
    });
};
<commit_msg>i am a bad programmer<commit_after>const match_embed = require("../embeds/match");

function check_if_registered(client, dota_id, hero_id) {
    return new Promise((resolve) => {
        client.pg.query({
            "text": "SELECT * FROM public.users WHERE dotaid = $1",
            "values": [dota_id]
        }).then(res => {
            if (res.rowCount != 0) {
                resolve({
                    "discord_id": res.rows[0].id,
                    "hero_id": hero_id
                });
            } else {
                resolve(undefined);
            }
        });
    });
}

function send_message(message, client, helper, match_data, origin) {
    let queries = [];

    match_data.players.forEach(player => {
        queries.push(check_if_registered(client, player.account_id, player.hero_id));
    });

    Promise.all(queries).then(results => {
        let filtered = results.filter(result => result && message.channel.guild.members.find(member => member.id == result.discord_id));
        message.channel.createMessage({
            "embed": match_embed(match_data, filtered),
            "content": message.header || ""
        }).then(() => {
            helper.log(message, `  sent match data from ${origin}`);
        }).catch(err => {
            helper.log(message, "  something went wrong posting a match");
            helper.log(message, err);
        });
    }).catch(err => {
        helper.log(message, "  something went wrong selecting from the database");
        helper.log(message, err);
    });
}

function fix_scores(match_data, mika) {
    match_data.radiant_score = match_data.players.slice(0, 5).map(player => player.kills).reduce((a, b) => a + b, 0);
    match_data.dire_score = match_data.players.slice(5, 10).map(player => player.kills).reduce((a, b) => a + b, 0);

    if (mika) mika.postRequestByMatchId(match_data.match_id);
}

module.exports = (message, client, helper) => {
    let match_id = message.content.split(" ")[1];
    if (!match_id) return;

    if (match_id.match("dotabuff") || match_id.match("opendota")) {
        let url = match_id.split("/");
        match_id = url[url.length - 1];
    }

    if (isNaN(match_id)) {
        message.channel.createMessage("I couldn't find a match ID in your message!");
        return;
    }

    helper.log(message, `matchinfo: ${match_id}`);

    message.channel.sendTyping().then(() => {
        client.redis.get(`matchinfo:${match_id}`, (err, reply) => {
            if (err) helper.log(message, err);
            if (reply) {
                reply = JSON.parse(reply)
                if (!reply.radiant_score && !reply.dire_score) fix_scores(reply);
                send_message(message, client, helper, reply, "redis");
            } else {
                client.mika.getMatch(match_id).then(match_data => {
                    if (!match_data.radiant_score && !match_data.dire_score) fix_scores(match_data, client.mika);
                    send_message(message, client, helper, match_data, "api");
                    client.redis.set(`matchinfo:${match_id}`, JSON.stringify(match_data), (err) => {
                        if (err) helper.log(message, err);
                        client.redis.expire(`matchinfo:${match_id}`, 604800);
                    });
                }).catch(err => {
                    message.channel.createMessage("Something went wrong.");
                    helper.log(message, "  something went wrong with mika");
                    helper.log(message, err);
                });
            }
        });
    });
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport'),
    GithubStrategy = require('passport-github').Strategy,
    users = require('../../controllers/users.server.controller');

module.exports = function(config) {
  // Use github strategy
  passport.use(new GithubStrategy({
    clientID: config.github.clientID,
    clientSecret: config.github.clientSecret,
    callbackURL: config.github.callbackURL,
    passReqToCallback: true
  },
  function(req, accessToken, refreshToken, profile, done) {
    // Set the provider data and include tokens
    var providerData = profile._json;
    providerData.accessToken = accessToken;
    providerData.refreshToken = refreshToken;

    // Create the user OAuth profile
    var providerUserProfile = {
      displayName: profile.displayName || profile.username,
      email: profile.emails[0].value,
      username: profile.username,
      provider: 'github',
      providerIdentifierField: 'id',
      providerData: providerData
    };

    // Save the user OAuth profile
    users.saveOAuthUserProfile(req, providerUserProfile, done);
  }));
};
<commit_msg>Fix GitHub strategy missing email<commit_after>'use strict';

/**
 * Module dependencies.
 */
var passport = require('passport'),
    GithubStrategy = require('passport-github').Strategy,
    users = require('../../controllers/users.server.controller');

module.exports = function(config) {
  // Use github strategy
  passport.use(new GithubStrategy({
    clientID: config.github.clientID,
    clientSecret: config.github.clientSecret,
    callbackURL: config.github.callbackURL,
    passReqToCallback: true
  },
  function(req, accessToken, refreshToken, profile, done) {
    // Set the provider data and include tokens
    var providerData = profile._json;
    providerData.accessToken = accessToken;
    providerData.refreshToken = refreshToken;

    // Create the user OAuth profile
    var providerUserProfile = {
      displayName: profile.displayName || profile.username,
      email: (profile.emails && profile.emails.length) ? profile.emails[0].value : undefined,
      username: profile.username,
      provider: 'github',
      providerIdentifierField: 'id',
      providerData: providerData
    };

    // Save the user OAuth profile
    users.saveOAuthUserProfile(req, providerUserProfile, done);
  }));
};
<|endoftext|>