<commit_before>(function(){

L.Control.EasyButton = L.Control.extend({

  options: {
    position:  'topleft', // part of leaflet's defaults

    id:        null,      // an id to tag the container with

    type:      'replace', // [(replace|animate)]
                          // replace swaps out elements
                          // animate changes classes with all elements inserted

    auto:      'NOOP',    // [(CYCLE|FORWARD|REVERSE-CYCLE|BACK|NOOP)]
                          // CYCLE => after callback go to the next state (loop to beginning from the end)
                          // FORWARD => after callback go to the next state (stop at end)
                          // REVERSE-CYCLE => after callback go to the previous state (loop to the end from the beginning)
                          // BACK => after callback go to the next state (stop at beginning)
                          // NOOP => no operation

    states:    [],        // state names look like this
                          // {
                          //   stateName: 'untracked',
                          //   callback: function(){ track_Something(); this.state('tracking'); },
                          //   title: 'click to make inactive',
                          //   icon: 'fa-circle',    // wrapped with
                          // }

    extraHTML: null,      // extra html to be inserted in
                          // the container. useful for indicators
  },

  initialize: function(uno, dos, tres){

    this.options.states = [];

    // is the last item an object?
    if( typeof arguments[arguments.length-1] === "object" ){

      // if so, it should be the options
      L.Util.setOptions( this, arguments[arguments.length-1] );
    }

    //if there aren't any states set manually
    if( this.options.states.length === 0 ){

      // if the uno argument is a string, set it
      if( uno && typeof uno  === "string"){
        L.Util.setOptions( this, { icon: uno});
      }

      // if the dos argument is a function, set it
      if( dos && typeof dos === 'function'){
        L.Util.setOptions( this, { callback: dos});
      }

      // turn the options object into a state
      this.options.states.push(Object.create(this.options));
    }

    this._states = curateStates(this);

  },

  onAdd: function () {

    this.container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
    this.options.id && (this.container.id = this.options.id);

    this.container.innerHTML = this.options.extraHTML;

    if(this.options.type == 'replace'){
      this.container.appendChild(this._currentState.icon);
    } else {
      for(var i=0;i<this._states.length;i++){
        this.container.appendChild(this._states[i].icon);
      }
    }

    this.state(0);

    return this.container;
  },

  _currentState: {
    // placeholder content
    stateName: 'unnamed',
    icon: (function(){ return document.createElement('span'); })()
  },

  _states: null, // populated on init

  state: function(newState){

    if(typeof newState == "string"){
      switch (newState){
        case "FORWARD":
          this._forward();
          break;
        case "CYCLE":
        case "FORWARD-CYCLE":
          this._forwardCycle();
          break;
        case "REVERSE":
          this._reverse();
          break;
        case "REV-CYCLE":
        case "REVERSE-CYCLE":
          this._reverseCycle();
          break;
        case "NOOP":
          break;
        default:
          this._activateStateNamed(newState);
          break;
      }
    } else if (typeof newState == "number"){
      this._activateState(this._states[newState]);
    }

    return this;
  },

  _activateStateNamed: function(stateName){
    for(var i = 0; i < this._states.length; i++){
      if( this._states[i].stateName == stateName ){
        this._activateState( this._states[i] );
      }
    }
  },

  _activateState: function(newState){

    if( this.options.type == 'replace' ){
      this.container.appendChild(newState.icon);
      this.container.removeChild(this._currentState.icon);
    }

    // update classes
    for(var i=0;i<this._states.length;i++){
      L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
      L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
    }

    L.DomUtil.removeClass(this.container, this._currentState.stateName + '-active');
    L.DomUtil.addClass(this.container, newState.stateName + '-active');

    this._currentState = newState;
  },

  _forward: function(){
    var index = this._states.indexOf(this._currentState);
    index++;
    if( index < this._states.length ){
      this._activateState(this._states[index]);
    }
  },

  _reverse: function(){
    var index = this._states.indexOf(this._currentState);
    index--;
    if( index >= 0 ){
      this._activateState(this._states[index]);
    }
  },

  _forwardCycle: function(){
    var index = this._states.indexOf(this._currentState);
    index++;
    if( index < this._states.length ){
      this._activateState(this._states[index]);
    } else {
      this._activateState(this._states[0]);
    }
  },

  _reverseCycle: function(){
    var index = this._states.indexOf(this._currentState);
    index--;
    if( index >= 0 ){
      this._activateState(this._states[index]);
    } else {
      this._activateState(this._states[this._states.length - 1]);
    }
  },

  enable: function(){
    L.DomUtil.addClass(this.container, 'enabled');
    L.DomUtil.removeClass(this.container, 'disabled');
    return this;
  },

  disable: function(){
    L.DomUtil.addClass(this.container, 'disabled');
    L.DomUtil.removeClass(this.container, 'enabled');
    return this;
  }

});

L.easyButton = function(){
  var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments)
  return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
};

/*************************
 *
 * util functions
 *
 *************************/

function buildIcon(ambiguousIconString) {

  var tmpIcon;

  // does this look like html? (i.e. not a class)
  if( ambiguousIconString.match(/[&;=<>"']/) ){

    // if so, the user should have put in html
    // so move forward as such
    tmpIcon = ambiguousIconString;

  } else { // it's a class list, figure out what kind
      ambiguousIconString = ambiguousIconString.trim();
      tmpIcon = L.DomUtil.create('span', '', this.link);

      if( ambiguousIconString.indexOf('fa-') === 0 ){
        L.DomUtil.addClass(tmpIcon, "fa fa-lg "  + ambiguousIconString)

      } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
        L.DomUtil.addClass(tmpIcon, "glyphicon " + ambiguousIconString)

      } else {
        L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
      }

      // make this a string so that it's easy to set innerHTML below
      tmpIcon = tmpIcon.outerHTML;
  }

  return tmpIcon;
}

function curateStates(easyButton){
  var curatedStates = [],
      dirtyStates = easyButton.options.states;

  for(var i = 0; i < easyButton.options.states.length; i++){
    curatedStates.push( curateState(easyButton.options.states[i], easyButton) );
  }

  return curatedStates;

  function curateState(state, newThis){
    var cleanState = {};

    cleanState.title = state.title;
    cleanState.stateName = state.stateName;

    cleanState.icon = L.DomUtil.create('a', 'easy-button-button leaflet-bar-part');
    cleanState.icon.href = 'javascript:void(0);';
    state.title && (cleanState.icon.title = state.title);
    cleanState.icon.innerHTML = buildIcon(state.icon);

    L.DomEvent
      .addListener(
        cleanState.icon,
        'click',
        boundWithAuto(state.callback, newThis)
      );

    return cleanState;
  }
}

function boundWithAuto(usersCallback, ebObject){
  var callback = usersCallback,
      tmp = ebObject.options.auto,
      cleanup = L.DomUtil.false,
      end = L.Util.bind(function(){
        this._map.getContainer().focus();
        cleanup();
      }, ebObject);
  if( typeof callback == 'function'){
    callback = L.Util.bind(callback, ebObject);
  } else {
    callback = L.Util.false
  }

  if( ebObject.options.auto ){
    cleanup = L.Util.bind(function(){ this.state(tmp) }, ebObject);
  }

  // will have `this` set approprately
  return function(e){
    L.DomEvent.stop(e);
    callback();
    end();
  };

}

})();
<commit_msg>fire callback upon entering state<commit_after>(function(){

L.Control.EasyButton = L.Control.extend({

  options: {
    position:  'topleft', // part of leaflet's defaults

    id:        null,      // an id to tag the container with

    type:      'replace', // [(replace|animate)]
                          // replace swaps out elements
                          // animate changes classes with all elements inserted

    auto:      'CYCLE',   // [(CYCLE|FORWARD|REVERSE-CYCLE|BACK|NOOP)]
                          // CYCLE => after callback go to the next state (loop to beginning from the end)
                          // FORWARD => after callback go to the next state (stop at end)
                          // REVERSE-CYCLE => after callback go to the previous state (loop to the end from the beginning)
                          // BACK => after callback go to the next state (stop at beginning)
                          // NOOP => no operation

    states:    [],        // state names look like this
                          // {
                          //   stateName: 'untracked',
                          //   callback: function(){ track_Something(); this.state('tracking'); },
                          //   title: 'click to make inactive',
                          //   icon: 'fa-circle',    // wrapped with
                          // }

    extraHTML: null,      // extra html to be inserted in
                          // the container. useful for indicators
  },

  initialize: function(uno, dos, tres){

    this.options.states = [];

    // is the last item an object?
    if( typeof arguments[arguments.length-1] === "object" ){

      // if so, it should be the options
      L.Util.setOptions( this, arguments[arguments.length-1] );
    }

    //if there aren't any states set manually
    if( this.options.states.length === 0 ){

      // if the uno argument is a string, set it
      if( uno && typeof uno  === "string"){
        L.Util.setOptions( this, { icon: uno});
      }

      // if the dos argument is a function, set it
      if( dos && typeof dos === 'function'){
        L.Util.setOptions( this, { callback: dos});
      }

      // turn the options object into a state
      this.options.states.push(Object.create(this.options));
    }

    this._states = curateStates(this);

  },

  onAdd: function () {

    this.container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
    this.options.id && (this.container.id = this.options.id);

    this.container.innerHTML = this.options.extraHTML;

    // prep the contents of the control
    if(this.options.type == 'replace'){
      this._currentState = this._states[0];
      this.container.appendChild(this._currentState.icon);
    } else {
      for(var i=0;i<this._states.length;i++){
        this.container.appendChild(this._states[i].icon);
      }
    }

    //this.state(0);

    return this.container;
  },

  _currentState: {
    // placeholder content
    stateName: 'unnamed',
    icon: (function(){ return document.createElement('span'); })()
  },

  _states: null, // populated on init

  state: function(newState){

    if(typeof newState == "string"){
      switch (newState){
        case "FORWARD":
          this._forward();
          break;
        case "CYCLE":
        case "FORWARD-CYCLE":
          this._forwardCycle();
          break;
        case "REVERSE":
          this._reverse();
          break;
        case "REV-CYCLE":
        case "REVERSE-CYCLE":
          this._reverseCycle();
          break;
        case "NOOP":
          break;
        default:
          this._activateStateNamed(newState);
          break;
      }
    } else if (typeof newState == "number"){
      this._activateState(this._states[newState]);
    }

    return this;
  },

  _activateStateNamed: function(stateName){
    for(var i = 0; i < this._states.length; i++){
      if( this._states[i].stateName == stateName ){
        this._activateState( this._states[i] );
      }
    }
  },

  _activateState: function(newState){

    newState.callback();

    // don't touch the dom if it'll just be the same after
    if( newState.icon !== this._currentState.icon){

      // swap out elements... if you're into that kind of thing
      if( this.options.type == 'replace' ){
        this.container.appendChild(newState.icon);
        this.container.removeChild(this._currentState.icon);
      }

      // update classes for animations
      for(var i=0;i<this._states.length;i++){
        L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
        L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
      }

      // update classes for animations
      L.DomUtil.removeClass(this.container, this._currentState.stateName + '-active');
      L.DomUtil.addClass(this.container, newState.stateName + '-active');

      // update the record
      this._currentState = newState;

    }
  },

  _forward: function(){
    var index = this._states.indexOf(this._currentState);
    index++;
    if( index < this._states.length ){
      this._activateState(this._states[index]);
    }
  },

  _reverse: function(){
    var index = this._states.indexOf(this._currentState);
    index--;
    if( index >= 0 ){
      this._activateState(this._states[index]);
    }
  },

  _forwardCycle: function(){
    var index = this._states.indexOf(this._currentState);
    index++;
    if( index < this._states.length ){
      this._activateState(this._states[index]);
    } else {
      this._activateState(this._states[0]);
    }
  },

  _reverseCycle: function(){
    var index = this._states.indexOf(this._currentState);
    index--;
    if( index >= 0 ){
      this._activateState(this._states[index]);
    } else {
      this._activateState(this._states[this._states.length - 1]);
    }
  },

  enable: function(){
    L.DomUtil.addClass(this.container, 'enabled');
    L.DomUtil.removeClass(this.container, 'disabled');
    return this;
  },

  disable: function(){
    L.DomUtil.addClass(this.container, 'disabled');
    L.DomUtil.removeClass(this.container, 'enabled');
    return this;
  }

});

L.easyButton = function(){
  var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments)
  return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
};

/*************************
 *
 * util functions
 *
 *************************/

function buildIcon(ambiguousIconString) {

  var tmpIcon;

  // does this look like html? (i.e. not a class)
  if( ambiguousIconString.match(/[&;=<>"']/) ){

    // if so, the user should have put in html
    // so move forward as such
    tmpIcon = ambiguousIconString;

  } else { // it's a class list, figure out what kind
      ambiguousIconString = ambiguousIconString.trim();
      tmpIcon = L.DomUtil.create('span', '', this.link);

      if( ambiguousIconString.indexOf('fa-') === 0 ){
        L.DomUtil.addClass(tmpIcon, "fa fa-lg "  + ambiguousIconString)

      } else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
        L.DomUtil.addClass(tmpIcon, "glyphicon " + ambiguousIconString)

      } else {
        L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
      }

      // make this a string so that it's easy to set innerHTML below
      tmpIcon = tmpIcon.outerHTML;
  }

  return tmpIcon;
}

function curateStates(easyButton){
  var curatedStates = [],
      dirtyStates = easyButton.options.states;

  for(var i = 0; i < easyButton.options.states.length; i++){
    curatedStates.push( curateState(easyButton.options.states[i], easyButton) );
  }

  return curatedStates;

  function curateState(state, newThis){
    var cleanState = {};

    cleanState.title = state.title;
    cleanState.stateName = state.stateName;

    cleanState.icon = L.DomUtil.create('a', 'easy-button-button leaflet-bar-part');
    cleanState.icon.href = 'javascript:void(0);';
    state.title && (cleanState.icon.title = state.title);
    cleanState.icon.innerHTML = buildIcon(state.icon);
    cleanState.callback = L.Util.bind(state.callback, newThis);

    L.DomEvent.addListener(
      cleanState.icon,
      'click',
      autoHandler(newThis)
    );

    return cleanState;
  }
}

function autoHandler(ebObject){
  var autonav = ebObject.options.auto,
      cleanup = L.DomUtil.false,
      stateNavigation = L.Util.bind(function(){
        this._map.getContainer().focus();
        cleanup();
      }, ebObject);

  if( ebObject.options.auto ){
    cleanup = L.Util.bind(function(){ this.state(autonav) }, ebObject);
  }

  // will have `this` set approprately
  return function(e){
    L.DomEvent.stop(e);
    stateNavigation();
  };

}

})();
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016
 *
 * This file is licensed under the Affero General Public License version 3
 * or later.
 *
 * See the COPYING-README file.
 *
 */

/* global OC, Handlebars */
(function() {

	var TEMPLATE_MENU =
		'
' +
		'{{#each items}}' +
		'
' +
		'' +
			'{{#if icon}}' +
			'{{else}}'+
				'{{#if iconClass}}' +
				'' +
				'{{else}}' +
				'' +
				'{{/if}}' +
			'{{/if}}' +
			'
{{displayName}}
' +
			'{{tooltip}}
' +
		'
' +
		'{{/each}}' +
		'
';

	/**
	 * Construct a new FederationScopeMenu instance
	 * @constructs FederationScopeMenu
	 * @memberof OC.Settings
	 */
	var FederationScopeMenu = OC.Backbone.View.extend({
		tagName: 'div',
		className: 'federationScopeMenu popovermenu bubble hidden menu',
		field: undefined,
		_scopes: undefined,

		initialize: function(options) {
			this.field = options.field;
			this._scopes = [
				{
					name: 'private',
					displayName: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Local') : t('core', 'Private'),
					tooltip: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Only visible to local users') : t('core', 'Only visible to you'),
					icon: OC.imagePath('core', 'actions/password'),
					active: false
				},
				{
					name: 'contacts',
					displayName: t('core', 'Contacts'),
					tooltip: t('core', 'Visible to local users and to trusted servers'),
					icon: OC.imagePath('core', 'places/contacts-dark'),
					active: false
				},
				{
					name: 'public',
					displayName: t('core', 'Public'),
					tooltip: t('core', 'Will be synced to a global and public address book'),
					icon: OC.imagePath('core', 'places/link'),
					active: false
				}
			];
		},

		/**
		 * Current context
		 *
		 * @type OCA.Files.FileActionContext
		 */
		_context: null,

		events: {
			'click a.action': '_onClickAction'
		},

		template: Handlebars.compile(TEMPLATE_MENU),

		/**
		 * Event handler whenever an action has been clicked within the menu
		 *
		 * @param {Object} event event object
		 */
		_onClickAction: function(event) {
			var $target = $(event.currentTarget);
			if (!$target.hasClass('menuitem')) {
				$target = $target.closest('.menuitem');
			}

			this.trigger('select:scope', $target.data('action'));

			OC.hideMenus();
		},

		/**
		 * Renders the menu with the currently set items
		 */
		render: function() {
			this.$el.html(this.template({
				items: this._scopes
			}));
		},

		/**
		 * Displays the menu
		 */
		show: function(context) {
			this._context = context;
			var currentlyActiveValue = $('#'+context.target.closest('form').id).find('.icon-checkmark > input')[0].value;

			for(var i in this._scopes) {
				this._scopes[i].active = false;
			}

			switch (currentlyActiveValue) {
				case "private":
					this._scopes[0].active = true;
					break;
				case "contacts":
					this._scopes[1].active = true;
					break;
				case "public":
					this._scopes[2].active = true;
					break;
			}

			var $el = $(context.target);
			var offsetIcon = $el.offset();
			var offsetHeading = $el.closest('h2').offset();

			this.render();
			this.$el.removeClass('hidden');

			OC.showMenu(null, this.$el);

			//Set the menuwidth
			var menuWidth = this.$el.width();
			this.$el.css('width', menuWidth);

			//Calculate menu position
			var l = offsetIcon.left - offsetHeading.left;
			l = l - (menuWidth / 2) + ($el.outerWidth()/2);
			this.$el.css('left', l);

		}
	});

	OC.Settings = OC.Settings || {};
	OC.Settings.FederationScopeMenu = FederationScopeMenu;

})();
<commit_msg>Fix federation scope dropdown<commit_after>/*
 * Copyright (c) 2016
 *
 * This file is licensed under the Affero General Public License version 3
 * or later.
 *
 * See the COPYING-README file.
 *
 */

/* global OC, Handlebars */
(function() {

	var TEMPLATE_MENU =
		'
' +
		'{{#each items}}' +
		'
' +
		'' +
			'{{#if icon}}' +
			'{{else}}'+
				'{{#if iconClass}}' +
				'' +
				'{{else}}' +
				'' +
				'{{/if}}' +
			'{{/if}}' +
			'
{{displayName}}
' +
			'{{tooltip}}
' +
		'
' +
		'{{/each}}' +
		'
';

	/**
	 * Construct a new FederationScopeMenu instance
	 * @constructs FederationScopeMenu
	 * @memberof OC.Settings
	 */
	var FederationScopeMenu = OC.Backbone.View.extend({
		tagName: 'div',
		className: 'federationScopeMenu popovermenu bubble hidden menu',
		field: undefined,
		_scopes: undefined,

		initialize: function(options) {
			this.field = options.field;
			this._scopes = [
				{
					name: 'private',
					displayName: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Local') : t('core', 'Private'),
					tooltip: (this.field === 'avatar' || this.field === 'displayname') ? t('core', 'Only visible to local users') : t('core', 'Only visible to you'),
					icon: OC.imagePath('core', 'actions/password'),
					active: false
				},
				{
					name: 'contacts',
					displayName: t('core', 'Contacts'),
					tooltip: t('core', 'Visible to local users and to trusted servers'),
					icon: OC.imagePath('core', 'places/contacts-dark'),
					active: false
				},
				{
					name: 'public',
					displayName: t('core', 'Public'),
					tooltip: t('core', 'Will be synced to a global and public address book'),
					icon: OC.imagePath('core', 'places/link'),
					active: false
				}
			];
		},

		/**
		 * Current context
		 *
		 * @type OCA.Files.FileActionContext
		 */
		_context: null,

		events: {
			'click a.action': '_onClickAction'
		},

		template: Handlebars.compile(TEMPLATE_MENU),

		/**
		 * Event handler whenever an action has been clicked within the menu
		 *
		 * @param {Object} event event object
		 */
		_onClickAction: function(event) {
			var $target = $(event.currentTarget);
			if (!$target.hasClass('menuitem')) {
				$target = $target.closest('.menuitem');
			}

			this.trigger('select:scope', $target.data('action'));

			OC.hideMenus();
		},

		/**
		 * Renders the menu with the currently set items
		 */
		render: function() {
			this.$el.html(this.template({
				items: this._scopes
			}));
		},

		/**
		 * Displays the menu
		 */
		show: function(context) {
			this._context = context;
			var currentlyActiveValue = $('#'+context.target.closest('form').id).find('input[type="hidden"]')[0].value;

			for(var i in this._scopes) {
				this._scopes[i].active = false;
			}

			switch (currentlyActiveValue) {
				case "private":
					this._scopes[0].active = true;
					break;
				case "contacts":
					this._scopes[1].active = true;
					break;
				case "public":
					this._scopes[2].active = true;
					break;
			}

			var $el = $(context.target);
			var offsetIcon = $el.offset();
			var offsetHeading = $el.closest('h2').offset();

			this.render();
			this.$el.removeClass('hidden');

			OC.showMenu(null, this.$el);

			//Set the menuwidth
			var menuWidth = this.$el.width();
			this.$el.css('width', menuWidth);

			//Calculate menu position
			var l = offsetIcon.left - offsetHeading.left;
			l = l - (menuWidth / 2) + ($el.outerWidth()/2);
			this.$el.css('left', l);

		}
	});

	OC.Settings = OC.Settings || {};
	OC.Settings.FederationScopeMenu = FederationScopeMenu;

})();
<|endoftext|>


<commit_before>/*
 * grunt-generate-configs
 * https://github.com/creynders/grunt-generate-configs
 *
 * Copyright (c) 2014 Camille Reynders
 * Licensed under the MIT license.
 */

'use strict';

var inquirer = require('inquirer');

function jsonify(data){
    return JSON.stringify(data, null, 2);
}

module.exports = function(grunt){

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    grunt.registerTask('generate_configs', 'Splits your grunt configuration into separate files', function(){
        var done = this.async();
        var target = grunt.option('target') || 'config';
        var type = grunt.option('type') || 'json';

        function writeFiles(){
            var handler;
            switch(type){
                case "json":
                    handler = function(key){
                        var filename = target + '/' + key + '.json';
                        grunt.file.write(filename, jsonify(grunt.config.data[key]));
                        grunt.log.writeln('Generated: ' + filename);
                    };
                    break;
                case "js":
                case "module":
                    handler = function(key){
                        var filename = target + '/' + key + '.json';
                        grunt.file.write(filename, 'module.exports = ' + jsonify(grunt.config.data[key]));
                        grunt.log.writeln('Generated: ' + filename);
                    };
                    break;
            }
            Object.keys(grunt.config.data).forEach(handler);
        }

        if(grunt.file.exists(target)){
            inquirer.prompt(
            [
                {
                    type    : "confirm",
                    message : "A directory '" + target + "' already exists, its files will be overwritten.\nAre you sure you want to continue?",
                    name    : "overwrite",
                    default : false
                }
            ], function(answers){
                if(answers.overwrite){
                    writeFiles();
                    done(true);
                }else{
                    done(false);
                }
            });
        }else{
            writeFiles();
            done(true);
        }
    });
};
<commit_msg>fixes #2, incorrect ext for js files<commit_after>/*
 * grunt-generate-configs
 * https://github.com/creynders/grunt-generate-configs
 *
 * Copyright (c) 2014 Camille Reynders
 * Licensed under the MIT license.
 */

'use strict';

var inquirer = require('inquirer');

function jsonify(data){
    return JSON.stringify(data, null, 2);
}

module.exports = function(grunt){

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    grunt.registerTask('generate_configs', 'Splits your grunt configuration into separate files', function(){
        var done = this.async();
        var target = grunt.option('target') || 'config';
        var type = grunt.option('type') || 'json';

        function writeFiles(){
            var handler;
            switch(type){
                case "json":
                    handler = function(key){
                        var filename = target + '/' + key + '.json';
                        grunt.file.write(filename, jsonify(grunt.config.data[key]));
                        grunt.log.writeln('Generated: ' + filename);
                    };
                    break;
                case "js":
                case "module":
                    handler = function(key){
                        var filename = target + '/' + key + '.js';
                        grunt.file.write(filename, 'module.exports = ' + jsonify(grunt.config.data[key]));
                        grunt.log.writeln('Generated: ' + filename);
                    };
                    break;
            }
            Object.keys(grunt.config.data).forEach(handler);
        }

        if(grunt.file.exists(target)){
            inquirer.prompt(
            [
                {
                    type    : "confirm",
                    message : "A directory '" + target + "' already exists, its files will be overwritten.\nAre you sure you want to continue?",
                    name    : "overwrite",
                    default : false
                }
            ], function(answers){
                if(answers.overwrite){
                    writeFiles();
                    done(true);
                }else{
                    done(false);
                }
            });
        }else{
            writeFiles();
            done(true);
        }
    });
};
<|endoftext|>


<commit_before>import Ora from 'ora'
import path from 'path'
import fs from 'fs'
import xml2js from 'xml2js'
import open from 'opn'
import { prompt } from 'inquirer'
import { exec } from '@skpm/internal-utils/exec'
import getSkpmConfigFromPackageJSON from '@skpm/internal-utils/skpm-config'
import extractRepository from '@skpm/internal-utils/extract-repository'
import replaceArraysByLastItem from '@skpm/internal-utils/replace-arrays-by-last-item'
import toolConfig from '@skpm/internal-utils/tool-config'
import auth from '../utils/auth'
import github from '../utils/github'
import asyncCommand from '../utils/async-command'
import { error } from '../utils'

const EMPTY_APPCAST = {
  rss: {
    $: {
      'xmlns:sparkle': 'http://www.andymatuschak.org/xml-namespaces/sparkle',
      'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
      version: '2.0',
    },
    channel: [{ item: [] }],
  },
}

let CACHED_TOKEN
async function getToken(repo) {
  if (CACHED_TOKEN) {
    return CACHED_TOKEN
  }
  try {
    const token = await auth.getToken()
    await github.getRepo(token, repo)
    CACHED_TOKEN = token
    return token
  } catch (e) {
    error(`The repository doesn't exist or the GitHub token is invalid`)
    throw e
  }
}

export default asyncCommand({
  command: 'publish <bump>',

  desc:
    'Publish a new version of the plugin. <bump> can be the new version number or any of the following: major, minor, patch, premajor preminor, prepatch, prerelease.',

  builder: {
    'repo-url': {
      description:
        'Specify the repository URL (default to the one specified in package.json).',
      type: 'string',
    },
    'skip-release': {
      description: 'Do not create a release on GitHub.com.',
      type: 'boolean',
      // conflicts: 'open-release', // TODO un-comment when https://github.com/yargs/yargs/issues/929 is fixed
      implies: 'download-url',
    },
    'open-release': {
      description: 'Open the newly created release on GitHub.com.',
      type: 'boolean',
      alias: 'o',
    },
    'skip-registry': {
      description:
        'Do not publish to the plugins registry if not already present.',
      type: 'boolean',
    },
    'download-url': {
      description:
        "Specify the new version's download URL (default to the asset of the release created on GitHub.com).",
      type: 'string',
    },
    appcast: {
      description:
        'Specify the local path to the appcast (default to .appcast.xml). If `false`, then the appcast update will be skipped',
      type: 'string',
    },
  },

  async handler(argv) {
    replaceArraysByLastItem(argv, [
      'repoUrl',
      'skipRelease',
      'openRelease',
      'skipRegistry',
      'downloadUrl',
      'appcast',
    ])

    let packageJSON
    try {
      packageJSON = require(path.join(process.cwd(), 'package.json'))
    } catch (err) {
      error(`Error while reading the package.json file`)
      throw err
    }

    const skpmConfig = getSkpmConfigFromPackageJSON(packageJSON)

    const repo =
      (argv.repoUrl && extractRepository(argv.repoUrl)) || skpmConfig.repository

    if (!repo) {
      throw new Error(
        'Please supply github.com repo URL as --repo-url or in "repository" field in the package.json.'
      )
    }

    if (!argv.skipRegistry && !skpmConfig.description) {
      const manifest = path.join(process.cwd(), skpmConfig.manifest)
      let manifestJSON
      try {
        manifestJSON = require(manifest)
      } catch (err) {
        // ignore
      }
      if (!manifestJSON || !manifestJSON.description) {
        throw new Error('Missing "description" field in the package.json.')
      }
    }

    let token

    if (!argv.skipRelease || !argv.skipRegistry) {
      token = await getToken(repo)
    }

    let tag = skpmConfig.version

    let spinner = null

    function print(text, action) {
      if (process.env.CI) {
        console.log(text)
      } else if (spinner) {
        if (action) {
          spinner[action](text)
        } else {
          spinner.text = text
          if (!spinner.isSpinning) {
            spinner.start()
          }
        }
      } else {
        spinner = new Ora({
          text,
          color: 'magenta',
        }).start()
      }
    }

    print(`Checking if \`${repo}\` is accessible`)

    if (argv.bump) {
      print('Bumping package.json version and creating git tag')
      const { stdout } = await exec(
        `npm version ${
          argv.bump
        } -m "Publish %s release :rocket:" --allow-same-version`
      )
      tag = stdout.trim()
    }

    if (argv.appcast !== 'false') {
      print('Updating the appcast file')

      const appcast = path.join(
        process.cwd(),
        (argv.appcast || skpmConfig.appcast || '.appcast.xml').replace(
          /^\.\//g,
          ''
        )
      )
      const appcastObj = await new Promise(resolve => {
        fs.readFile(appcast, (err, data) => {
          if (err) {
            return resolve(EMPTY_APPCAST)
          }
          return xml2js.parseString(data, (parseErr, result) => {
            if (parseErr) {
              return resolve(EMPTY_APPCAST)
            }
            return resolve(result)
          })
        })
      })

      appcastObj.rss.channel[0].item.unshift({
        enclosure: [
          {
            $: {
              url:
                argv.downloadUrl ||
                `https://github.com/${repo}/releases/download/${tag}/${path
                  .basename(skpmConfig.main)
                  .replace(/ /g, '.')}.zip`,
              'sparkle:version': tag.replace('v', ''),
            },
          },
        ],
      })
      const builder = new xml2js.Builder()
      const xml = await builder.buildObject(appcastObj)

      fs.writeFileSync(appcast, xml)

      await exec(`git add "${appcast}"`)
      await exec('git commit -m "Update .appcast with new tag :sparkles:"')
    }

    print('Pushing the changes to Github')

    await exec(`git push origin HEAD`)

    if (argv.bump) {
      await exec(`git push -f origin HEAD ${tag}`)
    }

    if (!argv.skipRelease) {
      let script = (packageJSON.scripts || {}).prepublish && 'prepublish'
      if (!script) {
        script = (packageJSON.scripts || {}).prepare && 'prepare'
      }
      if (!script) {
        script = (packageJSON.scripts || {}).build && 'build'
      }

      if (script) {
        print('Building the plugin')
        await exec(`NODE_ENV=production npm run ${script}`)
      }

      print('Zipping the plugin')
      const tempZip = `${Date.now()}.zip`
      await exec(
        `zip --symlinks -r ${tempZip} '${skpmConfig.main}' -x '*.DS_Store'`,
        {
          maxBuffer: 2000 * 1024,
        }
      )

      // eslint-disable-next-line prefer-const
      let { notarisation, notarization } = toolConfig.get()
      if (!notarization) {
        // suport both spelling
        notarization = notarisation
      }
      if (notarization) {
        if (notarization.command) {
          print('Running custom notarization command')
          await exec(`${notarization.command} ${tempZip}`)
        } else {
          print('Preparing the plugin to be notarized')
          await exec(
            `codesign -f -s "${
              notarization.authority
            }" --timestamp --identifier "${skpmConfig.identifer}" ${tempZip}`
          )

          print('Sending the plugin to be notarized')
          let requestId
          try {
            const { stderr } = await exec(
              `xcrun altool --notarize-app -f ${tempZip} --primary-bundle-id "${
                skpmConfig.identifer
              }" -u "${notarization.username}" -p "${notarization.password}"`
            )
            ;[requestId] = (stderr.split('RequestUUID = ')[1] || '').split('\n')

            if (!requestId) {
              throw new Error(stderr)
            }
          } catch (err) {
            if (err.message.indexOf('already been uploaded') !== -1) {
              // we can handle that
              ;[requestId] = (
                err.message.split('The upload ID is ')[1] || ''
              ).split('"')

              if (!requestId) {
                throw err
              }
            } else {
              throw err
            }
          }

          console.log(
            `\n\nPlugin sent to the notarization server. To check the status of the request (which might take a few minutes), run:\nxcrun altool --notarization-info ${requestId.trim()} -u "${
              notarization.username
            }" -p "${notarization.password}"\n\n`
          )
        }
      } else {
        console.warn(
          `\n\n⚠ The plugin won't be notarized because the configuration is missing. It is a requirement if the plugin contains a native obj-c framework or bundled binaries on macOS 10.15. If you don't use any of those (or don't know what it is, please ignore thise warning).\nFor more information about how to set up the notarization process and what it means, check https://developer.sketch.com/plugins/notarize-plugins.\n\n`
        )
      }

      print('Creating a draft release on GitHub')
      const { id: releaseId } = await github.createDraftRelease(
        token,
        repo,
        tag
      )

      print('Uploading zip asset')
      await github.updateAsset(
        token,
        repo,
        releaseId,
        tempZip,
        `${path.basename(skpmConfig.main)}.zip`
      )

      print('Publishing the release')
      await github.publishRelease(token, repo, releaseId)

      print('Removing the temporary zip')
      await exec(`rm -f ${tempZip}`)
    }

    if (
      !argv.skipRegistry &&
      (!packageJSON.skpm || !packageJSON.skpm.private)
    ) {
      print('Checking if the plugin is on the official plugin directory')
      const upstreamPluginJSON = await github.getRegistryRepo(
        token,
        skpmConfig,
        repo
      )
      if (!upstreamPluginJSON.existingPlugin) {
        if (spinner) spinner.stop()
        const { addToRegistry } = await prompt({
          type: 'confirm',
          name: 'addToRegistry',
          message: `The plugin is not on the plugins registry yet. Do you wish to add it?`,
          default: true,
        })
        if (addToRegistry) {
          print('Publishing the plugin on the official plugin directory')
          await github.addPluginToPluginsRegistryRepo(
            token,
            skpmConfig,
            repo,
            upstreamPluginJSON
          )
        }
      }
    }

    print('Plugin published!', 'succeed')
    console.log(`${skpmConfig.name}@${tag.replace('v', '')}`)

    if (argv.openRelease) {
      open(`https://github.com/${repo}/tag/${tag.replace('v', '')}`)
    }
  },
})
<commit_msg>fix release page URL<commit_after>import Ora from 'ora'
import path from 'path'
import fs from 'fs'
import xml2js from 'xml2js'
import open from 'opn'
import { prompt } from 'inquirer'
import { exec } from '@skpm/internal-utils/exec'
import getSkpmConfigFromPackageJSON from '@skpm/internal-utils/skpm-config'
import extractRepository from '@skpm/internal-utils/extract-repository'
import replaceArraysByLastItem from '@skpm/internal-utils/replace-arrays-by-last-item'
import toolConfig from '@skpm/internal-utils/tool-config'
import auth from '../utils/auth'
import github from '../utils/github'
import asyncCommand from '../utils/async-command'
import { error } from '../utils'

const EMPTY_APPCAST = {
  rss: {
    $: {
      'xmlns:sparkle': 'http://www.andymatuschak.org/xml-namespaces/sparkle',
      'xmlns:dc': 'http://purl.org/dc/elements/1.1/',
      version: '2.0',
    },
    channel: [{ item: [] }],
  },
}

let CACHED_TOKEN
async function getToken(repo) {
  if (CACHED_TOKEN) {
    return CACHED_TOKEN
  }
  try {
    const token = await auth.getToken()
    await github.getRepo(token, repo)
    CACHED_TOKEN = token
    return token
  } catch (e) {
    error(`The repository doesn't exist or the GitHub token is invalid`)
    throw e
  }
}

export default asyncCommand({
  command: 'publish <bump>',

  desc:
    'Publish a new version of the plugin. <bump> can be the new version number or any of the following: major, minor, patch, premajor preminor, prepatch, prerelease.',

  builder: {
    'repo-url': {
      description:
        'Specify the repository URL (default to the one specified in package.json).',
      type: 'string',
    },
    'skip-release': {
      description: 'Do not create a release on GitHub.com.',
      type: 'boolean',
      // conflicts: 'open-release', // TODO un-comment when https://github.com/yargs/yargs/issues/929 is fixed
      implies: 'download-url',
    },
    'open-release': {
      description: 'Open the newly created release on GitHub.com.',
      type: 'boolean',
      alias: 'o',
    },
    'skip-registry': {
      description:
        'Do not publish to the plugins registry if not already present.',
      type: 'boolean',
    },
    'download-url': {
      description:
        "Specify the new version's download URL (default to the asset of the release created on GitHub.com).",
      type: 'string',
    },
    appcast: {
      description:
        'Specify the local path to the appcast (default to .appcast.xml). If `false`, then the appcast update will be skipped',
      type: 'string',
    },
  },

  async handler(argv) {
    replaceArraysByLastItem(argv, [
      'repoUrl',
      'skipRelease',
      'openRelease',
      'skipRegistry',
      'downloadUrl',
      'appcast',
    ])

    let packageJSON
    try {
      packageJSON = require(path.join(process.cwd(), 'package.json'))
    } catch (err) {
      error(`Error while reading the package.json file`)
      throw err
    }

    const skpmConfig = getSkpmConfigFromPackageJSON(packageJSON)

    const repo =
      (argv.repoUrl && extractRepository(argv.repoUrl)) || skpmConfig.repository

    if (!repo) {
      throw new Error(
        'Please supply github.com repo URL as --repo-url or in "repository" field in the package.json.'
      )
    }

    if (!argv.skipRegistry && !skpmConfig.description) {
      const manifest = path.join(process.cwd(), skpmConfig.manifest)
      let manifestJSON
      try {
        manifestJSON = require(manifest)
      } catch (err) {
        // ignore
      }
      if (!manifestJSON || !manifestJSON.description) {
        throw new Error('Missing "description" field in the package.json.')
      }
    }

    let token

    if (!argv.skipRelease || !argv.skipRegistry) {
      token = await getToken(repo)
    }

    let tag = skpmConfig.version

    let spinner = null

    function print(text, action) {
      if (process.env.CI) {
        console.log(text)
      } else if (spinner) {
        if (action) {
          spinner[action](text)
        } else {
          spinner.text = text
          if (!spinner.isSpinning) {
            spinner.start()
          }
        }
      } else {
        spinner = new Ora({
          text,
          color: 'magenta',
        }).start()
      }
    }

    print(`Checking if \`${repo}\` is accessible`)

    if (argv.bump) {
      print('Bumping package.json version and creating git tag')
      const { stdout } = await exec(
        `npm version ${
          argv.bump
        } -m "Publish %s release :rocket:" --allow-same-version`
      )
      tag = stdout.trim()
    }

    if (argv.appcast !== 'false') {
      print('Updating the appcast file')

      const appcast = path.join(
        process.cwd(),
        (argv.appcast || skpmConfig.appcast || '.appcast.xml').replace(
          /^\.\//g,
          ''
        )
      )
      const appcastObj = await new Promise(resolve => {
        fs.readFile(appcast, (err, data) => {
          if (err) {
            return resolve(EMPTY_APPCAST)
          }
          return xml2js.parseString(data, (parseErr, result) => {
            if (parseErr) {
              return resolve(EMPTY_APPCAST)
            }
            return resolve(result)
          })
        })
      })

      appcastObj.rss.channel[0].item.unshift({
        enclosure: [
          {
            $: {
              url:
                argv.downloadUrl ||
                `https://github.com/${repo}/releases/download/${tag}/${path
                  .basename(skpmConfig.main)
                  .replace(/ /g, '.')}.zip`,
              'sparkle:version': tag.replace('v', ''),
            },
          },
        ],
      })
      const builder = new xml2js.Builder()
      const xml = await builder.buildObject(appcastObj)

      fs.writeFileSync(appcast, xml)

      await exec(`git add "${appcast}"`)
      await exec('git commit -m "Update .appcast with new tag :sparkles:"')
    }

    print('Pushing the changes to Github')

    await exec(`git push origin HEAD`)

    if (argv.bump) {
      await exec(`git push -f origin HEAD ${tag}`)
    }

    if (!argv.skipRelease) {
      let script = (packageJSON.scripts || {}).prepublish && 'prepublish'
      if (!script) {
        script = (packageJSON.scripts || {}).prepare && 'prepare'
      }
      if (!script) {
        script = (packageJSON.scripts || {}).build && 'build'
      }

      if (script) {
        print('Building the plugin')
        await exec(`NODE_ENV=production npm run ${script}`)
      }

      print('Zipping the plugin')
      const tempZip = `${Date.now()}.zip`
      await exec(
        `zip --symlinks -r ${tempZip} '${skpmConfig.main}' -x '*.DS_Store'`,
        {
          maxBuffer: 2000 * 1024,
        }
      )

      // eslint-disable-next-line prefer-const
      let { notarisation, notarization } = toolConfig.get()
      if (!notarization) {
        // suport both spelling
        notarization = notarisation
      }
      if (notarization) {
        if (notarization.command) {
          print('Running custom notarization command')
          await exec(`${notarization.command} ${tempZip}`)
        } else {
          print('Preparing the plugin to be notarized')
          await exec(
            `codesign -f -s "${
              notarization.authority
            }" --timestamp --identifier "${skpmConfig.identifer}" ${tempZip}`
          )

          print('Sending the plugin to be notarized')
          let requestId
          try {
            const { stderr } = await exec(
              `xcrun altool --notarize-app -f ${tempZip} --primary-bundle-id "${
                skpmConfig.identifer
              }" -u "${notarization.username}" -p "${notarization.password}"`
            )
            ;[requestId] = (stderr.split('RequestUUID = ')[1] || '').split('\n')

            if (!requestId) {
              throw new Error(stderr)
            }
          } catch (err) {
            if (err.message.indexOf('already been uploaded') !== -1) {
              // we can handle that
              ;[requestId] = (
                err.message.split('The upload ID is ')[1] || ''
              ).split('"')

              if (!requestId) {
                throw err
              }
            } else {
              throw err
            }
          }

          console.log(
            `\n\nPlugin sent to the notarization server. To check the status of the request (which might take a few minutes), run:\nxcrun altool --notarization-info ${requestId.trim()} -u "${
              notarization.username
            }" -p "${notarization.password}"\n\n`
          )
        }
      } else {
        console.warn(
          `\n\n⚠ The plugin won't be notarized because the configuration is missing. It is a requirement if the plugin contains a native obj-c framework or bundled binaries on macOS 10.15. If you don't use any of those (or don't know what it is, please ignore thise warning).\nFor more information about how to set up the notarization process and what it means, check https://developer.sketch.com/plugins/notarize-plugins.\n\n`
        )
      }

      print('Creating a draft release on GitHub')
      const { id: releaseId } = await github.createDraftRelease(
        token,
        repo,
        tag
      )

      print('Uploading zip asset')
      await github.updateAsset(
        token,
        repo,
        releaseId,
        tempZip,
        `${path.basename(skpmConfig.main)}.zip`
      )

      print('Publishing the release')
      await github.publishRelease(token, repo, releaseId)

      print('Removing the temporary zip')
      await exec(`rm -f ${tempZip}`)
    }

    if (
      !argv.skipRegistry &&
      (!packageJSON.skpm || !packageJSON.skpm.private)
    ) {
      print('Checking if the plugin is on the official plugin directory')
      const upstreamPluginJSON = await github.getRegistryRepo(
        token,
        skpmConfig,
        repo
      )
      if (!upstreamPluginJSON.existingPlugin) {
        if (spinner) spinner.stop()
        const { addToRegistry } = await prompt({
          type: 'confirm',
          name: 'addToRegistry',
          message: `The plugin is not on the plugins registry yet. Do you wish to add it?`,
          default: true,
        })
        if (addToRegistry) {
          print('Publishing the plugin on the official plugin directory')
          await github.addPluginToPluginsRegistryRepo(
            token,
            skpmConfig,
            repo,
            upstreamPluginJSON
          )
        }
      }
    }

    print('Plugin published!', 'succeed')
    console.log(`${skpmConfig.name}@${tag.replace('v', '')}`)

    if (argv.openRelease) {
      open(`https://github.com/${repo}/releases/tag/${tag}`)
    }
  },
})
<|endoftext|>


<commit_before>'use strict';

module.exports = ['groupFactory','$timeout', function(groupFactory, $timeout) {

  console.log('ui-select-tree directive');

  return {
    restrict: 'E',
    scope: { model: '=' },
    link: function (scope, el) {
      scope.breadcrumbs = [{"id":0,"title":"All"}];
      scope.groups = groupFactory.load(0);

      scope.loadChildGroupsOf = function(group, $select) {
        $select.search = '';

        scope.breadcrumbs.push(group);
        scope.groups = groupFactory.load(group.id);
        scope.$broadcast('uiSelectFocus');
      };

      scope.navigateBackTo = function (crumb, $select) {
        $select.search = '';
        var index = _.findIndex(scope.breadcrumbs, {id: crumb.id});

        scope.breadcrumbs.splice(index + 1, scope.breadcrumbs.length);
        scope.groups = groupFactory.load(_.last(scope.breadcrumbs).id);
        $select.open = false;
        scope.$broadcast('uiSelectFocus');
      };
    },
    templateUrl: '/ui-tree-select.html'
  };
}];
<commit_msg>remove log<commit_after>'use strict';

module.exports = ['groupFactory','$timeout', function(groupFactory, $timeout) {

  return {
    restrict: 'E',
    scope: { model: '=' },
    link: function (scope, el) {
      scope.breadcrumbs = [{"id":0,"title":"All"}];
      scope.groups = groupFactory.load(0);

      scope.loadChildGroupsOf = function(group, $select) {
        $select.search = '';

        scope.breadcrumbs.push(group);
        scope.groups = groupFactory.load(group.id);
        scope.$broadcast('uiSelectFocus');
      };

      scope.navigateBackTo = function (crumb, $select) {
        $select.search = '';
        var index = _.findIndex(scope.breadcrumbs, {id: crumb.id});

        scope.breadcrumbs.splice(index + 1, scope.breadcrumbs.length);
        scope.groups = groupFactory.load(_.last(scope.breadcrumbs).id);
        $select.open = false;
        scope.$broadcast('uiSelectFocus');
      };
    },
    templateUrl: '/ui-tree-select.html'
  };
}];
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { connect } from 'react-redux'

import {Tabs, Tab} from 'material-ui/Tabs'
import {Card} from 'material-ui/Card'
import SwipeableViews from 'react-swipeable-views'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import ImageEdit from 'material-ui/svg-icons/image/edit'
import FlatButton from 'material-ui/RaisedButton'
import Dialog from 'material-ui/Dialog'
import TextField from 'material-ui/TextField'

import { updateQuestion } from './actions'

const mapStateToProps = ({ question_text, page }) => ({
  question_text, page
})

class EditQuestion extends Component {
  constructor(props){
    super(props)
    const { question_text } = this.props
    this.state = {
      question_text: question_text,
      open: false,
      mainSlideIndex: 0,
      disabled: false,
      default_text: {
        'question': "国連加盟国のうち、アフリカ大陸の国は何パーセントを占めていると思いますか。",
        'answered': "回答は終了しました。",
        'waiting_text': "参加者の登録を待っています。\nこの画面のまましばらくお待ちください。",
        'min': 0,
        'step': 1,
        'max': 100,
        'unit': "%"
      }
    }
  }

  QuestionTab(){
    return (

        <TextField
         hintText={"問題"}
         defaultValue={this.state.question_text["question"]}
         onBlur={this.handleChange.bind(this, ["question"])}
         multiLine={true}
         fullWidth={true}
       />

スライダーの設定

        <TextField
         hintText={"最小値"}
         defaultValue={this.state.question_text["min"]}
         onChange={this.handleChangeOnlyNum.bind(this, ["min"])}
       /> から <TextField
         hintText={"最大値"}
         defaultValue={this.state.question_text["max"]}
         onChange={this.handleChangeOnlyNum.bind(this, ["max"])}
       /> まで

       (単位:<TextField
         hintText={"単位"}
         defaultValue={this.state.question_text["unit"]}
         onChange={this.handleChange.bind(this, ["unit"])}
       />)

    )
  }

  WaitingTab() {
    return (

        <TextField
         hintText={"待機画面に表示するテキスト"}
         defaultValue={this.state.question_text["waiting_text"]}
         onBlur={this.handleChange.bind(this, ["waiting_text"])}
         multiLine={true}
         fullWidth={true}
       />

    )
  }

  handleOpen() {
    this.setState({ open: true });
  }

  handleClose() {
    this.setState({ open: false, disabled: false })
  }

  handleChange(value, event){
    var question_text = Object.assign({}, this.state.question_text)
    var temp = question_text
    for(var i = 0; i < value.length - 1; i++){
      temp = temp[value[i]]
    }
    temp[value[value.length - 1]] = event.target.value
    this.setState({ question_text: question_text })
  }

  handleChangeOnlyNum(value, event){
    if(isNaN(event.target.value) || event.target.value.indexOf('.') != -1) {
      this.setState({ disabled: true })
      return
    }
    var question_text = Object.assign({}, this.state.question_text)
    var temp1 = question_text
    for(var i = 0; i < value.length - 1; i++){
      temp1 = temp1[value[i]]
    }
    var temp2 = parseInt(temp1[value[value.length - 1]])
    temp1[value[value.length - 1]] = parseInt(event.target.value)
    this.setState({ question_text: question_text, disabled: false })
    if(parseInt(question_text.min) >= parseInt(question_text.max)){
      temp1[value[value.length - 1]] = temp2
      this.setState({ question_text: question_text, disabled: true })
    }
  }

  handleMainSlide(value){
    this.setState({
      mainSlideIndex: value
    })
  }

  submit() {
    const { dispatch } = this.props
    dispatch(updateQuestion(this.state.question_text))
    this.setState({ open: false })
  }

  reset(){
    const { dispatch } = this.props
    dispatch(updateQuestion(this.state.default_text))
    this.setState({ question_text: this.state.default_text, open: false, disabled: false })
  }

  render(){
    const { page } = this.props
    const actions = [
      <FlatButton
        label="適用"
        disabled={this.state.disabled}
        primary={true}
        keyboardFocused={true}
        onTouchTap={this.submit.bind(this)}
      />,
      <FlatButton
        label="キャンセル"
        onTouchTap={this.handleClose.bind(this)}
      />,
     <FlatButton
        label="すべてリセット"
        onTouchTap={this.reset.bind(this)}
      />,
    ]
    return (

      <FloatingActionButton onClick={this.handleOpen.bind(this)} disabled={page != "waiting"}>
         <ImageEdit />
      </FloatingActionButton>
      <Dialog
        title="編集画面"
        actions={actions}
        modal={false}
        open={this.state.open}
        onRequestClose={this.handleClose.bind(this)}
        autoScrollBodyContent={this.state.mainSlideIndex == 1}
      >
        <Tabs
          onChange={this.handleMainSlide.bind(this)}
          value={this.state.mainSlideIndex}
        >
          <Tab label="待機画面" value={0}/>
          <Tab label="問題画面" value={1}/>
        </Tabs>
        <SwipeableViews
          index={this.state.mainSlideIndex}
          onChangeIndex={this.handleMainSlide.bind(this)}
        >
         {this. WaitingTab()}
         {this.QuestionTab()}
      </SwipeableViews>
      </Dialog>

)
  }
}

export default connect(mapStateToProps)(EditQuestion)<commit_msg>fix UI<commit_after>import React, { Component } from 'react'
import { connect } from 'react-redux'

import {Tabs, Tab} from 'material-ui/Tabs'
import SwipeableViews from 'react-swipeable-views'
import FloatingActionButton from 'material-ui/FloatingActionButton'
import ImageEdit from 'material-ui/svg-icons/image/edit'
import RaisedButton from 'material-ui/RaisedButton'
import Dialog from 'material-ui/Dialog'
import TextField from 'material-ui/TextField'
import Snackbar from 'material-ui/Snackbar'

import { updateQuestion, fetchContents } from './actions'

const mapStateToProps = ({ question_text, page }) => ({
  question_text, page
})

class EditQuestion extends Component {
  constructor(props){
    super(props)
    const { question_text } = this.props
    this.state = {
      question_text: question_text,
      open: false,
      snack: false,
      message: "設定を送信しました。",
      mainSlideIndex: 0,
      disabled: false,
      default_text: {
        'question': "国連加盟国のうち、アフリカ大陸の国は何パーセントを占めていると思いますか。",
        'answered': "回答は終了しました。",
        'waiting_text': "参加者の登録を待っています。\nこの画面のまましばらくお待ちください。",
        'min': 0,
        'step': 1,
        'max': 100,
        'unit': "%"
      }
    }
  }

  QuestionTab(){
    return (

        <TextField
         hintText={"問題"}
         defaultValue={this.state.question_text["question"]}
         onBlur={this.handleChange.bind(this, ["question"])}
         multiLine={true}
         fullWidth={true}
       />

スライダーの設定

        <TextField
         hintText={"最小値"}
         defaultValue={this.state.question_text["min"]}
         onChange={this.handleChangeOnlyNum.bind(this, ["min"])}
       /> から <TextField
         hintText={"最大値"}
         defaultValue={this.state.question_text["max"]}
         onChange={this.handleChangeOnlyNum.bind(this, ["max"])}
       /> まで

       (単位:<TextField
         hintText={"単位"}
         defaultValue={this.state.question_text["unit"]}
         onChange={this.handleChange.bind(this, ["unit"])}
       />)

    )
  }

  WaitingTab() {
    return (

        <TextField
         hintText={"待機画面に表示するテキスト"}
         defaultValue={this.state.question_text["waiting_text"]}
         onBlur={this.handleChange.bind(this, ["waiting_text"])}
         multiLine={true}
         fullWidth={true}
       />

    )
  }

  handleOpen() {
    const { dispatch } = this.props
    dispatch(fetchContents())
    this.setState({
      open: true,
      question_text: this.props.question_text,
      mainSlideIndex: 0,
    })
  }

  handleClose() {
    this.setState({ open: false, disabled: false })
  }

  handleChange(value, event){
    var question_text = Object.assign({}, this.state.question_text)
    var temp = question_text
    for(var i = 0; i < value.length - 1; i++){
      temp = temp[value[i]]
    }
    temp[value[value.length - 1]] = event.target.value
    this.setState({ question_text: question_text })
  }

  handleChangeOnlyNum(value, event){
    if(isNaN(event.target.value) || event.target.value.indexOf('.') != -1) {
      this.setState({ disabled: true })
      return
    }
    var question_text = Object.assign({}, this.state.question_text)
    var temp1 = question_text
    for(var i = 0; i < value.length - 1; i++){
      temp1 = temp1[value[i]]
    }
    var temp2 = parseInt(temp1[value[value.length - 1]])
    temp1[value[value.length - 1]] = parseInt(event.target.value)
    this.setState({ question_text: question_text, disabled: false })
    if(parseInt(question_text.min) >= parseInt(question_text.max)){
      temp1[value[value.length - 1]] = temp2
      this.setState({ question_text: question_text, disabled: true })
    }
  }

  handleMainSlide(value){
    this.setState({
      mainSlideIndex: value
    })
  }

  handleRequestClose() {
    this.setState({ snack: false })
  }

  submit() {
    this.setState({
      open: false,
      snack: true,
      message: "設定を送信しました。"
    })
    const { dispatch } = this.props
    dispatch(updateQuestion(this.state.question_text))
  }

  reset(){
    this.setState({
      question_text: this.state.default_text,
      open: false,
      snack: true,
      message: "設定を初期化しました。",
      disabled: false,
    })
    const { dispatch } = this.props
    dispatch(updateQuestion(this.state.default_text))
  }

  render(){
    const { page } = this.props
    const actions = [
      <RaisedButton
        label="適用"
        disabled={this.state.disabled}
        primary={true}
        keyboardFocused={true}
        onTouchTap={this.submit.bind(this)}
      />,
      <RaisedButton
        label="キャンセル"
        onTouchTap={this.handleClose.bind(this)}
      />,
     <RaisedButton
        label="すべてリセット"
        onTouchTap={this.reset.bind(this)}
      />,
    ]
    return (

      <FloatingActionButton onClick={this.handleOpen.bind(this)} disabled={page != "waiting"}>
         <ImageEdit />
      </FloatingActionButton>
      <Dialog
        title="編集画面"
        actions={actions}
        modal={false}
        open={this.state.open}
        autoScrollBodyContent={this.state.mainSlideIndex == 1}
      >
        <Tabs
          onChange={this.handleMainSlide.bind(this)}
          value={this.state.mainSlideIndex}
        >
          <Tab label="待機画面" value={0}/>
          <Tab label="問題画面" value={1}/>
        </Tabs>
        <SwipeableViews
          index={this.state.mainSlideIndex}
          onChangeIndex={this.handleMainSlide.bind(this)}
        >
         {this. WaitingTab()}
         {this.QuestionTab()}
      </SwipeableViews>
      </Dialog>
      <Snackbar
        open={this.state.snack}
        message={this.state.message}
        autoHideDuration={2000}
        onRequestClose={this.handleRequestClose.bind(this)}
      />

)
  }
}

export default connect(mapStateToProps)(EditQuestion)<|endoftext|>


<commit_before>var PR = {};

/**
 * @param {function (Object)} handler
 * @param {Array.<string>} fileExtensions
 */
PR.registerLangHandler = function registerLangHandler(handler, fileExtensions) {};

/**
 * @param {Array} shortcutStylePatterns
 * @param {Array} fallthroughStylePatterns
 * @return {function (Object)}
 */
PR.createSimpleLexer = function createSimpleLexer(
  shortcutStylePatterns, fallthroughStylePatterns) {};

/**
 * @param {Object} options a set of optional parameters.
 * @return {function (Object)} a function that examines the source code
 *     in the input job and builds the decoration list.
 */
PR.sourceDecorator = function sourceDecorator(options) {};

PR.PR_ATTRIB_NAME = 'atn';
PR.PR_ATTRIB_VALUE = 'atv';
PR.PR_COMMENT = 'com';
PR.PR_DECLARATION = 'dec';
PR.PR_KEYWORD = 'kwd';
PR.PR_LITERAL = 'lit';
PR.PR_NOCODE = 'nocode';
PR.PR_PLAIN = 'pln';
PR.PR_PUNCTUATION = 'pun';
PR.PR_SOURCE = 'src';
PR.PR_STRING = 'str';
PR.PR_TAG = 'tag';
PR.PR_TYPE = 'typ';

// See https://github.com/amdjs/amdjs-api/wiki/AMD
var define;
<commit_msg>(c) header for externs file<commit_after>/**
 * @license
 * Copyright (C) 20015 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var PR = {};

/**
 * @param {function (Object)} handler
 * @param {Array.<string>} fileExtensions
 */
PR.registerLangHandler = function registerLangHandler(handler, fileExtensions) {};

/**
 * @param {Array} shortcutStylePatterns
 * @param {Array} fallthroughStylePatterns
 * @return {function (Object)}
 */
PR.createSimpleLexer = function createSimpleLexer(
  shortcutStylePatterns, fallthroughStylePatterns) {};

/**
 * @param {Object} options a set of optional parameters.
 * @return {function (Object)} a function that examines the source code
 *     in the input job and builds the decoration list.
 */
PR.sourceDecorator = function sourceDecorator(options) {};

PR.PR_ATTRIB_NAME = 'atn';
PR.PR_ATTRIB_VALUE = 'atv';
PR.PR_COMMENT = 'com';
PR.PR_DECLARATION = 'dec';
PR.PR_KEYWORD = 'kwd';
PR.PR_LITERAL = 'lit';
PR.PR_NOCODE = 'nocode';
PR.PR_PLAIN = 'pln';
PR.PR_PUNCTUATION = 'pun';
PR.PR_SOURCE = 'src';
PR.PR_STRING = 'str';
PR.PR_TAG = 'tag';
PR.PR_TYPE = 'typ';

// See https://github.com/amdjs/amdjs-api/wiki/AMD
var define;
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react';
import elementType from 'react-prop-types/lib/elementType';

import GridTable from './GridTable';
import GridBody from './GridBody';
import GridRow from './GridRow';
import GridCell from './GridCell';
import GridColumnHeader from './GridColumnHeader';
import GridHeader from './GridHeader';
import GridHeaderCell from './GridHeaderCell';
import BaseCellRenderer from './BaseCellRenderer';
import ObjectCellRenderer from './ObjectCellRenderer';
import ArrayCellRenderer from './ArrayCellRenderer';
import Filter from './Filter';
import ToolbarDefault from './ToolbarDefault';
import Pager from './Pager';

import { filterConfigFromProp, sortConfigFromProp } from './utils';
import { ASCENDING, DESCENDING } from './constants';

const Ardagryd = (props)=>{
    //Merge custom and default config
    const config = Object.assign({},defaultConfig,props.config);

    //Get components from config
    const Grid = config.grid;
    const GridHeader = config.header;

    const Toolbar = config.toolbar;
    const GridBody = config.body;
    const ColumnHeader = config.columnHeader;

    //Get custom column-configuration
    const columnConfig = props.columns;

    config.eventHandler = props.dispatch;

    //Columns to show
    var columnKeys = [];

    const idColumn = getOrCreateIdColumn(props.objects,columnConfig);

    //Filter objects based on supplied filter strings
    let filters = {};
    const filterConfig = filterConfigFromProp(props.filter);
    const columnNamesWithFilter = filterConfig.map((filterObject) => {
        filters[filterObject.columnName] = filterObject.expression;
        return filterObject.columnName;
    });

    //filter function
    const filterFunction = config.filterFunction;

        let objects = props.objects.filter((currentObjectToBeFiltered) => {
            let currentColumn;
            let shouldPass = true;
            for (let i in columnNamesWithFilter){
                currentColumn = columnNamesWithFilter[i];
                    let customFilterFunction;
                    if (columnConfig
                        && columnConfig[currentColumn]
                        && columnConfig[currentColumn].filterFunction) {
                        customFilterFunction = columnConfig[currentColumn].filterFunction;
                    }
                    if (customFilterFunction){
                        shouldPass =  customFilterFunction({value: currentObjectToBeFiltered[currentColumn],
                            expression: filters[currentColumn]});
                    } else {
                        shouldPass = filterFunction({value: currentObjectToBeFiltered[currentColumn],
                                                expression: filters[currentColumn]});
                    }
                if (!shouldPass){
                    return false;
                }
            }
            return true;
        });

    //Generate array with selected column-names in configured order
    let availableColumnKeys;
    if (props.objects.length > 0){
        availableColumnKeys = Object.keys(props.objects[0]);
        Object.keys(columnConfig).forEach((columnName)=>{
            if (availableColumnKeys.indexOf(columnName) === -1){
                availableColumnKeys.push(columnName);
            }
        });

        columnKeys = availableColumnKeys.filter((currentColumnKey) => {
            const configForColumn = columnConfig[currentColumnKey];
            if(config.showColumnsWithoutConfig){
                return configForColumn === undefined || configForColumn.show !== false;
            } else {
                //Show only configured columns
                return configForColumn !== undefined && configForColumn.show !== false;
            }
        }).sort((a,b) => {
            const configForA = columnConfig[a];
            const configForB = columnConfig[b];
            const valueForA = configForA && configForA.order != null ? configForA.order : 1000;
            const valueForB = configForB && configForB.order != null ? configForB.order : 1000;
            return valueForA-valueForB;
        });
    }

    //Sort

    //default order

    var { columnName, order } = sortConfigFromProp(props.sort);

    //check for sort configuration

    if (columnName){
        let sortValueGetter;
        if (columnConfig
                && columnConfig[columnName]
                && columnConfig[columnName].sortValueGetter) {
            sortValueGetter = columnConfig[columnName].sortValueGetter;
        } else {
            sortValueGetter = config.sortValueGetter;
        }

        // temporary array holds objects with position and sort-value
        let mapped = objects.map(function(el, i) {
            const value = sortValueGetter({value: el[columnName], object: el});
            return { index: i, value: value };
        });

        //reverse order on "descending" sort option
        const multplier = order === DESCENDING ? -1 : 1;

        // sorting the mapped array containing the reduced values
        mapped.sort((a, b) => {
            if (a.value == null){
                return multplier * -1;
            }
            if (b.value == null){
                return multplier * 1;
            }
            return multplier * (+(a.value > b.value) || +(a.value === b.value) - 1);
        });

        // container for the resulting order
        const list = mapped.map(function(el){
            return objects[el.index];
        });

        objects = list;
    }

    let tools;
    if(config.showToolbar){
        tools = (<Toolbar config={config} columnKeys={columnKeys} columns={columnConfig} filter={filterConfig}/>);
    }

    let pagedObjects;
    const paging = config.paging;
    if (paging){
        pagedObjects = objects.slice(props.skip, props.skip+paging);
    } else {
        pagedObjects = props.objects;
    }
    const pager = () => {
        if(config.paging){
            return(
                    <Pager length={objects.length} updatePagination={config.eventHandler} skip={props.skip} paging={config.paging} />
                );
        }
    };
    return(

                {pager()}
                <Grid>
                    <GridHeader>
                        <ColumnHeader columns={columnConfig} config={config} columnKeys={columnKeys} sort={props.sort} />
                        {tools}
                    </GridHeader>
                    <GridBody idColumn={idColumn} objects={pagedObjects} columns={columnConfig} config={config} columnKeys={columnKeys}/>
                </Grid>

        );

};

const defaultConfig = {
    grid: GridTable,
    body: GridBody,
    row: GridRow,
    cell: GridCell,
    columnHeader: GridColumnHeader,
    header: GridHeader,
    columnHeaderCell: GridHeaderCell,
    cellRendererBase: BaseCellRenderer,
    cellRendererObject: ObjectCellRenderer,
    cellRendererArray: ArrayCellRenderer,
    filter: Filter,
    toolbar: ToolbarDefault,
    showToolbar: true,
    showColumnsWithoutConfig: true, //show all columns which are not explicitly hidden
    paging: 10,
    displayValueGetter: ({value}) => value,
    sortValueGetter: ({value}) => value && typeof value === 'string' ? value.toLowerCase() : value ? JSON.stringify(value).toLowerCase(): value,
    filterFunction: ({value, expression}) => {
        if (!(typeof expression === "string") || expression === "" || expression === undefined || expression === null){return true}
        if (value === undefined || value === null) {return false}
        if ((typeof value === "string" ) && value.toLowerCase().indexOf(expression.toLowerCase()) === -1){
            return false;
        } else if (typeof value === 'object' ){
            return JSON.stringify(value).toLowerCase().indexOf(expression.toLowerCase()) === -1;
        }
        else return true;
    }
};

Ardagryd.defaultProps = {
    config: {},
    columns: {},
    dispatch: () => {}
};

const filterConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    expression: React.PropTypes.string.isRequired
});

const sortConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    order: React.PropTypes.oneOf([ASCENDING, DESCENDING])
});

Ardagryd.propTypes = {
    objects: PropTypes.arrayOf(PropTypes.object),
    config: PropTypes.object.isRequired,
    columns: PropTypes.objectOf(PropTypes.shape({
        displayValueGetter: PropTypes.func,
        id: PropTypes.bool,
        label: PropTypes.string,
        order: PropTypes.number,
        hideTools: PropTypes.bool,
        sortable: PropTypes.bool,
        cellRenderer: elementType,
        sortValueGetter: PropTypes.func,
        filterFunction: PropTypes.func,
        filter: (props, propName, componentName, location, propFullName)=>{
            if (props[propName]){
                return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `filter` prop on the root component.'
              );
            }
        },
        sort: (props, propName, componentName, location, propFullName)=>{
            if (props[propName]){
                return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `sort` prop on the root component.'
              );
            }
        }
    })).isRequired,
    dispatch: PropTypes.func.isRequired,
    sort: PropTypes.oneOfType([
        PropTypes.string,
        sortConfig,
        PropTypes.arrayOf(sortConfig)
    ]),
    filter: PropTypes.oneOfType([
        filterConfig,
        PropTypes.arrayOf(filterConfig)])
};

//Find id-column, or enhance objects with ids
function getOrCreateIdColumn(objects, columns){
    //check if explicit id-column is set in column-config
    const idColumn = Object.keys(columns).find((key)=>{
        if(columns[key].id){
            return true;
        }
    });

    if (idColumn){
        return idColumn;
    } else if (objects.length > 0 && objects[0].id !== undefined) {
        //check if objects have a id-property
        return 'id';
    } else {
        //TODO: use some type of hashing
        //generate id-property
        let index = 0;
        objects.map((object) => {
            object.id = index++;
        });
        return 'id';
    }
}

export default Ardagryd;
<commit_msg>formatting<commit_after>import React, { PropTypes } from 'react';
import elementType from 'react-prop-types/lib/elementType';

import GridTable from './GridTable';
import GridBody from './GridBody';
import GridRow from './GridRow';
import GridCell from './GridCell';
import GridColumnHeader from './GridColumnHeader';
import GridHeader from './GridHeader';
import GridHeaderCell from './GridHeaderCell';
import BaseCellRenderer from './BaseCellRenderer';
import ObjectCellRenderer from './ObjectCellRenderer';
import ArrayCellRenderer from './ArrayCellRenderer';
import Filter from './Filter';
import ToolbarDefault from './ToolbarDefault';
import Pager from './Pager';

import { filterConfigFromProp, sortConfigFromProp } from './utils';
import { ASCENDING, DESCENDING } from './constants';

const Ardagryd = (props)=>{
    //Merge custom and default config
    const config = Object.assign({},defaultConfig,props.config);

    //Get components from config
    const Grid = config.grid;
    const GridHeader = config.header;

    const Toolbar = config.toolbar;
    const GridBody = config.body;
    const ColumnHeader = config.columnHeader;

    //Get custom column-configuration
    const columnConfig = props.columns;

    config.eventHandler = props.dispatch;

    //Columns to show
    var columnKeys = [];

    const idColumn = getOrCreateIdColumn(props.objects,columnConfig);

    //Filter objects based on supplied filter strings
    let filters = {};
    const filterConfig = filterConfigFromProp(props.filter);
    const columnNamesWithFilter = filterConfig.map((filterObject) => {
        filters[filterObject.columnName] = filterObject.expression;
        return filterObject.columnName;
    });

    //filter function
    const filterFunction = config.filterFunction;

    let objects = props.objects.filter((currentObjectToBeFiltered) => {
        let currentColumn;
        let shouldPass = true;
        for (let i in columnNamesWithFilter) {
            currentColumn = columnNamesWithFilter[i];
            let customFilterFunction;
            if (columnConfig
                && columnConfig[currentColumn]
                && columnConfig[currentColumn].filterFunction) {
                customFilterFunction = columnConfig[currentColumn].filterFunction;
            }
            if (customFilterFunction) {
                shouldPass = customFilterFunction({
                    value: currentObjectToBeFiltered[currentColumn],
                    expression: filters[currentColumn]
                });
            } else {
                shouldPass = filterFunction({
                    value: currentObjectToBeFiltered[currentColumn],
                    expression: filters[currentColumn]
                });
            }
            if (!shouldPass) {
                return false;
            }
        }
        return true;
    });

    //Generate array with selected column-names in configured order
    let availableColumnKeys;
    if (props.objects.length > 0){
        availableColumnKeys = Object.keys(props.objects[0]);
        Object.keys(columnConfig).forEach((columnName)=>{
            if (availableColumnKeys.indexOf(columnName) === -1){
                availableColumnKeys.push(columnName);
            }
        });

        columnKeys = availableColumnKeys.filter((currentColumnKey) => {
            const configForColumn = columnConfig[currentColumnKey];
            if(config.showColumnsWithoutConfig){
                return configForColumn === undefined || configForColumn.show !== false;
            } else {
                //Show only configured columns
                return configForColumn !== undefined && configForColumn.show !== false;
            }
        }).sort((a,b) => {
            const configForA = columnConfig[a];
            const configForB = columnConfig[b];
            const valueForA = configForA && configForA.order != null ? configForA.order : 1000;
            const valueForB = configForB && configForB.order != null ? configForB.order : 1000;
            return valueForA-valueForB;
        });
    }

    //Sort

    //default order

    var { columnName, order } = sortConfigFromProp(props.sort);

    //check for sort configuration

    if (columnName){
        let sortValueGetter;
        if (columnConfig
                && columnConfig[columnName]
                && columnConfig[columnName].sortValueGetter) {
            sortValueGetter = columnConfig[columnName].sortValueGetter;
        } else {
            sortValueGetter = config.sortValueGetter;
        }

        // temporary array holds objects with position and sort-value
        let mapped = objects.map(function(el, i) {
            const value = sortValueGetter({value: el[columnName], object: el});
            return { index: i, value: value };
        });

        //reverse order on "descending" sort option
        const multplier = order === DESCENDING ? -1 : 1;

        // sorting the mapped array containing the reduced values
        mapped.sort((a, b) => {
            if (a.value == null){
                return multplier * -1;
            }
            if (b.value == null){
                return multplier * 1;
            }
            return multplier * (+(a.value > b.value) || +(a.value === b.value) - 1);
        });

        // container for the resulting order
        const list = mapped.map(function(el){
            return objects[el.index];
        });

        objects = list;
    }

    let tools;
    if(config.showToolbar){
        tools = (<Toolbar config={config} columnKeys={columnKeys} columns={columnConfig} filter={filterConfig}/>);
    }

    let pagedObjects;
    const paging = config.paging;
    if (paging){
        pagedObjects = objects.slice(props.skip, props.skip+paging);
    } else {
        pagedObjects = props.objects;
    }
    const pager = () => {
        if(config.paging){
            return(
                    <Pager length={objects.length} updatePagination={config.eventHandler} skip={props.skip} paging={config.paging} />
                );
        }
    };
    return(

                {pager()}
                <Grid>
                    <GridHeader>
                        <ColumnHeader columns={columnConfig} config={config} columnKeys={columnKeys} sort={props.sort} />
                        {tools}
                    </GridHeader>
                    <GridBody idColumn={idColumn} objects={pagedObjects} columns={columnConfig} config={config} columnKeys={columnKeys}/>
                </Grid>

        );

};

const defaultConfig = {
    grid: GridTable,
    body: GridBody,
    row: GridRow,
    cell: GridCell,
    columnHeader: GridColumnHeader,
    header: GridHeader,
    columnHeaderCell: GridHeaderCell,
    cellRendererBase: BaseCellRenderer,
    cellRendererObject: ObjectCellRenderer,
    cellRendererArray: ArrayCellRenderer,
    filter: Filter,
    toolbar: ToolbarDefault,
    showToolbar: true,
    showColumnsWithoutConfig: true, //show all columns which are not explicitly hidden
    paging: 10,
    displayValueGetter: ({value}) => value,
    sortValueGetter: ({value}) => value && typeof value === 'string' ? value.toLowerCase() : value ? JSON.stringify(value).toLowerCase(): value,
    filterFunction: ({value, expression}) => {
        if (!(typeof expression === "string") || expression === "" || expression === undefined || expression === null){return true}
        if (value === undefined || value === null) {return false}
        if ((typeof value === "string" ) && value.toLowerCase().indexOf(expression.toLowerCase()) === -1){
            return false;
        } else if (typeof value === 'object' ){
            return JSON.stringify(value).toLowerCase().indexOf(expression.toLowerCase()) === -1;
        }
        else return true;
    }
};

Ardagryd.defaultProps = {
    config: {},
    columns: {},
    dispatch: () => {}
};

const filterConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    expression: React.PropTypes.string.isRequired
});

const sortConfig = React.PropTypes.shape({
    columnName: React.PropTypes.string.isRequired,
    order: React.PropTypes.oneOf([ASCENDING, DESCENDING])
});

Ardagryd.propTypes = {
    objects: PropTypes.arrayOf(PropTypes.object),
    config: PropTypes.object.isRequired,
    columns: PropTypes.objectOf(PropTypes.shape({
        displayValueGetter: PropTypes.func,
        id: PropTypes.bool,
        label: PropTypes.string,
        order: PropTypes.number,
        hideTools: PropTypes.bool,
        sortable: PropTypes.bool,
        cellRenderer: elementType,
        sortValueGetter: PropTypes.func,
        filterFunction: PropTypes.func,
        filter: (props, propName, componentName, location, propFullName)=>{
            if (props[propName]){
                return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `filter` prop on the root component.'
              );
            }
        },
        sort: (props, propName, componentName, location, propFullName)=>{
            if (props[propName]){
                return new Error(
                  'Invalid prop `' + propFullName + '` supplied to' +
                  ' `' + componentName + '`. Please use the `sort` prop on the root component.'
              );
            }
        }
    })).isRequired,
    dispatch: PropTypes.func.isRequired,
    sort: PropTypes.oneOfType([
        PropTypes.string,
        sortConfig,
        PropTypes.arrayOf(sortConfig)
    ]),
    filter: PropTypes.oneOfType([
        filterConfig,
        PropTypes.arrayOf(filterConfig)])
};

//Find id-column, or enhance objects with ids
function getOrCreateIdColumn(objects, columns){
    //check if explicit id-column is set in column-config
    const idColumn = Object.keys(columns).find((key)=>{
        if(columns[key].id){
            return true;
        }
    });

    if (idColumn){
        return idColumn;
    } else if (objects.length > 0 && objects[0].id !== undefined) {
        //check if objects have a id-property
        return 'id';
    } else {
        //TODO: use some type of hashing
        //generate id-property
        let index = 0;
        objects.map((object) => {
            object.id = index++;
        });
        return 'id';
    }
}

export default Ardagryd;
<|endoftext|>