<commit_before>/* Uses "DateTime Picker for Bootstrap" from http://www.malot.fr/bootstrap-datetimepicker/index.php under the Apache License v2.0 http://www.apache.org/licenses/LICENSE-2.0 */

const timeZoneList = [["−12:00",-12],["−11:00",-11],["−10:00",-10],["−09:30",-9.5],["−09:00",-9],["−08:00",-8],["−07:00",-7],["−06:00",-6],["−05:00",-5],["−04:00",-4],["−03:30",-3.5],["−03:00",-3],["−02:30",-2.5],["−02:00",-2],["−01:00",-1],["",0],["+01:00",1],["+01:30",1.5],["+02:00",2],["+03:00",3],["+03:30",3.5],["+04:00",4],["+04:30",4.5],["+05:00",5],["+05:30",5.5],["+05:45",5.75],["+06:00",6],["+06:30",6.5],["+07:00",7],["+08:00",8],["+08:30",8.5],["+09:00",9],["+09:30",9.5],["+10:00",10],["+10:30",10.5],["+11:00",11],["+12:00",12],["+12:45",12.75],["+13:00",13],["+13:45",13.75],["+14:00",14]];

$(function() {
  // Run on page load:
  $("#js-warning").remove();
  data = location.search.split("data=")[1];
  if (typeof data == "undefined") {
    createTableRows(3);
  } else {
    data = JSON.parse(decodeURIComponent(data));
    createTableRows(data.length);
    populateTable(data);
    updateChart();
  }
  $(".hidden-by-default").show();
  setEventTriggers();

});

function setEventTriggers() {
  $(".dtpicker").datetimepicker({format: "yyyy-mm-dd hh:ii", pickerPosition: "top-left"});
  $("input, select").on("blur change", updateChart);
  $(".insert-row").on("click", function() { insertRow($(this)); });
  $(".delete-row").on("click", function() { deleteRow($(this)); });
}

function updateChart() {
  var timeZoneLocations = [];
  $locationRows = $("tr.location-row");
  var rowCount = $locationRows.length;
  for(i = 0; i < rowCount; i++) {
    $row = $locationRows.eq(i);
    timeZoneLocations[i] = {
      start:    $row.find(".field-start").val(),
      location: $row.find(".field-location").val(),
      offset:   parseFloat($row.find(".field-offset").val()),
      end:      $row.find(".field-end").val()
    };
  }
  str = timeZoneLocations.map(function(element, index) {
    return index + ": " + JSON.stringify(element);
  }).join("
");
  $("#chart").html(str);
  updateShareLink(timeZoneLocations);
}

function updateShareLink(timeZoneLocations) {
  data = encodeURIComponent(JSON.stringify(timeZoneLocations));
  //window.history.replaceState({},"",[location.protocol, '//', location.host, location.pathname, "?data=", data].join(''));
  $("#share-link").attr("href", [location.protocol,'//',location.host,location.pathname,"?data=",data].join(''));
}

/* HTML CREATION FUNCTIONS */

function createInsertButton() {
  return '

';
}

function createOffsetSelect() {
  html = '
<select class="form-control field-offset">';
  html += timeZoneList.map(function(element) {
    return '<option value="' + element[1] + '"' + (element[1] == 0 ? ' selected' : '') +'>UTC' + element[0] + '</option>';
  }).join("\n");
  html += '</select>
';
  return html;
}

function createTableRows(numberOfRows) {
  $tableBody = $("tbody");
  for (i = 0; i < numberOfRows; i++) {
    $tableBody.append(createRow());
  }
  $tableBody.append('
' + createInsertButton() + '
');
  $(".location-row .form-group").filter(":first, :last").remove(); // Remove first start time and last end time
}

function createRow() {
  row = '
';
  row += '' + createInsertButton() + '	';
  row += '

';
  row += '<input type="text" class="form-control field-start" />';
  row += '';
  row += '

	';
  row += '
<input type="text" class="form-control field-location" />
	'
  row += '' + createOffsetSelect() + '	';
  row += '

';
  row += '<input type="text" class="form-control field-end" />';
  row += '';
  row += '

	';
  row += '

	';
  row += '
';
  return row
}

/* TABLE MANIPULATION FUNCTIONS */

function populateTable(timeZoneLocations) {
  timeZoneLocations.map(function(element, index) {
    $row = $(".location-row").eq(index);
    $row.find(".field-start").val(element["start"]);
    $row.find(".field-location").val(element["location"]);
    $row.find(".field-offset").val(element["offset"]);
    $row.find(".field-end").val(element["end"]);
  }).join("
");
}

function insertRow(button) {
  position = getPositionFromID(button.attr('id'));
  console.log("Insert row at position " + position);
  // Add new row at end:
  var newRowID = $("tr.location-row").length;
  $(".location-row").last().after(createRow(newRowID));
  setEventTriggers();

  // Shift everything down after the given position:
  // Insert row:
  // Update chart:
}

function deleteRow(button) {
  position = getPositionFromID(button.attr('id'));
  console.log("Delete row " + position);
  // Delete row:
  // Shift everything up after the given position:
  // Update chart:
}

// Takes an ID string and extracts the integer potion from it
function getPositionFromID(id) {
  return parseInt(id.substring(id.indexOf("_")+1,id.length))
}<commit_msg>Write TZC insertRow function<commit_after>/* Uses "DateTime Picker for Bootstrap" from http://www.malot.fr/bootstrap-datetimepicker/index.php under the Apache License v2.0 http://www.apache.org/licenses/LICENSE-2.0 */

const timeZoneList = [["−12:00",-12],["−11:00",-11],["−10:00",-10],["−09:30",-9.5],["−09:00",-9],["−08:00",-8],["−07:00",-7],["−06:00",-6],["−05:00",-5],["−04:00",-4],["−03:30",-3.5],["−03:00",-3],["−02:30",-2.5],["−02:00",-2],["−01:00",-1],["",0],["+01:00",1],["+01:30",1.5],["+02:00",2],["+03:00",3],["+03:30",3.5],["+04:00",4],["+04:30",4.5],["+05:00",5],["+05:30",5.5],["+05:45",5.75],["+06:00",6],["+06:30",6.5],["+07:00",7],["+08:00",8],["+08:30",8.5],["+09:00",9],["+09:30",9.5],["+10:00",10],["+10:30",10.5],["+11:00",11],["+12:00",12],["+12:45",12.75],["+13:00",13],["+13:45",13.75],["+14:00",14]];

$(function() {
  // Run on page load:
  $("#js-warning").remove();
  data = location.search.split("data=")[1];
  if (typeof data == "undefined") {
    createTableRows(3);
  } else {
    data = JSON.parse(decodeURIComponent(data));
    createTableRows(data.length);
    populateTable(data);
    updateChart();
  }
  $(".hidden-by-default").show();
  setEventTriggers();

});

function setEventTriggers() {
  $(".dtpicker").datetimepicker({format: "yyyy-mm-dd hh:ii", pickerPosition: "top-left"});
  $("input, select").on("blur change", updateChart);
  $(".insert-row").off().on("click", function() { insertRow($(this)); });
  $(".delete-row").off().on("click", function() { deleteRow($(this)); });
}

function updateChart() {
  var timeZoneLocations = [];
  $locationRows = $("tr.location-row");
  var rowCount = $locationRows.length;
  for(i = 0; i < rowCount; i++) {
    $row = $locationRows.eq(i);
    timeZoneLocations[i] = {
      start:    $row.find(".field-start").val(),
      location: $row.find(".field-location").val(),
      offset:   parseFloat($row.find(".field-offset").val()),
      end:      $row.find(".field-end").val()
    };
  }
  str = timeZoneLocations.map(function(element, index) {
    return index + ": " + JSON.stringify(element);
  }).join("
");
  $("#chart").html(str);
  updateShareLink(timeZoneLocations);
}

function updateShareLink(timeZoneLocations) {
  data = encodeURIComponent(JSON.stringify(timeZoneLocations));
  //window.history.replaceState({},"",[location.protocol, '//', location.host, location.pathname, "?data=", data].join(''));
  $("#share-link").attr("href", [location.protocol,'//',location.host,location.pathname,"?data=",data].join(''));
}

/* HTML CREATION FUNCTIONS */

function createInsertButton() {
  return '

';
}

function createDateTime(fieldName) {
  html = '

';
  html += '<input type="text" class="form-control field-' + fieldName + '" />';
  html += '';
  html += '

';
  return html;
}

function createOffsetSelect() {
  html = '
<select class="form-control field-offset">';
  html += timeZoneList.map(function(element) {
    return '<option value="' + element[1] + '"' + (element[1] == 0 ? ' selected' : '') +'>UTC' + element[0] + '</option>';
  }).join("\n");
  html += '</select>
';
  return html;
}

function createTableRows(numberOfRows) {
  $tableBody = $("tbody");
  for (i = 0; i < numberOfRows; i++) {
    $tableBody.append(createRow());
  }
  $tableBody.append('
' + createInsertButton() + '
');
  $(".location-row .form-group").filter(":first, :last").remove(); // Remove first start time and last end time
}

function createRow() {
  row = '
';
  row += '' + createInsertButton() + '	';
  row += '' + createDateTime("start") + '	';
  row += '
<input type="text" class="form-control field-location" />
	'
  row += '' + createOffsetSelect() + '	';
  row += '' + createDateTime("end") + '	';
  row += '

	';
  row += '
';
  return row
}

/* TABLE MANIPULATION FUNCTIONS */

function populateTable(timeZoneLocations) {
  timeZoneLocations.map(function(element, index) {
    $row = $(".location-row").eq(index);
    $row.find(".field-start").val(element["start"]);
    $row.find(".field-location").val(element["location"]);
    $row.find(".field-offset").val(element["offset"]);
    $row.find(".field-end").val(element["end"]);
  }).join("
");
}

function insertRow(button) {
  position = parseInt($(".insert-row").index(button));
  if (position < $(".location-row").length) {
    $oldRow = $(".location-row").eq(position)
    $oldRow.before(createRow());
    $newRow = $(".location-row").eq(position)
    $newRow.hide();
    if (position == 0) {
      $oldRow.find("td").eq(1).append(createDateTime("start"));
      $newRow.find(".form-group").filter(":first").remove();
    }
    $newRow.find(".field-offset").val($oldRow.find(".field-offset").val())
    $newRow.fadeIn();
  } else {
    $oldRow = $(".location-row").filter(":last");
    $oldRow.find("td").eq(4).append(createDateTime("end"));
    $oldRow.after(createRow());
    $newRow = $(".location-row").filter(":last");
    $newRow.hide();
    $newRow.find(".form-group").filter(":last").remove();
    $newRow.find(".field-offset").val($oldRow.find(".field-offset").val())
    $newRow.fadeIn();
  }

  setEventTriggers();

}

function deleteRow(button) {
  position = getPositionFromID(button.attr('id'));
  console.log("Delete row " + position);
  // Delete row:
  // Shift everything up after the given position:
  // Update chart:
}<|endoftext|>


<commit_before>import Ember from 'ember';

import ClickOutsideComponent from '../mixins/click-outside-component';
import Filterable from '../mixins/filterable';

export default Ember.Component.extend(Filterable, ClickOutsideComponent, {

    /** Bind conditional classes. */
    classNameBindings: ['hasSelected:has-success', 'isInvalid:has-error', '!removeCreate:input-group'],

    /** Setup component css classes. */
    classNames: [
        'autocomplete-input', 'dropdown-group'
    ],

    /** Update when item being updated from outside. */
    didUpdateAttrs() {
        this._super(...arguments);
        const item = this.get('item');

        if (item && item !== this.get('hasSelected')) {
            this.set('hasSelected', item);
        }
    },

    /** Enable multi select.
     *
     * When multi select is enabled the textfield will not be
     * set with the selected value.
     *
     */
    multiSelect: false,

    /** Setting to disable create button. */
    disableCreate: true,

    /** Setting to hide the create button. */
    removeCreate: true,

    /** Injected services. */
    store: Ember.inject.service('store'),

    /** Name of the store to fetch data from. */
    storeName: null,

    /** Value of the input field */
    value: '',

    /** Limit number of responses. */
    limit: 7,

    /** Has valid selection. */
    hasSelected: false,

    /** Highligted index in dropdown. */
    highlightedIndex: -1,

    /** Update dropdown list when index change. */
    onHighlightedIndexChange: function () {
        if (this.get('hasFocus')) {
            let listItem = this.$('li[data-index='+ this.get('highlightedIndex') +']');

            if (listItem) {
                listItem.addClass('active').siblings().removeClass('active');
            }
        }
    }.observes('highlightedIndex', 'hasFocus'),

    /** Handle item select. */
    onSelected: function () {
        if (this.get('hasSelected')) {
            this.set(
                'value', this.get('hasSelected').get(this.get('displayField', 'name'))
            );

            this.set('hasFocus', false);
        }
    }.observes('hasSelected'),

    /** Return if input is invalid. */
    isInvalid: Ember.computed('hasSelected', 'hasFocus', 'value', function () {
        const isInvalid = (
            (this.get('value') && this.get('value').length > 0 && !this.get('multiSelect')) &&
            !this.get('hasFocus') && !this.get('hasSelected')
        );

        return isInvalid;
    }),

    /** Data to display in preview dropdown. */
    previewData: [],

    /** Does the input field have focus. */
    hasFocus: false,

    /** Field to use in autocomplete search */
    filterField: null,

    /** Timeout between keyup events. */
    keyupTimeout: 150,

    /** Return if preview dropdown is visible. */
    isDropdownVisible: Ember.computed('previewData', 'hasFocus', function () {
        return this.get('hasFocus') && (
            this.get('previewData').length || this.get('value.length')
        );
    }),

    /** Override init. */
    init () {

        /** If using local data decrease the timeout. */
        if (this.localData) {
            this.keyupTimeout = 10;
        }

        return this._super(...arguments);
    },

    /** Fetch data stored in client store with out request.
     *
     * Using this method it's up to the application to keep the data up to date.
     *
     */
    _fetchLocalData (filterField, filterValue, limit) {
        var result = this.get('store').peekAll(this.storeName).filter((item) => {
            let fieldValue = item.get(filterField).toLowerCase();

            return fieldValue.indexOf(filterValue.toLowerCase()) !== -1;
        }).sortBy(filterField).slice(0, limit + 1);

        this.set('previewData', result);
    },

    /** Fetch data from remote server. */
    _fetchRemoteData (filterField, filterValue, limit) {
        var queryParams = {
            search: true,
            limit: limit,
            orderby: filterField
        };

        queryParams[filterField] = filterValue;

        Ember.$.extend(queryParams, this.get('filters'));

        this.get('store').query(this.storeName, queryParams).then((response) => {
            this.set('previewData', response);
        }).catch((reason) => {
            this.set('previewData', []);

            console.warn('Invalid response from server: ', reason);
        });
    },

    /**
     * Fetch data based on search value and update store.
     */
    fetchData () {
        if (this.localData) {
            this._fetchLocalData(
                this.get('filterField'), this.get('value'), this.get('limit')
            );
        } else {
            this._fetchRemoteData(
                this.get('filterField'), this.get('value'), this.get('limit')
            );
        }
    },

    /** Handle event when user clicks outside of component. */
    onOutsideClick () {
        this.set('hasFocus', false);
    },

    /**
     * Handle keyup events in input field.
     */
    _onKeyup () {
        if (!this.get('value').length) {
            this.set('previewData', []);
        } else {
            this.fetchData();
        }

        // Set item as undefined.
        if (!this.get('multiSelect')) {
            this.get('itemSelected')();
            this.set('hasSelected', false);
        }

        this.set('highlightedIndex', -1);
    },

    actions: {

        /**
        * Handle Keydown events in input field.
        *
        * Triggers private method to be able to delay execution.
        *
        */
        onKeyDown (value, event) {
            this.set('hasFocus', true);

            if ([38, 40, 13, 27, 9].indexOf(event.keyCode) !== -1) {
                let index = this.get('highlightedIndex');

                // Arrow key up.
                if (event.keyCode === 38) {
                    index -= 1;
                // Arrow key down
                } else if (event.keyCode === 40) {
                    index += 1;

                // Enter key
                } else if (event.keyCode === 13) {
                    this.send(
                        'onItemClick', this.get('previewData').objectAt(index)
                    );

                    return;

                // ESC and TAB key should remove focus.
                } else if ([27, 9].indexOf(event.keyCode) !== -1) {
                    this.set('hasFocus', false);
                }

                if (index < 0) {
                    index = 0;
                } else if (index > this.get('previewData').length - 1) {
                    index = this.get('previewData').length - 1;
                }

                this.set('highlightedIndex', index);

                return false;
            }

            // Debounce keyup event if user types fast.
            Ember.run.debounce(this, this._onKeyup, this.keyupTimeout);
        },

        /**
         * Handle focus events from input field.
         */
        onFocus () {
            this.set('hasFocus', true);
        },

        /**
         * Handle click event in dropdown list.
         */
        onItemClick (item) {
            // Should be set from parent.
            this.attrs.itemSelected(item);

            if (!this.get('multiSelect')) {
                this.set('hasSelected', item);

            // If multi select clear the value, close the dropdown and give focus to
            // input field.
            } else {
                this.set('value', '');
                this.set('previewData', []);
                this.$('input').focus();
            }
        }
    }
});
<commit_msg>include more results in regular autocomplete<commit_after>import Ember from 'ember';

import ClickOutsideComponent from '../mixins/click-outside-component';
import Filterable from '../mixins/filterable';

export default Ember.Component.extend(Filterable, ClickOutsideComponent, {

    /** Bind conditional classes. */
    classNameBindings: ['hasSelected:has-success', 'isInvalid:has-error', '!removeCreate:input-group'],

    /** Setup component css classes. */
    classNames: [
        'autocomplete-input', 'dropdown-group'
    ],

    /** Update when item being updated from outside. */
    didUpdateAttrs() {
        this._super(...arguments);
        const item = this.get('item');

        if (item && item !== this.get('hasSelected')) {
            this.set('hasSelected', item);
        }
    },

    /** Enable multi select.
     *
     * When multi select is enabled the textfield will not be
     * set with the selected value.
     *
     */
    multiSelect: false,

    /** Setting to disable create button. */
    disableCreate: true,

    /** Setting to hide the create button. */
    removeCreate: true,

    /** Injected services. */
    store: Ember.inject.service('store'),

    /** Name of the store to fetch data from. */
    storeName: null,

    /** Value of the input field */
    value: '',

    /** Limit number of responses. */
    limit: 40,

    /** Has valid selection. */
    hasSelected: false,

    /** Highligted index in dropdown. */
    highlightedIndex: -1,

    /** Update dropdown list when index change. */
    onHighlightedIndexChange: function () {
        if (this.get('hasFocus')) {
            let listItem = this.$('li[data-index='+ this.get('highlightedIndex') +']');

            if (listItem) {
                listItem.addClass('active').siblings().removeClass('active');
            }
        }
    }.observes('highlightedIndex', 'hasFocus'),

    /** Handle item select. */
    onSelected: function () {
        if (this.get('hasSelected')) {
            this.set(
                'value', this.get('hasSelected').get(this.get('displayField', 'name'))
            );

            this.set('hasFocus', false);
        }
    }.observes('hasSelected'),

    /** Return if input is invalid. */
    isInvalid: Ember.computed('hasSelected', 'hasFocus', 'value', function () {
        const isInvalid = (
            (this.get('value') && this.get('value').length > 0 && !this.get('multiSelect')) &&
            !this.get('hasFocus') && !this.get('hasSelected')
        );

        return isInvalid;
    }),

    /** Data to display in preview dropdown. */
    previewData: [],

    /** Does the input field have focus. */
    hasFocus: false,

    /** Field to use in autocomplete search */
    filterField: null,

    /** Timeout between keyup events. */
    keyupTimeout: 150,

    /** Return if preview dropdown is visible. */
    isDropdownVisible: Ember.computed('previewData', 'hasFocus', function () {
        return this.get('hasFocus') && (
            this.get('previewData').length || this.get('value.length')
        );
    }),

    /** Override init. */
    init () {

        /** If using local data decrease the timeout. */
        if (this.localData) {
            this.keyupTimeout = 10;
        }

        return this._super(...arguments);
    },

    /** Fetch data stored in client store with out request.
     *
     * Using this method it's up to the application to keep the data up to date.
     *
     */
    _fetchLocalData (filterField, filterValue, limit) {
        var result = this.get('store').peekAll(this.storeName).filter((item) => {
            let fieldValue = item.get(filterField).toLowerCase();

            return fieldValue.indexOf(filterValue.toLowerCase()) !== -1;
        }).sortBy(filterField).slice(0, limit + 1);

        this.set('previewData', result);
    },

    /** Fetch data from remote server. */
    _fetchRemoteData (filterField, filterValue, limit) {
        var queryParams = {
            search: true,
            limit: limit,
            orderby: filterField
        };

        queryParams[filterField] = filterValue;

        Ember.$.extend(queryParams, this.get('filters'));

        this.get('store').query(this.storeName, queryParams).then((response) => {
            this.set('previewData', response);
        }).catch((reason) => {
            this.set('previewData', []);

            console.warn('Invalid response from server: ', reason);
        });
    },

    /**
     * Fetch data based on search value and update store.
     */
    fetchData () {
        if (this.localData) {
            this._fetchLocalData(
                this.get('filterField'), this.get('value'), this.get('limit')
            );
        } else {
            this._fetchRemoteData(
                this.get('filterField'), this.get('value'), this.get('limit')
            );
        }
    },

    /** Handle event when user clicks outside of component. */
    onOutsideClick () {
        this.set('hasFocus', false);
    },

    /**
     * Handle keyup events in input field.
     */
    _onKeyup () {
        if (!this.get('value').length) {
            this.set('previewData', []);
        } else {
            this.fetchData();
        }

        // Set item as undefined.
        if (!this.get('multiSelect')) {
            this.get('itemSelected')();
            this.set('hasSelected', false);
        }

        this.set('highlightedIndex', -1);
    },

    actions: {

        /**
        * Handle Keydown events in input field.
        *
        * Triggers private method to be able to delay execution.
        *
        */
        onKeyDown (value, event) {
            this.set('hasFocus', true);

            if ([38, 40, 13, 27, 9].indexOf(event.keyCode) !== -1) {
                let index = this.get('highlightedIndex');

                // Arrow key up.
                if (event.keyCode === 38) {
                    index -= 1;
                // Arrow key down
                } else if (event.keyCode === 40) {
                    index += 1;

                // Enter key
                } else if (event.keyCode === 13) {
                    this.send(
                        'onItemClick', this.get('previewData').objectAt(index)
                    );

                    return;

                // ESC and TAB key should remove focus.
                } else if ([27, 9].indexOf(event.keyCode) !== -1) {
                    this.set('hasFocus', false);
                }

                if (index < 0) {
                    index = 0;
                } else if (index > this.get('previewData').length - 1) {
                    index = this.get('previewData').length - 1;
                }

                this.set('highlightedIndex', index);

                return false;
            }

            // Debounce keyup event if user types fast.
            Ember.run.debounce(this, this._onKeyup, this.keyupTimeout);
        },

        /**
         * Handle focus events from input field.
         */
        onFocus () {
            this.set('hasFocus', true);
        },

        /**
         * Handle click event in dropdown list.
         */
        onItemClick (item) {
            // Should be set from parent.
            this.attrs.itemSelected(item);

            if (!this.get('multiSelect')) {
                this.set('hasSelected', item);

            // If multi select clear the value, close the dropdown and give focus to
            // input field.
            } else {
                this.set('value', '');
                this.set('previewData', []);
                this.$('input').focus();
            }
        }
    }
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';

import Panel from '../../shared/Panel';
import ShowMoreFooter from '../../shared/ShowMoreFooter';

import Row from './row';
import Chooser from './chooser';

const INITIAL_PAGE_SIZE = 5;
const PAGE_SIZE = 20;

class TeamMemberships extends React.PureComponent {
  static displayName = "Member.Teams";

  static propTypes = {
    organizationMember: PropTypes.shape({
      uuid: PropTypes.string.isRequired,
      teams: PropTypes.shape({
        count: PropTypes.number.isRequired,
        edges: PropTypes.arrayOf(
          PropTypes.shape({
            node: PropTypes.object.isRequired
          }).isRequired
        ).isRequired
      }).isRequired
    }),
    relay: PropTypes.object.isRequired
  };

  state = {
    loading: false
  };

  render() {
    return (

        <Chooser
          organizationMember={this.props.organizationMember}
          onChoose={this.handleTeamMemberAdd}
        />
        <Panel>
          {this.renderTeams()}
          <ShowMoreFooter
            connection={this.props.organizationMember.teams}
            label="teams"
            loading={this.state.loading}
            onShowMore={this.handleShowMoreTeams}
          />
        </Panel>

    );
  }

  handleTeamMemberAdd = () => {
    this.props.relay.forceFetch();
  };

  renderTeams() {
    const teams = this.props.organizationMember.teams.edges;

    if (!teams.length) {
      return (

          This user is not a member of any teams.

      );
    }

    return teams.map(({ node }) => (
      <Row
        key={node.id}
        teamMember={node}
      />
    ));
  }

  handleShowMoreTeams = () => {
    this.setState({ loading: true });

    let { teamsPageSize } = this.props.relay.variables;

    teamsPageSize += PAGE_SIZE;

    this.props.relay.setVariables(
      { teamsPageSize },
      (readyState) => {
        if (readyState.done) {
          this.setState({ loading: false });
        }
      }
    );
  };
}

export default Relay.createContainer(TeamMemberships, {
  initialVariables: {
    teamsPageSize: INITIAL_PAGE_SIZE
  },

  fragments: {
    organizationMember: () => Relay.QL`
      fragment on OrganizationMember {
        uuid
        user {
          id
        }
        teams(first: $teamsPageSize, order: NAME) {
          ${ShowMoreFooter.getFragment('connection')}
          count
          edges {
            node {
              id
              ${Row.getFragment('teamMember')}
            }
          }
        }
        ${Chooser.getFragment('organizationMember')}
      }
    `
  }
});
<commit_msg>Toggle the display of "Add to Team" on the member teams page<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import Relay from 'react-relay/classic';

import Panel from '../../shared/Panel';
import ShowMoreFooter from '../../shared/ShowMoreFooter';

import Row from './row';
import Chooser from './chooser';

const INITIAL_PAGE_SIZE = 5;
const PAGE_SIZE = 20;

class TeamMemberships extends React.PureComponent {
  static displayName = "Member.Teams";

  static propTypes = {
    organizationMember: PropTypes.shape({
      uuid: PropTypes.string.isRequired,
      permissions: PropTypes.object,
      teams: PropTypes.shape({
        count: PropTypes.number.isRequired,
        edges: PropTypes.arrayOf(
          PropTypes.shape({
            node: PropTypes.object.isRequired
          }).isRequired
        ).isRequired
      }).isRequired
    }),
    relay: PropTypes.object.isRequired
  };

  state = {
    loading: false
  };

  render() {
    return (

        {this.renderTeamAddButton()}
        <Panel>
          {this.renderTeams()}
          <ShowMoreFooter
            connection={this.props.organizationMember.teams}
            label="teams"
            loading={this.state.loading}
            onShowMore={this.handleShowMoreTeams}
          />
        </Panel>

    );
  }

  renderTeamAddButton() {
    if (this.props.organizationMember.organization.permissions.teamAdmin.allowed) {
      return (
        <Chooser
          organizationMember={this.props.organizationMember}
          onChoose={this.handleTeamMemberAdd}
        />
      );
    }
  }

  handleTeamMemberAdd = () => {
    this.props.relay.forceFetch();
  };

  renderTeams() {
    const teams = this.props.organizationMember.teams.edges;

    if (!teams.length) {
      return (

          This user is not a member of any teams.

      );
    }

    return teams.map(({ node }) => (
      <Row
        key={node.id}
        teamMember={node}
      />
    ));
  }

  handleShowMoreTeams = () => {
    this.setState({ loading: true });

    let { teamsPageSize } = this.props.relay.variables;

    teamsPageSize += PAGE_SIZE;

    this.props.relay.setVariables(
      { teamsPageSize },
      (readyState) => {
        if (readyState.done) {
          this.setState({ loading: false });
        }
      }
    );
  };
}

export default Relay.createContainer(TeamMemberships, {
  initialVariables: {
    teamsPageSize: INITIAL_PAGE_SIZE
  },

  fragments: {
    organizationMember: () => Relay.QL`
      fragment on OrganizationMember {
        uuid
        user {
          id
        }
        organization {
          permissions {
            teamAdmin {
              allowed
            }
          }
        }
        teams(first: $teamsPageSize, order: NAME) {
          ${ShowMoreFooter.getFragment('connection')}
          count
          edges {
            node {
              id
              ${Row.getFragment('teamMember')}
            }
          }
        }
        ${Chooser.getFragment('organizationMember')}
      }
    `
  }
});
<|endoftext|>


<commit_before>var React = require('react');
var SliderGuage = require('./slider-guage/SliderGuage');
var QueueItem = require('./QueueItem');
var Rebase = require('../../../utils/firebase/rebase');
var appConstants = require('../../../constants/appConstants');
var helpers = require('../../../utils/firebase/helpers');
var EnterQueue = require('./EnterQueue');
var Card = require('../../card/Card');

var base = Rebase.createClass(appConstants.FIREBASE_URL);

class Queue extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      queue: [],
      user: {},
      status: 0
    };
  }
  componentDidMount(){
    var userId = helpers.getCurrentUserId();

    //todo: check to make sure syncState isn't called with asArray
    base.syncState(`queue/${this.props.query.classId}`, {
      context: this,
      state: 'queue',
      asArray: true
    });

    base.bindToState(`users/${userId}/classes/${this.props.query.classId}`, {
      context: this,
      state: 'user'
    });

    base.listenTo(`studentStatus/${this.props.query.classId}`, {
      context: this,
      asArray: true,
      then(data){
        var total = 0;
        data.forEach((item) => {
          total += item;
        });
        var avg = Math.floor((total / data.length));
        isNaN(avg) && (avg = 0);
        avg > 100 && (avg = 100)
        this.setState({
          status: avg
        });
      }
    });
  }
  componentWillUnmount(){
    base.removeBinding(`queue/${this.props.query.classId}`);
    base.removeBinding(`users/${helpers.getCurrentUserId()}/classes/${this.props.query.classId}`);
    base.removeBinding(`studentStatus/${this.props.query.classId}`);
  }
  joinQueue(question, anon){
    var user = JSON.parse(localStorage.getItem('user'));
    var email = anon ? '' : user.email;
    var name = anon ? 'Anonymous' : user.firstName + ' ' + user.lastName;
    var userId = user.pushId;
    var beingHelped = false;
    var beingHelpedBy = false;
    this.setState({
      queue: this.state.queue.concat([{question, anon, email, name, userId, beingHelped}])
    });
  }
  answer(index){
    var temp = this.state.queue;
    var helper = JSON.parse(localStorage.getItem('user'));
    temp[index].beingHelped = helper.firstName + ' ' + helper.lastName;
    this.setState({
      queue: temp
    });
  }
  removeUser(index, item){
    var user = helpers.getLocalUser();
    if(item.email === user.email){
      var temp = this.state.queue;
      temp.splice(index, 1);
      this.setState({
        queue: temp
      })
    }
  }
  updateStatus(status){
    var userId = helpers.getCurrentUserId();
    base.post(`studentStatus/${this.props.query.classId}/${userId}`, {
      data: status,
    });
  }
  render(){
    var enter, list, isAdmin, status, slider;
    var height = 165;
    if(this.state.user.isTeacher || this.state.user.isMentor){
      status = this.state.status;
      enter =
      slider = <SliderGuage status={status} draggable={false} />
      isAdmin = true;
    } else if(this.state.user.isStudent){
      status = 0;
      slider = <SliderGuage status={0} draggable={true} updateStatus={this.updateStatus.bind(this)} />
      enter = (<Card size={12} color={appConstants.blue} height={130}>
                <EnterQueue enter={this.joinQueue.bind(this)} />
              </Card>);
      isAdmin = false;
    }
    list = this.state.queue.map((item, index) => {
      var showRemoveBtn = item.email === helpers.getLocalUser().email;
      if(isAdmin === true){
        return (
          <Card size={12} height={height} color={appConstants.purple} key={index} >
            <QueueItem
              item={item}
              isAdmin={'isAdmin'}
              button={this.answer.bind(this, index)}
              removeUser={this.removeUser.bind(this, index)}
              showRemoveBtn={true} />
          </Card>
        )
      } else {
        return (
          <Card size={12} height={height} color={appConstants.green} key={index}>
            <QueueItem
              item={item}
              isAdmin={isAdmin}
              showRemoveBtn={showRemoveBtn}
              button={this.removeUser.bind(this, index, item)} />
          </Card>
        );
      }
    });
    var className = this.context.router.getCurrentParams().class;
    return (

 {className}

        {slider}

        {enter}

        {list}

    )
  }
};

Queue.contextTypes = {
  router: React.PropTypes.func.isRequired
}

module.exports = Queue;<commit_msg>Bug fix<commit_after>var React = require('react');
var SliderGuage = require('./slider-guage/SliderGuage');
var QueueItem = require('./QueueItem');
var Rebase = require('../../../utils/firebase/rebase');
var appConstants = require('../../../constants/appConstants');
var helpers = require('../../../utils/firebase/helpers');
var EnterQueue = require('./EnterQueue');
var Card = require('../../card/Card');

var base = Rebase.createClass(appConstants.FIREBASE_URL);

class Queue extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      queue: [],
      user: {},
      status: 0
    };
  }
  componentDidMount(){
    var userId = helpers.getCurrentUserId();

    //todo: check to make sure syncState isn't called with asArray
    base.syncState(`queue/${this.props.query.classId}`, {
      context: this,
      state: 'queue',
      asArray: true
    });

    base.bindToState(`users/${userId}/classes/${this.props.query.classId}`, {
      context: this,
      state: 'user'
    });

    base.listenTo(`studentStatus/${this.props.query.classId}`, {
      context: this,
      asArray: true,
      then(data){
        var total = 0;
        data.forEach((item) => {
          total += item;
        });
        var avg = Math.floor((total / data.length));
        isNaN(avg) && (avg = 0);
        avg > 100 && (avg = 100)
        this.setState({
          status: avg
        });
      }
    });
  }
  componentWillUnmount(){
    base.removeBinding(`queue/${this.props.query.classId}`);
    base.removeBinding(`users/${helpers.getCurrentUserId()}/classes/${this.props.query.classId}`);
    base.removeBinding(`studentStatus/${this.props.query.classId}`);
  }
  joinQueue(question, anon){
    var user = JSON.parse(localStorage.getItem('user'));
    var email = anon ? '' : user.email;
    var name = anon ? 'Anonymous' : user.firstName + ' ' + user.lastName;
    var userId = user.pushId;
    var beingHelped = false;
    var beingHelpedBy = false;
    this.setState({
      queue: this.state.queue.concat([{question, anon, email, name, userId, beingHelped}])
    });
  }
  answer(index){
    var temp = this.state.queue;
    var helper = JSON.parse(localStorage.getItem('user'));
    temp[index].beingHelped = helper.firstName + ' ' + helper.lastName;
    this.setState({
      queue: temp
    });
  }
  removeUser(index, item){
    var temp = this.state.queue;
    temp.splice(index, 1);
    this.setState({
      queue: temp
    })
  }
  removeSelf(index, item){
    var user = helpers.getLocalUser();
      if(item.email === user.email){
        var temp = this.state.queue;
        temp.splice(index, 1);
        this.setState({
          queue: temp
        })
    }
  }
  updateStatus(status){
    var userId = helpers.getCurrentUserId();
    base.post(`studentStatus/${this.props.query.classId}/${userId}`, {
      data: status,
    });
  }
  render(){
    var enter, list, isAdmin, status, slider;
    var height = 165;
    if(this.state.user.isTeacher || this.state.user.isMentor){
      status = this.state.status;
      enter =
      slider = <SliderGuage status={status} draggable={false} />
      isAdmin = true;
    } else if(this.state.user.isStudent){
      status = 0;
      slider = <SliderGuage status={0} draggable={true} updateStatus={this.updateStatus.bind(this)} />
      enter = (<Card size={12} color={appConstants.blue} height={130}>
                <EnterQueue enter={this.joinQueue.bind(this)} />
              </Card>);
      isAdmin = false;
    }
    list = this.state.queue.map((item, index) => {
      var showRemoveBtn = item.email === helpers.getLocalUser().email;
      if(isAdmin === true){
        return (
          <Card size={12} height={height} color={appConstants.purple} key={index} >
            <QueueItem
              item={item}
              isAdmin={'isAdmin'}
              button={this.answer.bind(this, index)}
              removeUser={this.removeUser.bind(this, index)}
              showRemoveBtn={true} />
          </Card>
        )
      } else {
        return (
          <Card size={12} height={height} color={appConstants.green} key={index}>
            <QueueItem
              item={item}
              isAdmin={isAdmin}
              showRemoveBtn={showRemoveBtn}
              button={this.removeSelf.bind(this, index, item)} />
          </Card>
        );
      }
    });
    var className = this.context.router.getCurrentParams().class;
    return (

 {className}

        {slider}

        {enter}

        {list}

    )
  }
};

Queue.contextTypes = {
  router: React.PropTypes.func.isRequired
}

module.exports = Queue;<|endoftext|>


<commit_before>// eslint-disable
/*
 * JackpotPage
 *
 * List all the Jackpot
 */
import React from 'react';
import Helmet from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import classNames from 'classnames';
import H1 from 'components/H1';
import messages from './messages';
import styles from './styles.css';

export default class CarouselPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  static propTypes = { jackpotTotal: PropTypes.number };
  static defaultProps = { jackpotTotal: 12345678 };

  constructor(props) {
    super(props);
    this.state = {
      jackpotTotal: props.jackpotTotal,
      jackpot: Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
    };
    this.init = false;
    this.tmpJackpot = Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    this.preJackpot = Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    this.styleNumber = [
      'number0',
      'number1',
      'number2',
      'number3',
      'number4',
      'number5',
      'number6',
      'number7',
      'number8',
      'number9',
    ];
  }

  componentDidMount() {
    this.startCount();
    // this.preJackpot = this.state.jackpot;
  }

  componentDidUpdate() {
    // this.init = true;
    // this.preJackpot = this.state.jackpot;
  }

  componentWillUnmount() {
    clearInterval(this.timerId);
  }

  startCount() {
    this.timerId = setInterval(
      () => this.tick(),
      8500,
    );
    // this.timerId = setTimeout(
    //   () => this.tick(),
    //   1500,
    // );
  }

  tick() {
    // const n = Math.floor(Math.random() * (100));
    // this.setState({ jackpotTotal: (this.state.jackpotTotal + n) });
    this.setState({ jackpotTotal: (this.state.jackpotTotal + 2) });
  }

  render() {
    console.log('==========render start=========');
    const realPotArray = this.state.jackpotTotal.toString().split('');

    let tmpInt = 0;
    // 將預設的12位陣列為0的以map取出, 並確認 真實pot數字位數相同時才放入,否則依照原值放入
    this.preJackpot = this.tmpJackpot;

    this.tmpJackpot = this.state.jackpot.map((pot, index) => {
      if (index >= (this.state.jackpot.size - realPotArray.length)) {
        const newPot = realPotArray[tmpInt];
        tmpInt += 1;
        return newPot;
      }
      return pot;
    });

    if (this.init === false) {
      this.preJackpot = this.tmpJackpot;
      this.init = true;
    }

    // });

    return (

        <Helmet
          title="Jackpot Page"
          meta={[
            { name: 'description', content: 'Jackpot page of React.js Boilerplate application' },
          ]}
        />

          <FormattedMessage {...messages.header} />

            {
              this.tmpJackpot.map((pot, index) => {
                const newKey = index;
                // const newPot = pot;
                let preCount = 0;
                // this.preJackpot = this.preJackpot.set(index, pot);

                const nowCount = Number(this.tmpJackpot.join('').substr(0, index + 1));
                preCount = Number(this.preJackpot.join('').substr(0, index + 1));
                // } else {
                //   preCount = nowCount;
                // }

                //  if (index === 11) {
                //    console.log(tmpHtml, preCount, tmpJackpot, nowCount);
                //  }

                const tmpHtml = [];
                if (index === 11) {
                  console.log(tmpHtml, `preCount:${preCount}`, this.preJackpot.toArray(), `nowCount:${nowCount}`);
                }
                for (let i = preCount; i <= nowCount; i += 1) {
                  const number = i % 10;
                  const numberNewKey = `${newKey}-${number}`;
                  tmpHtml.push(

,
                  );
                }

                // 取得下一個位數共2位數的 數字差,來產生總共幾個數字
                // ex: 988 -> 98
                //    1000 ->100

                const translate3dX = (nowCount - preCount) * -71;
                return (
                  <div
                    className="maiNumber"
                    key={newKey}
                    data-translate3dX={translate3dX}
                    id={`mainNumber${newKey}`}
                    style={{
                      transform: `translate3d(0, ${translate3dX}px, 0px)`,
                    }}
                  >
                    {tmpHtml}

                );
              })
            }

    );
  }
}
<commit_msg>change method ver1<commit_after>// eslint-disable
/*
 * JackpotPage
 *
 * List all the Jackpot
 */
import React from 'react';
import Helmet from 'react-helmet';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
import classNames from 'classnames';
import H1 from 'components/H1';
import messages from './messages';
require('./styles.css');

export default class CarouselPage extends React.Component { // eslint-disable-line react/prefer-stateless-function
  static propTypes = { jackpotTotal: PropTypes.number };
  static defaultProps = { jackpotTotal: 12345678 };

  constructor(props) {
    super(props);
    this.state = {
      jackpotTotal: props.jackpotTotal,
      jackpot: Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
    };
    this.init = false;
    this.tmpJackpot = Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    this.preJackpot = Immutable.List([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
    this.styleNumber = [
      'number0',
      'number1',
      'number2',
      'number3',
      'number4',
      'number5',
      'number6',
      'number7',
      'number8',
      'number9',
    ];
  }

  componentDidMount() {
    this.startCount();
    // this.preJackpot = this.state.jackpot;
  }

  componentDidUpdate() {
    // this.init = true;
    // this.preJackpot = this.state.jackpot;
  }

  componentWillUnmount() {
    clearInterval(this.timerId);
  }

  startCount() {
    this.timerId = setInterval(
      () => this.tick(),
      8500,
    );
    // this.timerId = setTimeout(
    //   () => this.tick(),
    //   1500,
    // );
  }

  tick() {
    // const n = Math.floor(Math.random() * (100));
    // this.setState({ jackpotTotal: (this.state.jackpotTotal + n) });
    this.setState({ jackpotTotal: (this.state.jackpotTotal + 2) });
  }

  render() {
    console.log('==========render start=========');
    const realPotArray = this.state.jackpotTotal.toString().split('');

    let tmpInt = 0;
    // 將預設的12位陣列為0的以map取出, 並確認 真實pot數字位數相同時才放入,否則依照原值放入
    this.preJackpot = this.tmpJackpot;

    this.tmpJackpot = this.state.jackpot.map((pot, index) => {
      if (index >= (this.state.jackpot.size - realPotArray.length)) {
        const newPot = realPotArray[tmpInt];
        tmpInt += 1;
        return newPot;
      }
      return pot;
    });

    if (this.init === false) {
      this.preJackpot = this.tmpJackpot;
      this.init = true;
    }

    // });

    return (

        <Helmet
          title="Jackpot Page"
          meta={[
            { name: 'description', content: 'Jackpot page of React.js Boilerplate application' },
          ]}
        />

          <FormattedMessage {...messages.header} />

            {
              this.tmpJackpot.map((pot, index) => {
                const newKey = index;
                // const newPot = pot;
                let preCount = 0;
                // this.preJackpot = this.preJackpot.set(index, pot);

                const nowCount = Number(this.tmpJackpot.join('').substr(0, index + 1));
                preCount = Number(this.preJackpot.join('').substr(0, index + 1));
                // } else {
                //   preCount = nowCount;
                // }

                //  if (index === 11) {
                //    console.log(tmpHtml, preCount, tmpJackpot, nowCount);
                //  }

                const tmpHtml = [];
                if (index === 11) {
                  console.log(tmpHtml, `preCount:${preCount}`, this.preJackpot.toArray(), `nowCount:${nowCount}`);
                }
                for (let i = preCount; i <= nowCount; i += 1) {
                  const number = i % 10;
                  const numberNewKey = `${newKey}-${number}`;
                  tmpHtml.push(

,
                  );
                }

                // 取得下一個位數共2位數的 數字差,來產生總共幾個數字
                // ex: 988 -> 98
                //    1000 ->100

                const translate3dX = (nowCount - preCount) * -71;
                return (
                  <div
                    className="maiNumber"
                    key={newKey}
                    data-translate3dX={translate3dX}
                    id={`mainNumber${newKey}`}
                    style={{
                      transform: `translate3d(0, ${translate3dX}px, 0px)`,
                    }}
                  >
                    {tmpHtml}

                );
              })
            }

    );
  }
}
<|endoftext|>


<commit_before>angular.module('ngWhisk')
    .controller('requestController', function($scope, $http, $log, $rootScope, ngDialog) {
        $log.log('inside');
        var API_URL = 'https://service.us.apiconnect.ibmcloud.com/gws/apigateway/api/b2844128c00ef617cbec07a2c205b9d411c2d31da094f2120c9054289445ee2f/OpenWhiskContact/api/v1';
        //var API_URL = 'https://6e28847c-de9b-4446-8207-c2fadad1d143-gws.api-gw.mybluemix.net/OpenWhiskContact/api/v1'; //$rootScope.process.env.OWAPI; // OpenWhisk exposed API
        $scope.emailSent = false; // setting message logic to false not display any message by default

        $scope.sendMail = function () { //The send button will call this method to make an API to a OpenWhisk Action exposed as a API

            //scope binding the form data
            var data = {
                myName: $scope.myName,
                myEmail: $scope.myEmail,
                myUrl: $scope.myUrl,
                myDescription: $scope.myDescription
            };
            // adding content type for post header, this is needed for when calling OpenWhisk API
            var config = {
                headers: { accept: 'application/json' } };
                //headers : {
                    //'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
                  //  'Content-Type': 'application/json;charset=UTF-8;',
                  //  'Accept': 'application/json'
                //}
            //};
            // Simple POST request example (passing data) :
            $http.post(API_URL, data, config)
                .then(function(response) {
                    // this callback will be called asynchronously
                    // when the response is available
                    $log.log('Email Sent with response status of: ' + JSON.stringify(response.status));
                    $scope.emailSent = true;

                    // Injecting success into the array of alerts
                    $scope.alerts = [ {type: 'success', msg: ''} ];
                    $scope.alerts[0].msg = 'Thanks, ' + $scope.myName + ' we have got your request, and we will be in touch. ';

                    // After user sent the email, then clear the values to be empty
                    $scope.myName = "";
                    $scope.myEmail = "";
                    $scope.myUrl = "";
                    $scope.myDescription = "";
                }, function(response) {
                    // called asynchronously if an error occurs
                    // or server returns response with an error status.
                    $log.log("Error occurred with status: " + '' + JSON.stringify(response.status));
                    if ($scope.alerts) {
                    // alerts have value
                         // if there was an error, then display this message
                        $scope.alerts[0].msg = JSON.stringify(response.status);
                    } else {
                    // alerts is still null
                         // if there was an error, then display this message
                        $scope.alerts = [ {type: 'danger', msg: ''} ];
                        $scope.alerts[0].msg = JSON.stringify(response.status);
                    }

                    $scope.emailSent = true;
                    $scope.alerts[0].msg = 'Oops! There was a problem sending your request, please contact Johan Rodin directly at johan.rodin@se.ibm.com';
                });
        };

        // Remove alert message area
        $scope.closeAlert = function (index) {
            $scope.alerts.splice(index, 1);
        };

        ///////Handling the popup
        $rootScope.jsonData = '{"foo": "bar"}';
        $rootScope.theme = 'ngdialog-theme-default';
        $scope.open = function () {
            ngDialog.open({ template: 'firstDialogId', controller: 'InsideCtrl' });
        };

        $scope.openDefault = function () {
            ngDialog.open({
                template: 'firstDialogId',
                controller: 'InsideCtrl',
                className: 'ngdialog-theme-default'
            });
        };

        $scope.openPlain = function () {
            $rootScope.theme = 'ngdialog-theme-plain';

            ngDialog.open({
                template: 'firstDialogId',
                controller: 'InsideCtrl',
                className: 'ngdialog-theme-plain'
            });
        };

        $scope.openTemplate = function () {
            $scope.value = true;

            ngDialog.open({
                template: 'externalTemplate.html',
                className: 'ngdialog-theme-plain',
                scope: $scope
            });
        };

    });

//popup ctrl
angular
    .module('ngWhisk')
    .controller('InsideCtrl', function ($scope, ngDialog) {
        $scope.openSecond = function () {
            ngDialog.open({
                template: '
Close all by click here!
',
                plain: true,
                closeByEscape: false,
                controller: 'SecondModalCtrl'
            });
        };
    });

angular
    .module('ngWhisk')
    .controller('SecondModalCtrl', function ($scope, ngDialog) {
        $scope.closeSecond = function () {
            ngDialog.close();
        };
    });

<commit_msg>added myDate<commit_after>angular.module('ngWhisk')
    .controller('requestController', function($scope, $http, $log, $rootScope, ngDialog) {
        $log.log('inside');
        var API_URL = 'https://service.us.apiconnect.ibmcloud.com/gws/apigateway/api/b2844128c00ef617cbec07a2c205b9d411c2d31da094f2120c9054289445ee2f/OpenWhiskContact/api/v1';
        //var API_URL = 'https://6e28847c-de9b-4446-8207-c2fadad1d143-gws.api-gw.mybluemix.net/OpenWhiskContact/api/v1'; //$rootScope.process.env.OWAPI; // OpenWhisk exposed API
        $scope.emailSent = false; // setting message logic to false not display any message by default

        $scope.sendMail = function () { //The send button will call this method to make an API to a OpenWhisk Action exposed as a API

            //scope binding the form data
            var data = {
                myName: $scope.myName,
                myEmail: $scope.myEmail,
                myUrl: $scope.myUrl,
                myDate: $scope.myDate,
                myDescription: $scope.myDescription
            };
            // adding content type for post header, this is needed for when calling OpenWhisk API
            var config = {
                headers: { accept: 'application/json' } };
                //headers : {
                    //'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
                  //  'Content-Type': 'application/json;charset=UTF-8;',
                  //  'Accept': 'application/json'
                //}
            //};
            // Simple POST request example (passing data) :
            $http.post(API_URL, data, config)
                .then(function(response) {
                    // this callback will be called asynchronously
                    // when the response is available
                    $log.log('Email Sent with response status of: ' + JSON.stringify(response.status));
                    $scope.emailSent = true;

                    // Injecting success into the array of alerts
                    $scope.alerts = [ {type: 'success', msg: ''} ];
                    $scope.alerts[0].msg = 'Thanks, ' + $scope.myName + ' we have got your request, and we will be in touch. ';

                    // After user sent the email, then clear the values to be empty
                    $scope.myName = "";
                    $scope.myEmail = "";
                    $scope.myUrl = "";
                    $scope.myDate = "";
                    $scope.myDescription = "";
                }, function(response) {
                    // called asynchronously if an error occurs
                    // or server returns response with an error status.
                    $log.log("Error occurred with status: " + '' + JSON.stringify(response.status));
                    if ($scope.alerts) {
                    // alerts have value
                         // if there was an error, then display this message
                        $scope.alerts[0].msg = JSON.stringify(response.status);
                    } else {
                    // alerts is still null
                         // if there was an error, then display this message
                        $scope.alerts = [ {type: 'danger', msg: ''} ];
                        $scope.alerts[0].msg = JSON.stringify(response.status);
                    }

                    $scope.emailSent = true;
                    $scope.alerts[0].msg = 'Oops! There was a problem sending your request, please contact Johan Rodin directly at johan.rodin@se.ibm.com';
                });
        };

        // Remove alert message area
        $scope.closeAlert = function (index) {
            $scope.alerts.splice(index, 1);
        };

        ///////Handling the popup
        $rootScope.jsonData = '{"foo": "bar"}';
        $rootScope.theme = 'ngdialog-theme-default';
        $scope.open = function () {
            ngDialog.open({ template: 'firstDialogId', controller: 'InsideCtrl' });
        };

        $scope.openDefault = function () {
            ngDialog.open({
                template: 'firstDialogId',
                controller: 'InsideCtrl',
                className: 'ngdialog-theme-default'
            });
        };

        $scope.openPlain = function () {
            $rootScope.theme = 'ngdialog-theme-plain';

            ngDialog.open({
                template: 'firstDialogId',
                controller: 'InsideCtrl',
                className: 'ngdialog-theme-plain'
            });
        };

        $scope.openTemplate = function () {
            $scope.value = true;

            ngDialog.open({
                template: 'externalTemplate.html',
                className: 'ngdialog-theme-plain',
                scope: $scope
            });
        };

    });

//popup ctrl
angular
    .module('ngWhisk')
    .controller('InsideCtrl', function ($scope, ngDialog) {
        $scope.openSecond = function () {
            ngDialog.open({
                template: '
Close all by click here!
',
                plain: true,
                closeByEscape: false,
                controller: 'SecondModalCtrl'
            });
        };
    });

angular
    .module('ngWhisk')
    .controller('SecondModalCtrl', function ($scope, ngDialog) {
        $scope.closeSecond = function () {
            ngDialog.close();
        };
    });

<|endoftext|>


<commit_before>(function(a){a.fn.formbuilder=function(q){var d=a.extend({save_url:false,load_url:false,control_box_target:false,useJson:true,serialize_prefix:"frmb",options_wrap_class:"options-wrap",messages:{save:"Save",add_new_field:"Add New Field...",text:"Text Field",title:"Title",paragraph:"Paragraph",checkboxes:"Checkboxes",radio:"Radio",select:"Select List",text_field:"Text Field",label:"Label",paragraph_field:"Paragraph Field",select_options:"Select Options",add:"Add",checkbox_group:"Checkbox Group",remove_message:"Are you sure you want to remove this element?",
remove:"X",radio_group:"Radio Group",selections_message:"Allow Multiple Selections",hide:"Hide",required:"Required",show:"Show"}},q),l="frmb-"+a("ul[id^=frmb-]").length++;return this.each(function(){var h=a(this).append('

').find("ul"),b="",s="",j=1,k;a(h).addClass(l);(function(e){var c="",f="",g="",m=l+"-control-box",t=l+"-save-button";c+='<option value="0">'+d.messages.add_new_field+"</option>";c+='<option value="input_text">'+d.messages.text+"</option>";c+='<option value="textarea">'+
d.messages.paragraph+"</option>";c+='<option value="checkbox">'+d.messages.checkboxes+"</option>";c+='<option value="radio">'+d.messages.radio+"</option>";c+='<option value="select">'+d.messages.select+"</option>";f='<select id="'+m+'" class="frmb-control">'+c+"</select>";g='<input type="submit" id="'+t+'" class="frmb-submit" value="'+d.messages.save+'"/>';e?a(e).append(f):a(h).before(f);a(h).after(g);a("#"+t).click(function(){a("#schema").attr("value",JSON.stringify(a(h).serializeFormList()));a("#form").submit();
return true});a("#"+m).change(function(){u(a(this).val());a(this).val(0).blur();a("body").animate({scrollTop:a("#frm-"+(j-1)+"-item").offset().top},500);return false})})(d.control_box_target);var u=function(e,c,f,g){b="";s=e;if(typeof c==="undefined")c="";switch(e){case "input_text":v(c,f,g);break;case "textarea":w(c,f,g);break;case "checkbox":x(c,f,g);break;case "radio":y(c,f,g);break;case "select":z(c,f,g);break}},v=function(e,c,f){e="";if(typeof c==="object")e=c[0];b+=""+d.messages.label+
"";b+='<input class="fld-label" id="label-'+j+'" type="text" value="'+unescape(e)+'" />';k="";n(d.messages.text,b,f,k)},w=function(e,c,f){e="";if(typeof c==="object")e=c[0];b+=""+d.messages.label+"";b+='<input type="text" value="'+unescape(e)+'" />';k="";n(d.messages.paragraph_field,b,f,k)},x=function(e,c,f){var g="";if(typeof c==="object")g=c[0];b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+
unescape(g)+'" />
';b+='
'+d.messages.select_options+"
";b+='
';if(typeof e==="object")for(i=0;i<e.length;i++)b+=o(e[i]);else b+=o("");b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.checkbox_group,b,f,k)},o=function(e){var c=false,f="";if(typeof e==="object"){f=e[0];c=e[1]==="false"?false:true}b="";b+="
";b+='<input type="checkbox"'+(c?
' checked="checked"':"")+" />";b+='<input type="text" value="'+unescape(f)+'" />';b+=''+d.messages.remove+"";b+="
";return b},y=function(e,c,f){var g="";if(typeof c==="object")g=c[0];b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+unescape(g)+'" />
';b+='
'+d.messages.select_options+"
";b+='
';
if(typeof e==="object")for(i=0;i<e.length;i++)b+=p(e[i],"frm-"+j+"-fld");else b+=p("","frm-"+j+"-fld");b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.radio_group,b,f,k)},p=function(e,c){var f=false,g="";if(typeof e==="object"){g=e[0];f=e[1]==="false"?false:true}b="";b+="
";b+='<input type="radio"'+(f?' checked="checked"':"")+' name="radio_'+c+'" />';b+='<input type="text" value="'+unescape(g)+'" />';b+=''+d.messages.remove+"";b+="
";return b},z=function(e,c,f){var g=false,m="";if(typeof c==="object"){m=c[0];g=c[1]==="true"?true:false}b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+unescape(m)+'" />
';b+="";b+='
'+d.messages.select_options+"
";b+='
';b+='<input type="checkbox" name="multiple"'+(g?'checked="checked"':"")+
">";b+=''+d.messages.selections_message+"";if(typeof e==="object")for(i=0;i<e.length;i++)b+=r(e[i],g);else b+=r("",g);b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.select,b,f,k)},r=function(e,c){return c?o(e):p(e)},n=function(e,c,f){if(f)f=f===true?true:false;c="";c+='
';c+='
';c+=''+d.messages.hide+
" ";c+=''+e+"
";c+='
';c+='
';c+=b;c+='
'+d.messages.required+"";c+='<input class="required" type="checkbox" value="1" name="required-'+j+'" id="required-'+j+'"'+(f?' checked="checked"':"")+" />
";c+='Delete';
c+="
";c+="
";c+="
";a(h).append(c);a("#frm-"+j+"-item").hide();a("#frm-"+j+"-item").animate({opacity:"show",height:"show"},"slow");j++};(function(e){var c="",f=false;a(e).each(function(){if(this.field_type==="input_text")f=[this.label];if(this.field_type==="textarea")f=[this.label];else if(this.field_type==="checkbox"){f=[this.label];c=[];a.each(this.values,function(){c.push([this.value,this.checked])})}else if(this.field_type==="radio"){f=[this.label];c=[];a.each(this.values,function(){c.push([this.value,
this.checked])})}else if(this.field_type==="select"){f=[this.label,this.multiple];c=[];a.each(this.values,function(){c.push([this.value,this.checked])})}else c=[this.values];u(this.field_type,c,f,this.required)})})(JSON.parse(a("#schema").attr("value")));a(".remove").live("click",function(){a(this).parent("div").animate({opacity:"hide",height:"hide",marginBottom:"0px"},"fast",function(){a(this).remove()});return false});a(".toggle-form").live("click",function(){var e=a(this).attr("id");if(a(this).html()===
d.messages.hide){a(this).removeClass("open").addClass("closed").html(d.messages.show);a("#"+e+"-fld").animate({opacity:"hide",height:"hide"},"slow");return false}if(a(this).html()===d.messages.show){a(this).removeClass("closed").addClass("open").html(d.messages.hide);a("#"+e+"-fld").animate({opacity:"show",height:"show"},"slow");return false}return false});a(".delete-confirm").live("click",function(){var e=a(this).attr("id").replace(/del_/,"");confirm(a(this).attr("label"))&&a("#frm-"+e+"-item").animate({opacity:"hide",
height:"hide",marginBottom:"0px"},"slow",function(){a(this).remove()});return false});a(".add_ck").live("click",function(){a(this).parent().before(o());return false});a(".add_opt").live("click",function(){a(this).parent().before(r("",false));return false});a(".add_rd").live("click",function(){a(this).parent().before(p(false,a(this).parents(".frm-holder").attr("id")));return false})})}})(jQuery);
(function(a){a.fn.serializeFormList=function(q){var d=a.extend({prepend:"ul",is_child:false,attributes:["class"],serialization_attributes:["field_type"]},q),l=[];if(!d.is_child)d.prepend="&"+d.prepend;this.each(function(){a(this).children().each(function(){for(att in d.attributes){var h={};h[d.serialization_attributes[att]]=escape(a(this).attr(d.attributes[att]));if(d.attributes[att]=="class"){h.required=a("#"+a(this).attr("id")+" input.required").attr("checked");switch(a(this).attr(d.attributes[att])){case "input_text":h.label=
escape(a("#"+a(this).attr("id")+" input[type=text]").val());break;case "textarea":h.label=escape(a("#"+a(this).attr("id")+" input[type=text]").val());break;case "checkbox":h.values=[];a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")=="label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break;case "radio":h.values=[];a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")==
"label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break;case "select":h.multiple=a("#"+a(this).attr("id")+" input[name=multiple]").attr("checked");a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")=="label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break}}l.push(h)}})});return l}})(jQuery);
<commit_msg>Update the minified version of the melange formbuilder plugin.<commit_after>(function(a){a.fn.formbuilder=function(q){var d=a.extend({save_url:false,load_url:false,control_box_target:false,useJson:true,serialize_prefix:"frmb",options_wrap_class:"options-wrap",messages:{save:"Save",add_new_field:"Add New Field...",text:"Text Field",title:"Title",paragraph:"Paragraph",checkboxes:"Checkboxes",radio:"Radio",select:"Select List",text_field:"Text Field",label:"Label",paragraph_field:"Paragraph Field",select_options:"Select Options",add:"Add",checkbox_group:"Checkbox Group",remove_message:"Are you sure you want to remove this element?",
remove:"X",radio_group:"Radio Group",selections_message:"Allow Multiple Selections",hide:"Hide",required:"Required",show:"Show"}},q),l="frmb-"+a("ul[id^=frmb-]").length++;return this.each(function(){var h=a(this).append('

').find("ul"),b="",s="",j=1,k;a(h).addClass(l);(function(e){var c="",f="",g="",m=l+"-control-box",t=l+"-save-button";c+='<option value="0">'+d.messages.add_new_field+"</option>";c+='<option value="input_text">'+d.messages.text+"</option>";c+='<option value="textarea">'+
d.messages.paragraph+"</option>";c+='<option value="checkbox">'+d.messages.checkboxes+"</option>";c+='<option value="radio">'+d.messages.radio+"</option>";c+='<option value="select">'+d.messages.select+"</option>";f='<select id="'+m+'" class="frmb-control">'+c+"</select>";g='<input type="submit" id="'+t+'" class="frmb-submit" value="'+d.messages.save+'"/>';e?a(e).append(f):a(h).before(f);a(h).after(g);a("#"+t).click(function(){a("#schema").attr("value",JSON.stringify(a(h).serializeFormList()));a("#form").submit();
return true});a("#"+m).change(function(){u(a(this).val());a(this).val(0).blur();a("body").animate({scrollTop:a("#frm-"+(j-1)+"-item").offset().top},500);return false})})(d.control_box_target);var u=function(e,c,f,g){b="";s=e;if(typeof c==="undefined")c="";switch(e){case "input_text":v(c,f,g);break;case "textarea":w(c,f,g);break;case "checkbox":x(c,f,g);break;case "radio":y(c,f,g);break;case "select":z(c,f,g);break}},v=function(e,c,f){e="";if(typeof c==="object")e=c[0];b+=""+d.messages.label+
"";b+='<input class="fld-label" id="label-'+j+'" type="text" value="'+unescape(e)+'" />';k="";n(d.messages.text,b,f,k)},w=function(e,c,f){e="";if(typeof c==="object")e=c[0];b+=""+d.messages.label+"";b+='<input type="text" value="'+unescape(e)+'" />';k="";n(d.messages.paragraph_field,b,f,k)},x=function(e,c,f){var g="";if(typeof c==="object")g=c[0];b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+
unescape(g)+'" />
';b+='
'+d.messages.select_options+"
";b+='
';if(typeof e==="object")for(i=0;i<e.length;i++)b+=o(e[i]);else b+=o("");b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.checkbox_group,b,f,k)},o=function(e){var c=false,f="";if(typeof e==="object"){f=e[0];c=e[1]==="false"?false:true}b="";b+="
";b+='<input type="checkbox"'+(c?
' checked="checked"':"")+" />";b+='<input type="text" value="'+unescape(f)+'" />';b+=''+d.messages.remove+"";b+="
";return b},y=function(e,c,f){var g="";if(typeof c==="object")g=c[0];b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+unescape(g)+'" />
';b+='
'+d.messages.select_options+"
";b+='
';if(typeof e==="object")for(i=0;i<e.length;i++)b+=p(e[i],"frm-"+j+"-fld");else b+=p("","frm-"+j+"-fld");b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.radio_group,b,f,k)},p=function(e,c){var f=false,g="";if(typeof e==="object"){g=e[0];f=e[1]==="false"?false:true}b="";b+="
";b+='<input type="radio"'+(f?' checked="checked"':"")+' name="radio_'+c+'" />';b+='<input type="text" value="'+unescape(g)+
'" />';b+=''+d.messages.remove+"";b+="
";return b},z=function(e,c,f){var g=false,m="";if(typeof c==="object"){m=c[0];g=c[1]==="true"?true:false}b+='
';b+='
'+d.messages.label+"";b+='<input type="text" name="label" value="'+unescape(m)+'" />
';b+="";b+='
'+d.messages.select_options+"
";b+='
';
b+='<input type="checkbox" name="multiple"'+(g?'checked="checked"':"")+">";b+=''+d.messages.selections_message+"";if(typeof e==="object")for(i=0;i<e.length;i++)b+=r(e[i],g);else b+=r("",g);b+='
'+d.messages.add+"
";b+="
";b+="
";k="";n(d.messages.select,b,f,k)},r=function(e,c){return c?o(e):p(e)},n=function(e,c,f){if(f)f=f===true?true:false;c="";c+='
';c+='
';
c+=''+d.messages.hide+" ";c+=''+e+"
";c+='
';c+='
';c+=b;c+='
'+d.messages.required+"";c+='<input class="required" type="checkbox" value="1" name="required-'+j+'" id="required-'+j+'"'+(f?' checked="checked"':"")+" />
";c+='Delete';c+="
";c+="
";c+="
";a(h).append(c);a("#frm-"+j+"-item").hide();a("#frm-"+j+"-item").animate({opacity:"show",height:"show"},"slow");j++};(function(e){var c="",f=false;a(e).each(function(){if(this.field_type==="input_text")f=[this.label];if(this.field_type==="textarea")f=[this.label];else if(this.field_type==="checkbox"){f=[this.label];c=[];a.each(this.values,function(){c.push([this.value,this.checked])})}else if(this.field_type===
"radio"){f=[this.label];c=[];a.each(this.values,function(){c.push([this.value,this.checked])})}else if(this.field_type==="select"){f=[this.label,this.multiple];c=[];a.each(this.values,function(){c.push([this.value,this.checked])})}else c=[this.values];u(this.field_type,c,f,this.required)})})(JSON.parse(a("#schema").attr("value")));a(".remove").live("click",function(){a(this).parent("div").animate({opacity:"hide",height:"hide",marginBottom:"0px"},"fast",function(){a(this).remove()});return false});
a(".toggle-form").live("click",function(){var e=a(this).attr("id");if(a(this).html()===d.messages.hide){a(this).removeClass("open").addClass("closed").html(d.messages.show);a("#"+e+"-fld").animate({opacity:"hide",height:"hide"},"slow");return false}if(a(this).html()===d.messages.show){a(this).removeClass("closed").addClass("open").html(d.messages.hide);a("#"+e+"-fld").animate({opacity:"show",height:"show"},"slow");return false}return false});a(".delete-confirm").live("click",function(){var e=a(this).attr("id").replace(/del_/,
"");confirm(a(this).attr("label"))&&a("#frm-"+e+"-item").animate({opacity:"hide",height:"hide",marginBottom:"0px"},"slow",function(){a(this).remove()});return false});a(".add_ck").live("click",function(){a(this).parent().before(o());return false});a(".add_opt").live("click",function(){a(this).parent().before(r("",false));return false});a(".add_rd").live("click",function(){a(this).parent().before(p(false,a(this).parents(".frm-holder").attr("id")));return false})})}})(jQuery);
(function(a){a.fn.serializeFormList=function(q){var d=a.extend({prepend:"ul",is_child:false,attributes:["class"],serialization_attributes:["field_type"]},q),l=[];if(!d.is_child)d.prepend="&"+d.prepend;this.each(function(){a(this).children().each(function(){for(att in d.attributes){var h={};h[d.serialization_attributes[att]]=escape(a(this).attr(d.attributes[att]));if(d.attributes[att]=="class"){h.required=a("#"+a(this).attr("id")+" input.required").attr("checked");switch(a(this).attr(d.attributes[att])){case "input_text":h.label=
escape(a("#"+a(this).attr("id")+" input[type=text]").val());break;case "textarea":h.label=escape(a("#"+a(this).attr("id")+" input[type=text]").val());break;case "checkbox":h.values=[];a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")=="label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break;case "radio":h.values=[];a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")==
"label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break;case "select":h.multiple=a("#"+a(this).attr("id")+" input[name=multiple]").attr("checked");a("#"+a(this).attr("id")+" input[type=text]").each(function(){if(a(this).attr("name")=="label")h.label=escape(a(this).val());else{var b={};b.value=escape(a(this).val());b.checked=a(this).prev().attr("checked");h.values.push(b)}});break}}l.push(h)}})});return l}})(jQuery);
<|endoftext|>


<commit_before>'use strict';
/* This service handles everything related to configuration files
 *
 * It is a provider of resources and services.
 *
 * this.configuration needs to be set from the outside through
 * defineConfiguration(), typically by a route that enters the application.
 *
 *
 *
 * As of now a valid conf file contains four sections
 *   main
 *   navbar
 *   plugins
 *   resources
 *
 * Uses $http to retrieve additional configuration files that are embedded
 * through the fileUrl property.
 * This could eventually be replaced by using the arethusa resource service
 * once it's clear where such conf files will be stored. Or maybe not,
 * we'll see.
 *
 */
angular.module('arethusa.core').service('configurator', [
  '$injector',
  '$http',
  '$rootScope',
  'Resource',
  '$timeout',
  'Loader',
  function ($injector, $http, $rootScope, Resource, $timeout, Loader) {
    var self = this;
    var includeParam = 'fileUrl';

    var loader = new Loader();

    function filesToInclude(obj) {
      return arethusaUtil.findNestedProperties(obj, includeParam)[includeParam];
    }

    function checkLoadStatus() {
      if (loader.allLoaded()) {
        broadcastLoading();
      }
    }

    function broadcastLoading() {
      $rootScope.$broadcast('confLoaded');
    }

    // While using a timeout might seem hacky, it's just the write tool for the
    // job.
    // A run of the program looks like this:
    //
    // 1 Route with resolve function waits for the configurator to start
    // 2 Route resolves to start the MainCtrl
    // 3 MainCtrl waits for confLoaded event
    // 4 MainCtrl initializes the State object
    // 5 MainCtrl waits for stateLoaded event
    // 6 MainCtrl initializes itself and all plugins
    //
    // When the configurator needs to load external files (in 1)
    // the route resolves before the configurator is finished.
    // Only when it is finished the confLoaded event is fired.
    // External files are loaded asynchonously, the event is fired
    // from within a callback.
    // It is therefore guaranteed to happen after 2. Step 2 starts
    // the MainCtrl - it is the place where the listener to the
    // confLoaded event is defined.
    //
    // If there are no external files to load, the configurator gets
    // a chance to fire his confLoaded event while still being in step 1.
    // The event is fired, but as 2 hasn't run yet, there are no listeners.
    // The application fails to load properly.
    //
    // We therefore wrap the confLoaded event in a $timeout function.
    // Timeout with a delay of 0 defers the execution of the wrapped
    // function to the end of the current execution queue.
    // This is just what we need. Step 2 is already in the execution queue
    // at this time - if we can guarantee that the confLoaded event
    // happens after 2, everything is fine.
    function delayedBroadcast() {
      $timeout(function () {
        broadcastLoading();
      });
    }

    function includeExternalFiles(arrayOfObjects) {
      // If we have multiple files to include on the same level,
      // we need to declare all unloaded before we start including
      // files.
      angular.forEach(arrayOfObjects, function(obj, i) {
        loader.declareUnloaded(obj);
      });

      angular.forEach(arrayOfObjects, function (obj, i) {
        $http.get(obj[includeParam]).then(function (res) {
          // We have to delete fileUrl upfront.
          // When the object gets extended by a response, this response
          // might contain another fileUrl property. If that is the case
          // we then go on to look for such properties and call the function
          // recursively.
          delete obj[includeParam];
          angular.extend(obj, res.data);
          // As we want to load all potential external conf file BEFORE the application
          // starts, we have to apply this little trick:
          // Before the asynchronous call to retrieve an external file is made, we
          // declare a false load status, which we resolve inside the callback.
          //
          // Once we say we are loaded, we immediately check if our now extended object
          // has another external file to load. If it does, the load status of our
          // object will be immediately false again.
          // On the end of each callback we check all load states. If everything has
          // loaded successfully, we broadcast an event an let others now that the
          // configurator is finished.
          loader.declareLoaded(obj);
          includeExternalFiles(filesToInclude(obj));
          checkLoadStatus();
        });
      });
    }

    // Receives an external file and resolves it to a valid configuration file.
    // The second param is optional.
    this.defineConfiguration = function (confFile, location) {
      var conf = this.loadConfFile(confFile, location);
      this.configuration = conf.data;
      this.confFileLocation = conf.location;
    };

    this.loadConfFile = function (confFile, location) {
      var conf = {
          location: location,
          data: confFile
        };
      var fti = filesToInclude(conf.data);
      if (fti.length === 0) {
        delayedBroadcast();
      } else {
        // this function will eventually broadcast the load event
        includeExternalFiles(fti);
      }
      return conf;
    };

    // Returns an empty configuration files with all sections
    // as empty object properties.
    // Useful for the configuration editor.
    this.getConfTemplate = function () {
      return {
        main: {},
        navbar: {},
        plugins: {},
        resources: {}
      };
    };

    // Merges two configuration objects.
    // There is a clear contract that has to be fulfilled to make this work:
    //
    // The datatypes of individual properties need to be static.
    // E.g.
    //
    // {
    //   plugins: {
    //     morph: {
    //       retrievers: ['x']
    //     }
    //   }
    // }
    //
    // If plugins.morph.retrievers is an Array, it can only be an Array and nothing
    // else. The same goes for Objects, Strings, and Numbers.
    //
    // Objects call the function recursively.
    // Arrays are flat-pushed.
    // Strings and Numbers are overwritten.
    // a is extended with properties in b, that are not present in a.
    this.mergeConfigurations = function (a, b) {
      var that = this;
      angular.forEach(b, function (value, key) {
        var origVal = a[key];
        if (origVal) {
          // Every Array is an Object, but not every Object is an Array!
          // This defines the order of the if-else conditional.
          if (angular.isArray(origVal)) {
            arethusaUtil.pushAll(origVal, value);
          } else if (angular.isObject(origVal)) {
            that.mergeConfigurations(origVal, value);
          } else {
            a[key] = value;
          }
        } else {
          a[key] = value;
        }
      });
      return a;
    };

    this.getService = function (serviceName) {
      return $injector.get(serviceName);
    };

    this.getServices = function (serviceNames) {
      if (serviceNames) {
        var that = this;
        // inject to an object, we want the names as well
        return arethusaUtil.inject({}, serviceNames, function (obj, name) {
          obj[name] = that.getService(name);
        });
      } else {
        return {};
      }
    };

    // right now very hacky, not sure about the design of the conf file atm
    // we therefore just tell the service where the conf for specific things
    // is to be found in the JSON tree.
    // I guess the key is to abstract the conf file a little more.
    this.configurationFor = function (plugin) {
      var conf = this.configuration;
      return conf[plugin] || conf.plugins[plugin] || conf.resources[plugin];
    };

    function standardProperties() {
      return [
        'name',
        'main',
        'template',
        'external',
        'listener',
        'contextMenu',
        'contextMenuTemplate',
        'noView'
      ];
    }

    // Delegates a set of standard properties to the given object to allow
    // a more direct access.
    this.delegateConf = function (obj, otherKeys) {
      var props = arethusaUtil.pushAll(standardProperties(), otherKeys);
      angular.forEach(props, function (property, i) {
        obj[property] = obj.conf[property];
      });
    };

    this.getConfAndDelegate = function (name, obj, keys) {
      obj.conf = self.configurationFor(name);
      self.delegateConf(obj, keys);
      return obj;
    };

    this.getRetrievers = function (retrievers) {
      return arethusaUtil.inject({}, retrievers, function (memo, name, conf) {
        var Retriever = self.getService(name);
        memo[name] = new Retriever(conf);
      });
    };
    // We alias this for now as the function has to do the same -
    // we might need a new name for it but we'll fix that later
    this.getPersisters = this.getRetrievers;

    this.getRetriever = function(retrievers) {
      var retrs = self.getRetrievers(retrievers);
      return retrs[Object.keys(retrs)[0]];
    };

    this.provideResource = function (name) {
      var conf = this.configuration.resources[name];
      var resource = new Resource(conf);
      resource.auth = self.getAuth(conf.auth);
      return resource;
    };

    this.getAuth = function(name) {
      return self.configuration.auths[name];
    };
  }
]);
<commit_msg>Pass the auth object as argument to the Resource<commit_after>'use strict';
/* This service handles everything related to configuration files
 *
 * It is a provider of resources and services.
 *
 * this.configuration needs to be set from the outside through
 * defineConfiguration(), typically by a route that enters the application.
 *
 *
 *
 * As of now a valid conf file contains four sections
 *   main
 *   navbar
 *   plugins
 *   resources
 *
 * Uses $http to retrieve additional configuration files that are embedded
 * through the fileUrl property.
 * This could eventually be replaced by using the arethusa resource service
 * once it's clear where such conf files will be stored. Or maybe not,
 * we'll see.
 *
 */
angular.module('arethusa.core').service('configurator', [
  '$injector',
  '$http',
  '$rootScope',
  'Resource',
  '$timeout',
  'Loader',
  function ($injector, $http, $rootScope, Resource, $timeout, Loader) {
    var self = this;
    var includeParam = 'fileUrl';

    var loader = new Loader();

    function filesToInclude(obj) {
      return arethusaUtil.findNestedProperties(obj, includeParam)[includeParam];
    }

    function checkLoadStatus() {
      if (loader.allLoaded()) {
        broadcastLoading();
      }
    }

    function broadcastLoading() {
      $rootScope.$broadcast('confLoaded');
    }

    // While using a timeout might seem hacky, it's just the write tool for the
    // job.
    // A run of the program looks like this:
    //
    // 1 Route with resolve function waits for the configurator to start
    // 2 Route resolves to start the MainCtrl
    // 3 MainCtrl waits for confLoaded event
    // 4 MainCtrl initializes the State object
    // 5 MainCtrl waits for stateLoaded event
    // 6 MainCtrl initializes itself and all plugins
    //
    // When the configurator needs to load external files (in 1)
    // the route resolves before the configurator is finished.
    // Only when it is finished the confLoaded event is fired.
    // External files are loaded asynchonously, the event is fired
    // from within a callback.
    // It is therefore guaranteed to happen after 2. Step 2 starts
    // the MainCtrl - it is the place where the listener to the
    // confLoaded event is defined.
    //
    // If there are no external files to load, the configurator gets
    // a chance to fire his confLoaded event while still being in step 1.
    // The event is fired, but as 2 hasn't run yet, there are no listeners.
    // The application fails to load properly.
    //
    // We therefore wrap the confLoaded event in a $timeout function.
    // Timeout with a delay of 0 defers the execution of the wrapped
    // function to the end of the current execution queue.
    // This is just what we need. Step 2 is already in the execution queue
    // at this time - if we can guarantee that the confLoaded event
    // happens after 2, everything is fine.
    function delayedBroadcast() {
      $timeout(function () {
        broadcastLoading();
      });
    }

    function includeExternalFiles(arrayOfObjects) {
      // If we have multiple files to include on the same level,
      // we need to declare all unloaded before we start including
      // files.
      angular.forEach(arrayOfObjects, function(obj, i) {
        loader.declareUnloaded(obj);
      });

      angular.forEach(arrayOfObjects, function (obj, i) {
        $http.get(obj[includeParam]).then(function (res) {
          // We have to delete fileUrl upfront.
          // When the object gets extended by a response, this response
          // might contain another fileUrl property. If that is the case
          // we then go on to look for such properties and call the function
          // recursively.
          delete obj[includeParam];
          angular.extend(obj, res.data);
          // As we want to load all potential external conf file BEFORE the application
          // starts, we have to apply this little trick:
          // Before the asynchronous call to retrieve an external file is made, we
          // declare a false load status, which we resolve inside the callback.
          //
          // Once we say we are loaded, we immediately check if our now extended object
          // has another external file to load. If it does, the load status of our
          // object will be immediately false again.
          // On the end of each callback we check all load states. If everything has
          // loaded successfully, we broadcast an event an let others now that the
          // configurator is finished.
          loader.declareLoaded(obj);
          includeExternalFiles(filesToInclude(obj));
          checkLoadStatus();
        });
      });
    }

    // Receives an external file and resolves it to a valid configuration file.
    // The second param is optional.
    this.defineConfiguration = function (confFile, location) {
      var conf = this.loadConfFile(confFile, location);
      this.configuration = conf.data;
      this.confFileLocation = conf.location;
    };

    this.loadConfFile = function (confFile, location) {
      var conf = {
          location: location,
          data: confFile
        };
      var fti = filesToInclude(conf.data);
      if (fti.length === 0) {
        delayedBroadcast();
      } else {
        // this function will eventually broadcast the load event
        includeExternalFiles(fti);
      }
      return conf;
    };

    // Returns an empty configuration files with all sections
    // as empty object properties.
    // Useful for the configuration editor.
    this.getConfTemplate = function () {
      return {
        main: {},
        navbar: {},
        plugins: {},
        resources: {}
      };
    };

    // Merges two configuration objects.
    // There is a clear contract that has to be fulfilled to make this work:
    //
    // The datatypes of individual properties need to be static.
    // E.g.
    //
    // {
    //   plugins: {
    //     morph: {
    //       retrievers: ['x']
    //     }
    //   }
    // }
    //
    // If plugins.morph.retrievers is an Array, it can only be an Array and nothing
    // else. The same goes for Objects, Strings, and Numbers.
    //
    // Objects call the function recursively.
    // Arrays are flat-pushed.
    // Strings and Numbers are overwritten.
    // a is extended with properties in b, that are not present in a.
    this.mergeConfigurations = function (a, b) {
      var that = this;
      angular.forEach(b, function (value, key) {
        var origVal = a[key];
        if (origVal) {
          // Every Array is an Object, but not every Object is an Array!
          // This defines the order of the if-else conditional.
          if (angular.isArray(origVal)) {
            arethusaUtil.pushAll(origVal, value);
          } else if (angular.isObject(origVal)) {
            that.mergeConfigurations(origVal, value);
          } else {
            a[key] = value;
          }
        } else {
          a[key] = value;
        }
      });
      return a;
    };

    this.getService = function (serviceName) {
      return $injector.get(serviceName);
    };

    this.getServices = function (serviceNames) {
      if (serviceNames) {
        var that = this;
        // inject to an object, we want the names as well
        return arethusaUtil.inject({}, serviceNames, function (obj, name) {
          obj[name] = that.getService(name);
        });
      } else {
        return {};
      }
    };

    // right now very hacky, not sure about the design of the conf file atm
    // we therefore just tell the service where the conf for specific things
    // is to be found in the JSON tree.
    // I guess the key is to abstract the conf file a little more.
    this.configurationFor = function (plugin) {
      var conf = this.configuration;
      return conf[plugin] || conf.plugins[plugin] || conf.resources[plugin];
    };

    function standardProperties() {
      return [
        'name',
        'main',
        'template',
        'external',
        'listener',
        'contextMenu',
        'contextMenuTemplate',
        'noView'
      ];
    }

    // Delegates a set of standard properties to the given object to allow
    // a more direct access.
    this.delegateConf = function (obj, otherKeys) {
      var props = arethusaUtil.pushAll(standardProperties(), otherKeys);
      angular.forEach(props, function (property, i) {
        obj[property] = obj.conf[property];
      });
    };

    this.getConfAndDelegate = function (name, obj, keys) {
      obj.conf = self.configurationFor(name);
      self.delegateConf(obj, keys);
      return obj;
    };

    this.getRetrievers = function (retrievers) {
      return arethusaUtil.inject({}, retrievers, function (memo, name, conf) {
        var Retriever = self.getService(name);
        memo[name] = new Retriever(conf);
      });
    };
    // We alias this for now as the function has to do the same -
    // we might need a new name for it but we'll fix that later
    this.getPersisters = this.getRetrievers;

    this.getRetriever = function(retrievers) {
      var retrs = self.getRetrievers(retrievers);
      return retrs[Object.keys(retrs)[0]];
    };

    this.provideResource = function (name) {
      var conf = this.configuration.resources[name];
      return new Resource(conf, self.getAuth(conf.auth));
    };

    this.getAuth = function(name) {
      return self.configuration.auths[name];
    };
  }
]);
<|endoftext|>


<commit_before><commit_msg>Added more tests to setView<commit_after><|endoftext|>