<commit_before>/**
 * this infowindow is shown in the map when user clicks on a feature
 */

(function() {

  var MapInfowindow = cdb.geo.ui.Infowindow.extend({
    _TEXTS: {
      _NO_FIELDS_SELECTED: "Choose some fields to be
 shown on infowindows,
 please."
    },

    events: cdb.core.View.extendEvents({
        'click .edit':    '_editGeom',
        'click .remove':  '_removeGeom'
    }),

    initialize: function() {
      var self = this;
      this.table = this.options.table;
      this.model.set({ content: 'loading...' });
      // call parent
      this.constructor.__super__.initialize.apply(this);
      this.model.set('offset', [28, 0]);
      this.model.bind('change', function() {
        if(!this.hasChanged('content') && self.row) {
          self.renderInfo();
        }
      });

      // Set live tipsy when geom is enabled or disabled
      this.$el.find("a.edit, a.remove").tipsy({
        live:true,
        fade:true,
        gravity: 's',
        offset: -2,
        className: function(){
          return $(this).closest(".cartodb-popup").hasClass('dark') ? 'dark' : ''
        },
        title: function(ev){
          var enabled = !$(this).hasClass("disabled");
          if (enabled) {
            return ''
          } else {
            return 'Not available in SQL view'
          }
        }
      })
    },

    setFeatureInfo: function(cartodb_id) {
      this.cartodb_id = cartodb_id;
      if(this.row) {
        this.row.unbind();
      }
      this.row = this.table.data().getRow(cartodb_id);
      this.row.bind('change', this.renderInfo, this);
      this.row.fetch();
      this.model.set({ content: 'loading...' });
      return this;
    },

    /**
     * renders the infowindows adding some editing features
     */
    render: function() {
      // render original
      cdb.geo.ui.Infowindow.prototype.render.call(this);
      // render edit and remove buttons
      if(this.model.toJSON().fields && !this.model.toJSON().fields.length) {
        this.$('.cartodb-popup-content').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        this.trigger('editInfoWindow');
      }
      var cartodb_id = '';
      if(this.row) {
        cartodb_id = this.row.attributes['cartodb_id'];
        cartodb_id = cartodb_id? '#'+cartodb_id : '';
      }
      this.$('.cartodb-popup-content-wrapper')
        .append(this.getTemplate('table/views/infowindow_footer')({"cartodb_id": cartodb_id}));

      if(this.table.data().isReadOnly()) {
        this.$('.cartodb-popup-content-wrapper .edit').addClass('disabled');
        this.$('.cartodb-popup-content-wrapper .remove').addClass('disabled');
      }

    },

    renderInfo: function() {
      var self = this;
      var fields = _(this.row.attributes).map(function(v, k) {
        // Don't rendering cartodb_id field from the beginning
        // although back is answering us with it
        if(self.model.containsField(k) && k != "cartodb_id") {
          var h = {
            title: null,
            value: '',
            position: Number.MAX_INT
          };
          if(self.model.getFieldProperty(k, 'title')) {
            h.title = k;
          }
          h.value = v;
          h.position = self.model.getFieldPos(k);
          return h;
        }
        return null;
      });

      // sort
      fields = _.compact(fields);
      fields.sort(function(a, b) {
        return a.position - b.position;
      });

      // filter and add index
      var render_fields = [];
      for(var i = 0; i < fields.length; ++i) {
        var f = fields[i];
        if(f) {
          f.index = render_fields.length;
          render_fields.push(f);
        }
      }
      this.model.set({ content:  { fields: render_fields } });

      // Just move the map if need it when fields are already added.
      this._adjustPan();
    },

    /**
     * triggers an editGeom event with the geometry
     * infowindow is currently showing
     */
    _editGeom: function(e) {
      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        this.model.set("visibility", false);
        this.trigger('editGeom', this.row);
        return false;
      }

    },

    /**
     * triggers an removeGeom event when the geometry
     * is removed from the server
     */
    _removeGeom: function(e) {

      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        var self = this;
        this.model.set("visibility", false);
        this.row.destroy({
          success: function() {
            self.trigger('removeGeom', self.row);
          }
        }, { wait: true });
        return false;
      }
    }
  });

  // export
  cdb.admin.MapInfowindow = MapInfowindow;

})();
<commit_msg>empty infowindow styling<commit_after>/**
 * this infowindow is shown in the map when user clicks on a feature
 */

(function() {

  var MapInfowindow = cdb.geo.ui.Infowindow.extend({
    _TEXTS: {
      _NO_FIELDS_SELECTED: "You haven’t selected any
infowindow fields."
    },

    events: cdb.core.View.extendEvents({
        'click .edit':    '_editGeom',
        'click .remove':  '_removeGeom'
    }),

    initialize: function() {
      var self = this;
      this.table = this.options.table;
      this.model.set({ content: 'loading...' });
      // call parent
      this.constructor.__super__.initialize.apply(this);
      this.model.set('offset', [28, 0]);
      this.model.bind('change', function() {
        if(!this.hasChanged('content') && self.row) {
          self.renderInfo();
        }
      });

      // Set live tipsy when geom is enabled or disabled
      this.$el.find("a.edit, a.remove").tipsy({
        live:true,
        fade:true,
        gravity: 's',
        offset: -2,
        className: function(){
          return $(this).closest(".cartodb-popup").hasClass('dark') ? 'dark' : ''
        },
        title: function(ev){
          var enabled = !$(this).hasClass("disabled");
          if (enabled) {
            return ''
          } else {
            return 'Not available in SQL view'
          }
        }
      })
    },

    setFeatureInfo: function(cartodb_id) {
      this.cartodb_id = cartodb_id;
      if(this.row) {
        this.row.unbind();
      }
      this.row = this.table.data().getRow(cartodb_id);
      this.row.bind('change', this.renderInfo, this);
      this.row.fetch();
      this.model.set({ content: 'loading...' });
      return this;
    },

    /**
     * renders the infowindows adding some editing features
     */
    render: function() {
      // render original
      cdb.geo.ui.Infowindow.prototype.render.call(this);
      // render edit and remove buttons
      if(this.model.toJSON().fields && !this.model.toJSON().fields.length) {
        this.$('.cartodb-popup-content').html('
' + this._TEXTS._NO_FIELDS_SELECTED + '
');
        this.trigger('editInfoWindow');
      }
      var cartodb_id = '';
      if(this.row) {
        cartodb_id = this.row.attributes['cartodb_id'];
        cartodb_id = cartodb_id? '#'+cartodb_id : '';
      }
      this.$('.cartodb-popup-content-wrapper')
        .append(this.getTemplate('table/views/infowindow_footer')({"cartodb_id": cartodb_id}));

      if(this.table.data().isReadOnly()) {
        this.$('.cartodb-popup-content-wrapper .edit').addClass('disabled');
        this.$('.cartodb-popup-content-wrapper .remove').addClass('disabled');
      }

    },

    renderInfo: function() {
      var self = this;
      var fields = _(this.row.attributes).map(function(v, k) {
        // Don't rendering cartodb_id field from the beginning
        // although back is answering us with it
        if(self.model.containsField(k) && k != "cartodb_id") {
          var h = {
            title: null,
            value: '',
            position: Number.MAX_INT
          };
          if(self.model.getFieldProperty(k, 'title')) {
            h.title = k;
          }
          h.value = v;
          h.position = self.model.getFieldPos(k);
          return h;
        }
        return null;
      });

      // sort
      fields = _.compact(fields);
      fields.sort(function(a, b) {
        return a.position - b.position;
      });

      // filter and add index
      var render_fields = [];
      for(var i = 0; i < fields.length; ++i) {
        var f = fields[i];
        if(f) {
          f.index = render_fields.length;
          render_fields.push(f);
        }
      }
      this.model.set({ content:  { fields: render_fields } });

      // Just move the map if need it when fields are already added.
      this._adjustPan();
    },

    /**
     * triggers an editGeom event with the geometry
     * infowindow is currently showing
     */
    _editGeom: function(e) {
      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        this.model.set("visibility", false);
        this.trigger('editGeom', this.row);
        return false;
      }

    },

    /**
     * triggers an removeGeom event when the geometry
     * is removed from the server
     */
    _removeGeom: function(e) {

      e.preventDefault();

      if (!this.table.data().isReadOnly()) {
        var self = this;
        this.model.set("visibility", false);
        this.row.destroy({
          success: function() {
            self.trigger('removeGeom', self.row);
          }
        }, { wait: true });
        return false;
      }
    }
  });

  // export
  cdb.admin.MapInfowindow = MapInfowindow;

})();
<|endoftext|>


<commit_before>'use strict';
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------

var getFeatures,
	getCriteria,
	reloadInverval;

/**
 * Set the criteria to the given object.
 * @param {Object} configVal
 * @return {void}
 * @private
 */
function setCriteria(configVal) {
	self._criteria = configVal;
}

/**
 * Set the features.
 * @param {Object} configVal
 * @return {void}
 * @private
 */
function setFeatures(configVal) {
	if(typeof configVal == 'function') {
		getFeatures = configVal;
		updateFeatures();
	} else {
		getFeatures = undefined;
	}
	if(typeof configVal == 'object') {
		self._features = configVal;
	}
}

/**
 * Update the features by reloading them, if possible.
 * @return {void}
 * @private
 */
function updateFeatures() {
	if(!getFeatures) {
		return;
	} else if(getFeatures.length === 0) {
		self._features = getFeatures() || self._features;
		return;
	} else if(getFeatures.length === 1) {
		getFeatures(getFeaturesCallback);
		return;
	} else if(getFeatures.length > 1) {
		throw new Error('Too Many Arguments!');
	}
}

/**
 * The callback called by the user-defined function for reloading features.
 * @param {Object} data
 * @return {void}
 * @private
 */
function getFeaturesCallback(data) {
	self._features = data || self._features;
}

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
var self = module.exports = {

	// Object containing all features defined by the user.
	_features: {},

	// Object containing all criteria defined by the user.
	_criteria: {},

	// The reload rate for reloading the features
	_reloadRate: 30*1000,

	/**
	 * Configure fflip.
	 * @param  {Object} params
	 * @return {void}
	 */
	config: function(params) {
		// Set Criteria & Features
		setCriteria(params.criteria);
		setFeatures(params.features);

		// Refresh Rate
		self._reloadRate = params.reload*1000 || self._reloadRate;
		clearInterval(reloadInverval);
		if(getCriteria || getFeatures)
			reloadInverval = setInterval(self.reload, self._reloadRate);
	},

	/**
	 * Reload the features, if a reload is possible.
	 * @return {void}
	 */
	reload: function() {
		updateFeatures();
	},

	/**
	 * Check if a user has some given feature, and returns a boolean.
	 * Returns null if the feature does not exist.
	 * @param {Object} user The User object that criterial will check against.
	 * @param {string} featureName The name of the feature to check for.
	 * @return {Boolean|null}
	 */
	userHasFeature: function(user, featureName) {
		var feature = self._features[featureName];
		if(typeof feature != 'object') {
			return null;
		}
		var featureCriteria = feature.criteria || {};
		var criteriaArray = Object.keys(featureCriteria);
		var isEnabled = true;
		if(criteriaArray.length == 0) {
			return false;
		}
		criteriaArray.forEach(function(cKey) {
			if(isEnabled) {
				var c_data = featureCriteria[cKey];
				var c_func = self._criteria[cKey];
				isEnabled = c_func(user, c_data);
			}
		});
		return isEnabled;
	},

	/**
	 * Get the availability of all features for a given user.
	 * @param {Object} user The User object that criterial will check against.
	 * @param {Object} flags A collection of overrides
	 *        [@deprecated this flag will be removed soon]
	 * @return {Object} The collection of all features and their availability.
	 */
	userFeatures: function(user, flags) {
		flags = flags || {};
		var user_features = {};
		Object.keys(self._features).forEach(function(featureName) {
			if(flags[featureName] !== undefined) {
				user_features[featureName] = flags[featureName];
			} else {
				user_features[featureName] = self.userHasFeature(user, featureName);
			}
		});
		return user_features;
	},

	/**
	 * Express middleware. Attaches helper functions to the request object
	 * and wrap the res.render to automatically include features in the
	 * template.
	 * @param {Request} req
	 * @param {Response} res
	 * @param {Function} next
	 * @return {void}
	 * @private
	 */
	_express_middleware: function(req, res, next) {

		// Attach the fflip object to the request
		req.fflip = {
			_isSet: false,
			features: {}
		};
		if(req.cookies) {
			req.fflip.flags = req.cookies.fflip;
		} else {
			req.fflip.flags = {};
		}
		req.fflip.setForUser = function(user) {
			req.fflip.features = self.userFeatures(user, req.fflip.flags);
			req.fflip._isSet = true;
		};
		req.fflip.has = function(featureName) {
			if(!req.fflip._isSet) {
				console.error('FFlip: features not set - call setForUser() before checking for features (and consider adding middleware to always set features)');
				return null;
			}
			return req.fflip.features[featureName];
		};

		// Wrap res.render() to set options.features automatically
		res._render = res.render;
		res.render = function(view, options, callback) {
			options = options || {};
			if(typeof req.fflip.features == "object") {
				options.Features = req.fflip.features;
				options.FeaturesJSON = JSON.stringify(req.fflip.features);
			}
			res._render(view, options, callback);
		};

		// Carry On!
		next();
	},

	/**
	 * Attach routes for manual feature flipping.
	 * @param {Request} req
	 * @param {Response} res
	 * @param {Function} next
	 * @return {void}
	 * @private
	 */
	_express_route: function(req, res, next) {
		var name = req.params.name;
		var action = req.params.action;
		var actionName = '';

		// Check if feature exists.
		if(self._features[name] === undefined) {
			var err = new Error("FFlip: Feature " + name + " not found");
			err.fflip = true;
			err.statusCode = 404;
			return next(err);
		}

		// Check if cookies are enabled.
		if(!req.cookies) {
			var err = new Error("FFlip: Cookies are not enabled.");
			err.fflip = true;
			err.statusCode = 500;
			return next(err);
		}

		// Apply the action.
		var flags = req.cookies.fflip || {};
		switch(action) {
			// enable
			case '1':
				flags[name] = true;
				actionName = 'enabled';
				break;
			// disable
			case '0':
				flags[name] = false;
				actionName = 'disabled';
				break;
			// remove
			case '1':
				delete flags[name];
				actionName = 'removed';
				break;
			// other: propogate error
			default:
				var err = new Error("FFlip: Bad Input. Action (" + action + ") must be 1 (enable), 0 (disable), or -1 (remove)");
				err.fflip = true;
				err.statusCode = 400;
				return next(err);
		}

		// set new fflip cookie with new data
		res.cookie('fflip', flags, { maxAge: 900000 });
		res.json(200, {
			feature: name,
			action: action,
			status: 200,
			message: 'fflip: Feature ' + name + ' is now ' + actionName
		});
	},

	/**
	 * Attach FFlip functionality to an express app. Includes helpers & routes.
	 * @param {Object} app An Express Application
	 * @return {void}
	 */
	__express: function(app) {
		// Express Middleware
		app.use(self._express_middleware);
		// Manual Flipping Route
		app.get('/fflip/:name/:action', self._express_route);
	},

};<commit_msg>convert double quotes to single quotes<commit_after>'use strict';
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------

var getFeatures,
	getCriteria,
	reloadInverval;

/**
 * Set the criteria to the given object.
 * @param {Object} configVal
 * @return {void}
 * @private
 */
function setCriteria(configVal) {
	self._criteria = configVal;
}

/**
 * Set the features.
 * @param {Object} configVal
 * @return {void}
 * @private
 */
function setFeatures(configVal) {
	if(typeof configVal == 'function') {
		getFeatures = configVal;
		updateFeatures();
	} else {
		getFeatures = undefined;
	}
	if(typeof configVal == 'object') {
		self._features = configVal;
	}
}

/**
 * Update the features by reloading them, if possible.
 * @return {void}
 * @private
 */
function updateFeatures() {
	if(!getFeatures) {
		return;
	} else if(getFeatures.length === 0) {
		self._features = getFeatures() || self._features;
		return;
	} else if(getFeatures.length === 1) {
		getFeatures(getFeaturesCallback);
		return;
	} else if(getFeatures.length > 1) {
		throw new Error('Too Many Arguments!');
	}
}

/**
 * The callback called by the user-defined function for reloading features.
 * @param {Object} data
 * @return {void}
 * @private
 */
function getFeaturesCallback(data) {
	self._features = data || self._features;
}

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
var self = module.exports = {

	// Object containing all features defined by the user.
	_features: {},

	// Object containing all criteria defined by the user.
	_criteria: {},

	// The reload rate for reloading the features
	_reloadRate: 30*1000,

	/**
	 * Configure fflip.
	 * @param  {Object} params
	 * @return {void}
	 */
	config: function(params) {
		// Set Criteria & Features
		setCriteria(params.criteria);
		setFeatures(params.features);

		// Refresh Rate
		self._reloadRate = params.reload*1000 || self._reloadRate;
		clearInterval(reloadInverval);
		if(getCriteria || getFeatures)
			reloadInverval = setInterval(self.reload, self._reloadRate);
	},

	/**
	 * Reload the features, if a reload is possible.
	 * @return {void}
	 */
	reload: function() {
		updateFeatures();
	},

	/**
	 * Check if a user has some given feature, and returns a boolean.
	 * Returns null if the feature does not exist.
	 * @param {Object} user The User object that criterial will check against.
	 * @param {string} featureName The name of the feature to check for.
	 * @return {Boolean|null}
	 */
	userHasFeature: function(user, featureName) {
		var feature = self._features[featureName];
		if(typeof feature != 'object') {
			return null;
		}
		var featureCriteria = feature.criteria || {};
		var criteriaArray = Object.keys(featureCriteria);
		var isEnabled = true;
		if(criteriaArray.length == 0) {
			return false;
		}
		criteriaArray.forEach(function(cKey) {
			if(isEnabled) {
				var c_data = featureCriteria[cKey];
				var c_func = self._criteria[cKey];
				isEnabled = c_func(user, c_data);
			}
		});
		return isEnabled;
	},

	/**
	 * Get the availability of all features for a given user.
	 * @param {Object} user The User object that criterial will check against.
	 * @param {Object} flags A collection of overrides
	 *        [@deprecated this flag will be removed soon]
	 * @return {Object} The collection of all features and their availability.
	 */
	userFeatures: function(user, flags) {
		flags = flags || {};
		var user_features = {};
		Object.keys(self._features).forEach(function(featureName) {
			if(flags[featureName] !== undefined) {
				user_features[featureName] = flags[featureName];
			} else {
				user_features[featureName] = self.userHasFeature(user, featureName);
			}
		});
		return user_features;
	},

	/**
	 * Express middleware. Attaches helper functions to the request object
	 * and wrap the res.render to automatically include features in the
	 * template.
	 * @param {Request} req
	 * @param {Response} res
	 * @param {Function} next
	 * @return {void}
	 * @private
	 */
	_express_middleware: function(req, res, next) {

		// Attach the fflip object to the request
		req.fflip = {
			_isSet: false,
			features: {}
		};
		if(req.cookies) {
			req.fflip.flags = req.cookies.fflip;
		} else {
			req.fflip.flags = {};
		}
		req.fflip.setForUser = function(user) {
			req.fflip.features = self.userFeatures(user, req.fflip.flags);
			req.fflip._isSet = true;
		};
		req.fflip.has = function(featureName) {
			if(!req.fflip._isSet) {
				console.error('FFlip: features not set - call setForUser() before checking for features (and consider adding middleware to always set features)');
				return null;
			}
			return req.fflip.features[featureName];
		};

		// Wrap res.render() to set options.features automatically
		res._render = res.render;
		res.render = function(view, options, callback) {
			options = options || {};
			if(typeof req.fflip.features == 'object') {
				options.Features = req.fflip.features;
				options.FeaturesJSON = JSON.stringify(req.fflip.features);
			}
			res._render(view, options, callback);
		};

		// Carry On!
		next();
	},

	/**
	 * Attach routes for manual feature flipping.
	 * @param {Request} req
	 * @param {Response} res
	 * @param {Function} next
	 * @return {void}
	 * @private
	 */
	_express_route: function(req, res, next) {
		var name = req.params.name;
		var action = req.params.action;
		var actionName = '';

		// Check if feature exists.
		if(self._features[name] === undefined) {
			var err = new Error('FFlip: Feature ' + name + ' not found');
			err.fflip = true;
			err.statusCode = 404;
			return next(err);
		}

		// Check if cookies are enabled.
		if(!req.cookies) {
			var err = new Error('FFlip: Cookies are not enabled.');
			err.fflip = true;
			err.statusCode = 500;
			return next(err);
		}

		// Apply the action.
		var flags = req.cookies.fflip || {};
		switch(action) {
			// enable
			case '1':
				flags[name] = true;
				actionName = 'enabled';
				break;
			// disable
			case '0':
				flags[name] = false;
				actionName = 'disabled';
				break;
			// remove
			case '1':
				delete flags[name];
				actionName = 'removed';
				break;
			// other: propogate error
			default:
				var err = new Error('FFlip: Bad Input. Action (' + action + ') must be 1 (enable), 0 (disable), or -1 (remove)');
				err.fflip = true;
				err.statusCode = 400;
				return next(err);
		}

		// set new fflip cookie with new data
		res.cookie('fflip', flags, { maxAge: 900000 });
		res.json(200, {
			feature: name,
			action: action,
			status: 200,
			message: 'fflip: Feature ' + name + ' is now ' + actionName
		});
	},

	/**
	 * Attach FFlip functionality to an express app. Includes helpers & routes.
	 * @param {Object} app An Express Application
	 * @return {void}
	 */
	__express: function(app) {
		// Express Middleware
		app.use(self._express_middleware);
		// Manual Flipping Route
		app.get('/fflip/:name/:action', self._express_route);
	},

};<|endoftext|>


<commit_before>/**
 * @module Ink.UI.TreeView_1
 * @author inkdev AT sapo.pt
 * @version 1
 */
Ink.createModule('Ink.UI.TreeView', '1', ['Ink.UI.Common_1','Ink.Dom.Event_1','Ink.Dom.Css_1','Ink.Dom.Element_1','Ink.Dom.Selector_1','Ink.Util.Array_1'], function(Common, Event, Css, Element, Selector, InkArray ) {
    'use strict';

    /**
     * Shows elements in a tree-like hierarchical structure.
     *
     * @class Ink.UI.TreeView
     * @constructor
     * @version 1
     * @param {String|DOMElement} selector
     * @param {String} [options.node='li'] Selector to define which elements are seen as nodes.
     * @param {String} [options.children='ul'] Selector to define which elements are represented as children.
     * @param {String} [options.parentClass='parent'] Classes to be added to the parent node.
     * @param {String} [options.openClass='fa fa-minus-circle'] Classes to be added to the icon when a parent is open.
     * @param {String} [options.closedClass='fa fa-plus-circle'] Classes to be added to the icon when a parent is closed.
     * @param {String} [options.hideClass='hide-all'] Class to toggle visibility of the children.
     * @param {String} [options.iconTag='i'] The name of icon tag. The component tries to find a tag with that name as a direct child of the node. If it doesn't find it, it creates it.
     * @param {Boolean} [options.stopDefault=true] Stops the default behavior of the click handler.
     * @example
     *

     *
root
     *

     *
child 1

     *
child 2
     *

     *
grandchild 2a

     *
grandchild 2b
     *

     *
grandgrandchild 1bA

     *
grandgrandchild 1bB

     *

     *

     *

     *

     *
child 3

     *

     *

     *

     *
     */
    var TreeView = function(selector, options){
        this._element = Common.elOrSelector(selector, '[Ink.UI.TreeView_1]');

        this._options = Common.options('Treeview', {
            'node':   ['String', 'li'],
            // [3.0.1] Deprecate this terrible, terrible name
            'child':  ['String','ul'],
            'children':  ['String','ul'],
            'parentClass': ['String','parent'],
            'openNodeClass': ['String', 'open'],
            'openClass': ['String','fa fa-minus-circle'],
            'closedClass': ['String','fa fa-plus-circle'],
            'hideClass': ['String','hide-all'],
            'iconTag': ['String', 'i'],
            'stopDefault' : ['Boolean', true]
        }, options || {}, this._element);

        if (this._options.child) {
            Ink.warn('Ink.UI.TreeView: options.child is being renamed to options.children.');
            this._options.children = this._options.child;
        }

        this._init();
    };

    TreeView.prototype = {

        /**
         * Init function called by the constructor. Sets the necessary event handlers.
         *
         * @method _init
         * @private
         */
        _init: function(){
            this._handlers = {
                click: Ink.bindEvent(this._onClick,this)
            };

            Event.on(this._element, 'click', this._options.node, this._handlers.click);

            InkArray.each(Ink.ss(this._options.node, this._element), Ink.bind(function(item){
                if( this.isParent(item) ) {
                    Css.addClassName(item, this._options.parentClass);

                    var isOpen = this.isOpen(item);
                    if( !this._getIcon(item) ){
                        Element.create(this._options.iconTag, { insertTop: item });
                    }

                    this._setNodeOpen(item, isOpen);
                }
            },this));
        },

        _getIcon: function (node) {
            return Ink.s('> ' + this._options.iconTag, node);
        },

        isOpen: function (node) {
            if (!this._getChild(node)) {
                throw new Error('not a node!');
            }

            return Element.data(node).open === 'true' ||
                Css.hasClassName(node, this._options.openNodeClass);
        },

        isParent: function (node) {
            return Css.hasClassName(node, this._options.parentClass) ||
                this._getChild(node) != null;
        },

        _setNodeOpen: function (node, beOpen) {
            var child = this._getChild(node);
            if (child) {
                Css.setClassName(child, this._options.hideClass, !beOpen);
                var icon = this._getIcon(node);

                node.setAttribute('data-open', beOpen);

                /*
                 * Don't refactor this to
                 *
                 * setClassName(el, className, status); setClassName(el, className, !status);
                 *
                 * because it won't work with multiple classes.
                 *
                 * Doing:
                 * setClassName(el, 'fa fa-whatever', true);setClassName(el, 'fa fa-whatever-else', false);
                 *
                 * will remove 'fa' although it is a class we want.
                 */

                var toAdd = beOpen ? this._options.openClass : this._options.closedClass;
                var toRemove = beOpen ? this._options.closedClass : this._options.openClass;
                Css.removeClassName(icon, toRemove);
                Css.addClassName(icon, toAdd);

                Css.setClassName(node, this._options.openNodeClass, beOpen);
            } else {
                Ink.error('Ink.UI.TreeView: node', node, 'is not a node!');
            }
        },

        open: function (node) {
            this._setNodeOpen(node, true);
        },

        close: function (node) {
            this._setNodeOpen(node, false);
        },

        toggle: function (node) {
            if (this.isOpen(node)) {
                this.close(node);
            } else {
                this.open(node);
            }
        },

        _getChild: function (node) {
            return Selector.select(this._options.child, node)[0] || null;
        },

        /**
         * Handles the click event (as specified in the _init function).
         *
         * @method _onClick
         * @param {Event} event
         * @private
         */
        _onClick: function(ev){
            /**
             * Summary:
             * If the clicked element is a "node" as defined in the options, will check if it has any "child".
             * If so, will toggle its state and stop the event's default behavior if the stopDefault option is true.
             **/

            if (!this.isParent(ev.currentTarget) ||
                    Selector.matchesSelector(ev.target, this._options.node) ||
                    Selector.matchesSelector(ev.target, this._options.child)) {
                return;
            }

            if (this._options.stopDefault){
                ev.preventDefault();
            }

            this.toggle(ev.currentTarget);
        }
    };

    return TreeView;
});
<commit_msg>new option name "children" in Ink.UI.TreeView wasn't working<commit_after>/**
 * @module Ink.UI.TreeView_1
 * @author inkdev AT sapo.pt
 * @version 1
 */
Ink.createModule('Ink.UI.TreeView', '1', ['Ink.UI.Common_1','Ink.Dom.Event_1','Ink.Dom.Css_1','Ink.Dom.Element_1','Ink.Dom.Selector_1','Ink.Util.Array_1'], function(Common, Event, Css, Element, Selector, InkArray ) {
    'use strict';

    /**
     * Shows elements in a tree-like hierarchical structure.
     *
     * @class Ink.UI.TreeView
     * @constructor
     * @version 1
     * @param {String|DOMElement} selector
     * @param {String} [options.node='li'] Selector to define which elements are seen as nodes.
     * @param {String} [options.children='ul'] Selector to define which elements are represented as children.
     * @param {String} [options.parentClass='parent'] Classes to be added to the parent node.
     * @param {String} [options.openClass='fa fa-minus-circle'] Classes to be added to the icon when a parent is open.
     * @param {String} [options.closedClass='fa fa-plus-circle'] Classes to be added to the icon when a parent is closed.
     * @param {String} [options.hideClass='hide-all'] Class to toggle visibility of the children.
     * @param {String} [options.iconTag='i'] The name of icon tag. The component tries to find a tag with that name as a direct child of the node. If it doesn't find it, it creates it.
     * @param {Boolean} [options.stopDefault=true] Stops the default behavior of the click handler.
     * @example
     *

     *
root
     *

     *
child 1

     *
child 2
     *

     *
grandchild 2a

     *
grandchild 2b
     *

     *
grandgrandchild 1bA

     *
grandgrandchild 1bB

     *

     *

     *

     *

     *
child 3

     *

     *

     *

     *
     */
    var TreeView = function(selector, options){
        this._element = Common.elOrSelector(selector, '[Ink.UI.TreeView_1]');

        this._options = Common.options('Treeview', {
            'node':   ['String', 'li'],
            // [3.0.1] Deprecate this terrible, terrible name
            'child':  ['String',null],
            'children':  ['String','ul'],
            'parentClass': ['String','parent'],
            'openNodeClass': ['String', 'open'],
            'openClass': ['String','fa fa-minus-circle'],
            'closedClass': ['String','fa fa-plus-circle'],
            'hideClass': ['String','hide-all'],
            'iconTag': ['String', 'i'],
            'stopDefault' : ['Boolean', true]
        }, options || {}, this._element);

        if (this._options.child) {
            Ink.warn('Ink.UI.TreeView: options.child is being renamed to options.children.');
            this._options.children = this._options.child;
        }

        this._init();
    };

    TreeView.prototype = {

        /**
         * Init function called by the constructor. Sets the necessary event handlers.
         *
         * @method _init
         * @private
         */
        _init: function(){
            this._handlers = {
                click: Ink.bindEvent(this._onClick,this)
            };

            Event.on(this._element, 'click', this._options.node, this._handlers.click);

            InkArray.each(Ink.ss(this._options.node, this._element), Ink.bind(function(item){
                if( this.isParent(item) ) {
                    Css.addClassName(item, this._options.parentClass);

                    var isOpen = this.isOpen(item);
                    if( !this._getIcon(item) ){
                        Element.create(this._options.iconTag, { insertTop: item });
                    }

                    this._setNodeOpen(item, isOpen);
                }
            },this));
        },

        _getIcon: function (node) {
            return Ink.s('> ' + this._options.iconTag, node);
        },

        isOpen: function (node) {
            if (!this._getChild(node)) {
                throw new Error('not a node!');
            }

            return Element.data(node).open === 'true' ||
                Css.hasClassName(node, this._options.openNodeClass);
        },

        isParent: function (node) {
            return Css.hasClassName(node, this._options.parentClass) ||
                this._getChild(node) != null;
        },

        _setNodeOpen: function (node, beOpen) {
            var child = this._getChild(node);
            if (child) {
                Css.setClassName(child, this._options.hideClass, !beOpen);
                var icon = this._getIcon(node);

                node.setAttribute('data-open', beOpen);

                /*
                 * Don't refactor this to
                 *
                 * setClassName(el, className, status); setClassName(el, className, !status);
                 *
                 * because it won't work with multiple classes.
                 *
                 * Doing:
                 * setClassName(el, 'fa fa-whatever', true);setClassName(el, 'fa fa-whatever-else', false);
                 *
                 * will remove 'fa' although it is a class we want.
                 */

                var toAdd = beOpen ? this._options.openClass : this._options.closedClass;
                var toRemove = beOpen ? this._options.closedClass : this._options.openClass;
                Css.removeClassName(icon, toRemove);
                Css.addClassName(icon, toAdd);

                Css.setClassName(node, this._options.openNodeClass, beOpen);
            } else {
                Ink.error('Ink.UI.TreeView: node', node, 'is not a node!');
            }
        },

        open: function (node) {
            this._setNodeOpen(node, true);
        },

        close: function (node) {
            this._setNodeOpen(node, false);
        },

        toggle: function (node) {
            if (this.isOpen(node)) {
                this.close(node);
            } else {
                this.open(node);
            }
        },

        _getChild: function (node) {
            return Selector.select(this._options.children, node)[0] || null;
        },

        /**
         * Handles the click event (as specified in the _init function).
         *
         * @method _onClick
         * @param {Event} event
         * @private
         */
        _onClick: function(ev){
            /**
             * Summary:
             * If the clicked element is a "node" as defined in the options, will check if it has any "child".
             * If so, will toggle its state and stop the event's default behavior if the stopDefault option is true.
             **/

            if (!this.isParent(ev.currentTarget) ||
                    Selector.matchesSelector(ev.target, this._options.node) ||
                    Selector.matchesSelector(ev.target, this._options.children)) {
                return;
            }

            if (this._options.stopDefault){
                ev.preventDefault();
            }

            this.toggle(ev.currentTarget);
        }
    };

    return TreeView;
});
<|endoftext|>


<commit_before>const { ObjectId: generateObjectId } = require('mongoose').Types;

const logger = require('../../../src/logger/index');
const TestEventEmitter = require('./mongooseEventListener');

const serviceHelpers = require('./services');

const warn = (message, pass) => {
	logger.warning(message);
	return pass;
};

const performanceMessurceLimits = {
	chars: 1000,
	time: 100,
	dbCalls: 10,
	paginate: 1000,
};

module.exports = (app, opt = { schoolId: '5f2987e020834114b8efd6f8', generateObjectId }) => {
	const {
		accounts,
		activation,
		classes,
		consents,
		consentVersion,
		courses,
		courseGroups,
		roles,
		schools,
		years,
		schoolGroups,
		datasources,
		files,
		homeworks,
		lessons,
		login,
		registrationPins,
		ltiTools,
		problems,
		pseudonyms,
		storageProviders,
		submissions,
		teams,
		testSystem,
		users,
	} = serviceHelpers(app, opt);

	const cleanup = () =>
		Promise.all(
			[
				accounts,
				activation,
				users,
				consents,
				consentVersion,
				testSystem,
				classes,
				courses,
				courseGroups,
				ltiTools,
				problems,
				pseudonyms,
				teams,
				registrationPins,
				roles,
				schools,
				schoolGroups,
				years,
				datasources,
				submissions,
				lessons,
				homeworks,
				storageProviders,
				files,
			]
				.reverse()
				.map((factory) => factory.cleanup())
		)
			.then((res) => {
				logger.info('[TestObjects] cleanup data.');
				return res;
			})
			.catch((err) => {
				logger.warning('[TestObjects] Can not cleanup.', err);
				return err;
			});

	const info = () => ({
		accounts: accounts.info,
		activation: activation.info,
		classes: classes.info,
		courseGroups: courseGroups.info,
		courses: courses.info,
		datasources: datasources.info,
		files: files.info,
		homeworks: homeworks.info,
		lessons: lessons.info,
		registrationPins: registrationPins.info,
		ltiTools: ltiTools.info,
		problems: problems.info,
		pseudonyms: pseudonyms.info,
		schoolGroups: schoolGroups.info,
		schools: schools.info,
		storageProviders: storageProviders.info,
		submissions: submissions.info,
		teams: teams.info,
		tempPins: users.tempPinIds,
		testSystem: testSystem.info,
		users: users.info,
		years: years.info,
	});

	const createTestTeamWithOwner = async (userData) => {
		const user = await users.create(userData);
		const team = await teams.create(user);
		return { team, user };
	};

	const setupUser = async (userData) => {
		try {
			const user = await users.create(userData);
			const requestParams = await login.generateRequestParamsFromUser(user);
			const { account } = requestParams;

			return {
				user,
				account,
				requestParams,
				userId: user._id.toString(),
				accountId: account._id.toString(),
			};
		} catch (err) {
			logger.warning(err);
			return err;
		}
	};

	const rnd = () => Math.round(Math.random() * 10000);
	const randomGen = () => `${Date.now()}_${rnd()}`;

	return {
		createTestAccount: accounts.create,
		createTestActivation: activation.create,
		createTestClass: classes.create,
		createTestConsent: consents.create,
		createTestConsentVersion: consentVersion.create,
		createTestCourse: courses.create,
		createTestCourseGroup: courseGroups.create,
		createTestDatasource: datasources.create,
		createTestFile: files.create,
		createTestHomework: homeworks.create,
		createTestLesson: lessons.create,
		lessons,
		createTestRegistrationPin: registrationPins.create,
		createTestProblem: problems.create,
		createTestPseudonym: pseudonyms.create,
		createTestLtiTool: ltiTools.create,
		createTestRole: roles.create,
		createTestSchool: schools.create,
		createTestSchoolGroup: schoolGroups.create,
		createTestStorageProvider: storageProviders.create,
		createTestSubmission: submissions.create,
		createTestSystem: testSystem.create,
		createTestUser: users.create,
		cleanup,
		generateJWT: login.generateJWT,
		generateRequestParams: login.generateRequestParams,
		generateRequestParamsFromUser: login.generateRequestParamsFromUser,
		generateJWTFromUser: login.generateJWTFromUser,
		createdUserIds: warn('@deprecated use info() instead', users.info),
		teams,
		files,
		classes,
		schools,
		years,
		users,
		createTestTeamWithOwner,
		info,
		setupUser,
		options: opt,
		randomGen,
		generateObjectId,
		TestEventEmitter,
		performanceMessurceLimits,
	};
};
<commit_msg>reduce service metric limits<commit_after>const { ObjectId: generateObjectId } = require('mongoose').Types;

const logger = require('../../../src/logger/index');
const TestEventEmitter = require('./mongooseEventListener');

const serviceHelpers = require('./services');

const warn = (message, pass) => {
	logger.warning(message);
	return pass;
};

const performanceMessurceLimits = {
	chars: 500,
	time: 50,
	dbCalls: 5,
	paginate: 100,
};

module.exports = (app, opt = { schoolId: '5f2987e020834114b8efd6f8', generateObjectId }) => {
	const {
		accounts,
		activation,
		classes,
		consents,
		consentVersion,
		courses,
		courseGroups,
		roles,
		schools,
		years,
		schoolGroups,
		datasources,
		files,
		homeworks,
		lessons,
		login,
		registrationPins,
		ltiTools,
		problems,
		pseudonyms,
		storageProviders,
		submissions,
		teams,
		testSystem,
		users,
	} = serviceHelpers(app, opt);

	const cleanup = () =>
		Promise.all(
			[
				accounts,
				activation,
				users,
				consents,
				consentVersion,
				testSystem,
				classes,
				courses,
				courseGroups,
				ltiTools,
				problems,
				pseudonyms,
				teams,
				registrationPins,
				roles,
				schools,
				schoolGroups,
				years,
				datasources,
				submissions,
				lessons,
				homeworks,
				storageProviders,
				files,
			]
				.reverse()
				.map((factory) => factory.cleanup())
		)
			.then((res) => {
				logger.info('[TestObjects] cleanup data.');
				return res;
			})
			.catch((err) => {
				logger.warning('[TestObjects] Can not cleanup.', err);
				return err;
			});

	const info = () => ({
		accounts: accounts.info,
		activation: activation.info,
		classes: classes.info,
		courseGroups: courseGroups.info,
		courses: courses.info,
		datasources: datasources.info,
		files: files.info,
		homeworks: homeworks.info,
		lessons: lessons.info,
		registrationPins: registrationPins.info,
		ltiTools: ltiTools.info,
		problems: problems.info,
		pseudonyms: pseudonyms.info,
		schoolGroups: schoolGroups.info,
		schools: schools.info,
		storageProviders: storageProviders.info,
		submissions: submissions.info,
		teams: teams.info,
		tempPins: users.tempPinIds,
		testSystem: testSystem.info,
		users: users.info,
		years: years.info,
	});

	const createTestTeamWithOwner = async (userData) => {
		const user = await users.create(userData);
		const team = await teams.create(user);
		return { team, user };
	};

	const setupUser = async (userData) => {
		try {
			const user = await users.create(userData);
			const requestParams = await login.generateRequestParamsFromUser(user);
			const { account } = requestParams;

			return {
				user,
				account,
				requestParams,
				userId: user._id.toString(),
				accountId: account._id.toString(),
			};
		} catch (err) {
			logger.warning(err);
			return err;
		}
	};

	const rnd = () => Math.round(Math.random() * 10000);
	const randomGen = () => `${Date.now()}_${rnd()}`;

	return {
		createTestAccount: accounts.create,
		createTestActivation: activation.create,
		createTestClass: classes.create,
		createTestConsent: consents.create,
		createTestConsentVersion: consentVersion.create,
		createTestCourse: courses.create,
		createTestCourseGroup: courseGroups.create,
		createTestDatasource: datasources.create,
		createTestFile: files.create,
		createTestHomework: homeworks.create,
		createTestLesson: lessons.create,
		lessons,
		createTestRegistrationPin: registrationPins.create,
		createTestProblem: problems.create,
		createTestPseudonym: pseudonyms.create,
		createTestLtiTool: ltiTools.create,
		createTestRole: roles.create,
		createTestSchool: schools.create,
		createTestSchoolGroup: schoolGroups.create,
		createTestStorageProvider: storageProviders.create,
		createTestSubmission: submissions.create,
		createTestSystem: testSystem.create,
		createTestUser: users.create,
		cleanup,
		generateJWT: login.generateJWT,
		generateRequestParams: login.generateRequestParams,
		generateRequestParamsFromUser: login.generateRequestParamsFromUser,
		generateJWTFromUser: login.generateJWTFromUser,
		createdUserIds: warn('@deprecated use info() instead', users.info),
		teams,
		files,
		classes,
		schools,
		years,
		users,
		createTestTeamWithOwner,
		info,
		setupUser,
		options: opt,
		randomGen,
		generateObjectId,
		TestEventEmitter,
		performanceMessurceLimits,
	};
};
<|endoftext|>


<commit_before>/**
 * 获取赛程job
 *
 * */
var request = require("request");

var START = new Date(2015, 4, 18);
var END = new Date(2015, 4, 21);
//var END = new Date(2014, 9, 30);
var fs = require("fs");
var path = require("path");
var CopyCat = require("../copycat");

var moment = require("moment");
var GameService = require("../../service/game_service");
var TeamService = require("../../service/team_service");

var format = function (date) {
    return moment(date).format("YYYY-MM-DD");
};

var nextDay = function () {
    current.setDate(current.getDate() + 1);
};

var current = new Date(START);

var result = [];
var logger = console;

var find = module.exports = function (runner) {
    logger = runner || console;
    if (current > END) {
        return;
    }
    console.log(format(current));
    CopyCat("http://g.hupu.com/nba/schedule/" + format(current), function ($) {
        var trs = $(".players_table tr");
        trs.each(function (i, tr) {
            tr = $(tr);
            if (tr.hasClass('linglei')) {
                //日期tr
                var match = tr.text().match(/(\d+)月(\d+)日/);
                if (match) {
                    current = new Date(current.getFullYear(), match[1] - 1, +match[2]);
                } else {
                    //TODO dom error
                }
            } else {
                var tds = tr.find("td");
                var time = tds.eq(0).text().match(/(\d+):(\d+)/);
                if(!time){
                    return;
                }
                var gameTime = new Date(current);
                gameTime.setHours(+time[1]);
                gameTime.setMinutes(+time[2]);
                gameTime.setSeconds(0);
                gameTime.setMilliseconds(0);

                var gameTeams = tds.eq(1).find("a");
                var hostName = gameTeams.eq(0).text();
                var visitName = gameTeams.eq(1).text();
                var ThirdID = 0;

                var link = tds.eq(2).find("a");
                if (link.length) {
                    var match = link.attr("href").match(/boxscore_(\d+)/);
                    if (match) {
                        ThirdID = match[1];
                    }
                }

                //Save Game
                GameService.addGame({
                    Type: 1,
                    HostID: TeamService.id(hostName),
                    HostName: hostName,
                    VisitID: TeamService.id(visitName),
                    VisitName: visitName,
                    Time: gameTime,
                    Status: 0,
                    WinnerID: 0,
                    Memo: "NBA季后赛 " + TeamService.short(hostName) + "-" + TeamService.short(visitName),
                    IsPlayOff: 1,
                    Season: 20142015,
                    ThirdID:ThirdID,
                    HostScore:0,
                    VisitScore:0
                }, function (e) {
                    console.log(e)
                })
            }
        });

        nextDay();
        find();
    });

};

find();
<commit_msg>find schedule<commit_after>/**
 * 获取赛程job
 *
 * */
var request = require("request");

var START = new Date(2015, 4, 19);
var END = new Date(2015, 4, 30);
//var END = new Date(2014, 9, 30);
var fs = require("fs");
var path = require("path");
var CopyCat = require("../copycat");

var moment = require("moment");
var GameService = require("../../service/game_service");
var TeamService = require("../../service/team_service");

var format = function (date) {
    return moment(date).format("YYYY-MM-DD");
};

var nextDay = function () {
    current.setDate(current.getDate() + 1);
};

var current = new Date(START);

var result = [];
var logger = console;

var find = module.exports = function (runner) {
    logger = runner || console;
    if (current > END) {
        return;
    }
    console.log(format(current));
    CopyCat("http://g.hupu.com/nba/schedule/" + format(current), function ($) {
        var trs = $(".players_table tr");
        trs.each(function (i, tr) {
            tr = $(tr);
            if (tr.hasClass('linglei')) {
                //日期tr
                var match = tr.text().match(/(\d+)月(\d+)日/);
                if (match) {
                    current = new Date(current.getFullYear(), match[1] - 1, +match[2]);
                } else {
                    //TODO dom error
                }
            } else {
                var tds = tr.find("td");
                var time = tds.eq(0).text().match(/(\d+):(\d+)/);
                if (!time) {
                    return;
                }
                var gameTime = new Date(current);
                gameTime.setHours(+time[1]);
                gameTime.setMinutes(+time[2]);
                gameTime.setSeconds(0);
                gameTime.setMilliseconds(0);

                var gameTeams = tds.eq(1).find("a");
                var hostName = gameTeams.eq(0).text();
                var visitName = gameTeams.eq(1).text();
                var ThirdID = 0;

                var link = tds.eq(2).find("a");
                if (link.length) {
                    var match = link.attr("href").match(/boxscore_(\d+)/);
                    if (match) {
                        ThirdID = match[1];
                    }
                }

                //Save Game
                TeamService.wait(function () {

                    GameService.addGame({
                        Type: 1,
                        HostID: TeamService.id(hostName),
                        HostName: hostName,
                        VisitID: TeamService.id(visitName),
                        VisitName: visitName,
                        Time: gameTime,
                        Status: 0,
                        WinnerID: 0,
                        Memo: "NBA季后赛 " + TeamService.short(hostName) + "-" + TeamService.short(visitName),
                        IsPlayOff: 1,
                        Season: 20142015,
                        ThirdID: ThirdID,
                        HostScore: 0,
                        VisitScore: 0
                    }, function (e) {
                        if(e){
                            console.log(e)
                        }
                    })
                })
            }
        });

        nextDay();
        find();
    });

};

find();
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
define([
  'INST' /* INST */,
  'i18n!instructure',
  'jquery' /* jQuery, $ */,
  'underscore',
  'vendor/scribd.view' /* scribd */,
  'str/htmlEscape' /* htmlEscape, /\$\.h/ */,
  'jquery.ajaxJSON' /* ajaxJSON */,
  'jquery.google-analytics' /* trackEvent */,
  'jquery.instructure_misc_helpers' /*  /\$\.uniq/, capitalize */,
  'jquery.loadingImg' /* loadingImage */
], function(INST, I18n, $, _, scribd, htmlEscape) {

  // first element in array is if scribd can handle it, second is if google can.
  var previewableMimeTypes = {
      "application/vnd.openxmlformats-officedocument.wordprocessingml.template":   [1, 1],
      "application/vnd.oasis.opendocument.spreadsheet":                            [1, 1],
      "application/vnd.sun.xml.writer":                                            [1, 1],
      "application/excel":                                                         [1, 1],
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":         [1, 1],
      "text/rtf":                                                                  [1, false],
      "application/vnd.openxmlformats-officedocument.spreadsheetml.template":      [1, 1],
      "application/vnd.sun.xml.impress":                                           [1, 1],
      "application/vnd.sun.xml.calc":                                              [1, 1],
      "application/vnd.ms-excel":                                                  [1, 1],
      "application/msword":                                                        [1, 1],
      "application/mspowerpoint":                                                  [1, 1],
      "application/rtf":                                                           [1, 1],
      "application/vnd.oasis.opendocument.presentation":                           [1, 1],
      "application/vnd.oasis.opendocument.text":                                   [1, 1],
      "application/vnd.openxmlformats-officedocument.presentationml.template":     [1, 1],
      "application/vnd.openxmlformats-officedocument.presentationml.slideshow":    [1, 1],
      "text/plain":                                                                [1, false],
      "application/vnd.openxmlformats-officedocument.presentationml.presentation": [1, 1],
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document":   [1, 1],
      "application/postscript":                                                    [1, 1],
      "application/pdf":                                                           [1, 1],
      "application/vnd.ms-powerpoint":                                             [1, 1]

  };

  $.filePreviewsEnabled = function(){
    return !(
      INST.disableCrocodocPreviews &&
      INST.disableScribdPreviews &&
      INST.disableGooglePreviews
    );
  }

  // check to see if a file of a certan mimeType is previewable inline in the browser by either scribd or googleDocs
  // ex: $.isPreviewable("application/mspowerpoint")  -> true
  //     $.isPreviewable("application/rtf", 'google') -> false
  $.isPreviewable = function(mimeType, service){
    return $.filePreviewsEnabled() && previewableMimeTypes[mimeType] && (
      !service ||
      (!INST['disable' + $.capitalize(service) + 'Previews'] && previewableMimeTypes[mimeType][{scribd: 0, google: 1}[service]])
    );
  };

  $.fn.loadDocPreview = function(options) {
    // if it is a scribd doc and flash is available
    var flashVersion = swfobject.getFlashPlayerVersion(),
        hasGoodEnoughFlash = flashVersion && flashVersion.major > 9;

    return this.each(function(){
      var $this = $(this),
          opts = $.extend({
            width: '100%',
            height: '400px'
          }, $this.data(), options);

      function tellAppIViewedThisInline(serviceUsed){
        // if I have a url to ping back to the app that I viewed this file inline, ping it.
        if (opts.attachment_view_inline_ping_url) {
          $.ajaxJSON(opts.attachment_view_inline_ping_url, 'POST', {}, function() { }, function() { });
          $.trackEvent('Doc Previews', serviceUsed, JSON.stringify(opts));
        }
      }

      if (!INST.disableCrocodocPreviews && opts.crocodoc_session_url) {
        var iframe = $('', {
            src: opts.crocodoc_session_url,
            width: opts.width,
            height: opts.height
        });
        iframe.appendTo($this);
        iframe.load(function() {
          tellAppIViewedThisInline('crocodoc');
          if ($.isFunction(opts.ready))
            opts.ready();
        });
      }
      else if (!INST.disableScribdPreviews && opts.scribd_doc_id && opts.scribd_access_key && hasGoodEnoughFlash && scribd) {
        var scribdDoc = scribd.Document.getDoc( opts.scribd_doc_id, opts.scribd_access_key ),
            id = $this.attr('id'),
            // see http://www.scribd.com/developers/api?method_name=Javascript+API for an explaination of these options
            scribdParams = $.extend({
              'jsapi_version': 1,
              'disable_related_docs': true, //Disables the related documents tab in List Mode.
              'auto_size' : false, //When false, this parameter forces Scribd Reader to use the provided width and height rather than using a width multiplier of 85/110.
              'height' : opts.height,
              'use_ssl' : 'https:' == document.location.protocol
            }, opts.scribdParams);

        if (!id) {
          id = _.uniqueId("scribd_preview_");
          $this.attr('id', id);
        }
        $.each(scribdParams, function(key, value){
          scribdDoc.addParam(key, value);
        });
        if ($.isFunction(opts.ready)) {
          scribdDoc.addEventListener('iPaperReady', opts.ready);
        }
        scribdDoc.write( id );

        // this is a hack so that the <embed> doesn't throw a bunch of these errors after it is .remove()d:
        // Uncaught TypeError: Object #<HTMLEmbedElement> has no method 'keyboardShortcut(Up/Down)'
        // They come from the actionscript running in their viewer:
        // see: http://dragstudio.com/app/ScribdViewer/javascript/_-0j.as
        // and http://groups.google.com/group/scribd-platform-developers/msg/46a4f12db73d02d8
        this.childNodes[0].keyboardShortcutDown = this.childNodes[0].keyboardShortcutUp = function(){};

        tellAppIViewedThisInline('scribd');
      } else if (!INST.disableGooglePreviews && (!opts.mimeType || $.isPreviewable(opts.mimeType, 'google')) && opts.attachment_id || opts.public_url){
        // else if it's something google docs preview can handle and we can get a public url to this document.
        function loadGooglePreview(){
          // this handles both ssl and plain http.
          var googleDocPreviewUrl = '//docs.google.com/viewer?' + $.param({
            embedded: true,
            url: opts.public_url
          });
          $('')
            .appendTo($this)
            .load(function(){
              tellAppIViewedThisInline('google');
              if ($.isFunction(opts.ready)) {
                opts.ready();
              }
            });
        }
        if (opts.public_url) {
          loadGooglePreview()
        } else if (opts.attachment_id) {
          var url = '/files/'+opts.attachment_id+'/public_url.json';
          if (opts.submission_id) {
            url += '?' + $.param({ submission_id: opts.submission_id });
          }
          $this.loadingImage();
          $.ajaxJSON(url, 'GET', {}, function(data){
            $this.loadingImage('remove');
            if (data && data.public_url) {
              $.extend(opts, data);
              loadGooglePreview();
            }
          });
        }
      } else {
        // else fall back with a message that the document can't be viewed inline
        $this.html('
' + htmlEscape(I18n.t('errors.cannot_view_document_inline', 'This document cannot be viewed inline, you might not have permission to view it or it might have been deleted.')) + '
');
      }
    });
  };

});
<commit_msg>use HTML5 for scribd embeds<commit_after>/**
 * Copyright (C) 2011 Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
define([
  'INST' /* INST */,
  'i18n!instructure',
  'jquery' /* jQuery, $ */,
  'underscore',
  'vendor/scribd.view' /* scribd */,
  'str/htmlEscape' /* htmlEscape, /\$\.h/ */,
  'jquery.ajaxJSON' /* ajaxJSON */,
  'jquery.google-analytics' /* trackEvent */,
  'jquery.instructure_misc_helpers' /*  /\$\.uniq/, capitalize */,
  'jquery.loadingImg' /* loadingImage */
], function(INST, I18n, $, _, scribd, htmlEscape) {

  // first element in array is if scribd can handle it, second is if google can.
  var previewableMimeTypes = {
      "application/vnd.openxmlformats-officedocument.wordprocessingml.template":   [1, 1],
      "application/vnd.oasis.opendocument.spreadsheet":                            [1, 1],
      "application/vnd.sun.xml.writer":                                            [1, 1],
      "application/excel":                                                         [1, 1],
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":         [1, 1],
      "text/rtf":                                                                  [1, false],
      "application/vnd.openxmlformats-officedocument.spreadsheetml.template":      [1, 1],
      "application/vnd.sun.xml.impress":                                           [1, 1],
      "application/vnd.sun.xml.calc":                                              [1, 1],
      "application/vnd.ms-excel":                                                  [1, 1],
      "application/msword":                                                        [1, 1],
      "application/mspowerpoint":                                                  [1, 1],
      "application/rtf":                                                           [1, 1],
      "application/vnd.oasis.opendocument.presentation":                           [1, 1],
      "application/vnd.oasis.opendocument.text":                                   [1, 1],
      "application/vnd.openxmlformats-officedocument.presentationml.template":     [1, 1],
      "application/vnd.openxmlformats-officedocument.presentationml.slideshow":    [1, 1],
      "text/plain":                                                                [1, false],
      "application/vnd.openxmlformats-officedocument.presentationml.presentation": [1, 1],
      "application/vnd.openxmlformats-officedocument.wordprocessingml.document":   [1, 1],
      "application/postscript":                                                    [1, 1],
      "application/pdf":                                                           [1, 1],
      "application/vnd.ms-powerpoint":                                             [1, 1]

  };

  $.filePreviewsEnabled = function(){
    return !(
      INST.disableCrocodocPreviews &&
      INST.disableScribdPreviews &&
      INST.disableGooglePreviews
    );
  }

  // check to see if a file of a certan mimeType is previewable inline in the browser by either scribd or googleDocs
  // ex: $.isPreviewable("application/mspowerpoint")  -> true
  //     $.isPreviewable("application/rtf", 'google') -> false
  $.isPreviewable = function(mimeType, service){
    return $.filePreviewsEnabled() && previewableMimeTypes[mimeType] && (
      !service ||
      (!INST['disable' + $.capitalize(service) + 'Previews'] && previewableMimeTypes[mimeType][{scribd: 0, google: 1}[service]])
    );
  };

  $.fn.loadDocPreview = function(options) {

    return this.each(function(){
      var $this = $(this),
          opts = $.extend({
            width: '100%',
            height: '400px'
          }, $this.data(), options);

      function tellAppIViewedThisInline(serviceUsed){
        // if I have a url to ping back to the app that I viewed this file inline, ping it.
        if (opts.attachment_view_inline_ping_url) {
          $.ajaxJSON(opts.attachment_view_inline_ping_url, 'POST', {}, function() { }, function() { });
          $.trackEvent('Doc Previews', serviceUsed, JSON.stringify(opts));
        }
      }

      function makeOnLoadHandler(serviceToUse){
        return function(){
          tellAppIViewedThisInline(serviceToUse);
          if ($.isFunction(opts.ready)) opts.ready();
        }
      }

      if (!INST.disableCrocodocPreviews && opts.crocodoc_session_url) {
        var iframe = $('', {
            src: opts.crocodoc_session_url,
            width: opts.width,
            height: opts.height
        });
        iframe.appendTo($this);
        iframe.load(makeOnLoadHandler('crocodoc'));

      } else if (!INST.disableScribdPreviews && opts.scribd_doc_id && opts.scribd_access_key) {

        // see http://www.scribd.com/developers/api?method_name=Javascript+API for an explaination of these options
        var scribdParams = $.extend({
              'auto_size' : false, //When false, this parameter forces Scribd Reader to use the provided width and height rather than using a width multiplier of 85/110.
              'height' : opts.height
            }, opts.scribdParams);

        var scribdIframeUrl = '//www.scribd.com/embeds/' + opts.scribd_doc_id + '/content?' + $.param({
          start_page: 1,
          view_mode: 'list',
          access_key: opts.scribd_access_key
        });
        var el = $('')
          .appendTo($this)
          .load(makeOnLoadHandler('scribd'))[0];

        // START COPIED SNIPPITT STRAIGHT FROM: http://www.scribd.com/javascripts/embed_code/inject.js
        // Set the height for auto-height elements
        if (el.getAttribute('data-auto-height') === 'true' && el.getAttribute('data-auto-resized') !== 'true') {
            var aspect_ratio = 1 / el.getAttribute('data-aspect-ratio');
            if (aspect_ratio === Infinity) {
              aspect_ratio = 1;
            }
            var height = Math.round(el.clientWidth * aspect_ratio) + 25;
            el.style.height = height + "px";
            el.setAttribute('data-auto-resized', 'true');
        }
        // END COPIED SNIPPETT

      } else if (!INST.disableGooglePreviews && (!opts.mimeType || $.isPreviewable(opts.mimeType, 'google')) && opts.attachment_id || opts.public_url){
        // else if it's something google docs preview can handle and we can get a public url to this document.
        function loadGooglePreview(){
          // this handles both ssl and plain http.
          var googleDocPreviewUrl = '//docs.google.com/viewer?' + $.param({
            embedded: true,
            url: opts.public_url
          });
          $('')
            .appendTo($this)
            .load(makeOnLoadHandler('google'));
        }
        if (opts.public_url) {
          loadGooglePreview()
        } else if (opts.attachment_id) {
          var url = '/files/'+opts.attachment_id+'/public_url.json';
          if (opts.submission_id) {
            url += '?' + $.param({ submission_id: opts.submission_id });
          }
          $this.loadingImage();
          $.ajaxJSON(url, 'GET', {}, function(data){
            $this.loadingImage('remove');
            if (data && data.public_url) {
              $.extend(opts, data);
              loadGooglePreview();
            }
          });
        }
      } else {
        // else fall back with a message that the document can't be viewed inline
        $this.html('
' + htmlEscape(I18n.t('errors.cannot_view_document_inline', 'This document cannot be viewed inline, you might not have permission to view it or it might have been deleted.')) + '
');
      }
    });
  };

});
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react'
import {
  Editor, EditorState, RichUtils, convertToRaw, Entity, Modifier,
  CompositeDecorator
} from 'draft-js'
import { stateToMarkdown } from 'draft-js-export-markdown'
import { stateFromMarkdown } from 'draft-js-import-markdown'
import RichControlGroup from './rich-control-group'
import ContextualInput from './contextual-input'
import Link, { createLinkEntity, findLinkEntities } from './rich-label-link'
import Icon, { createIconEntity, findIconEntities } from './rich-label-icon'
//default styles for the editor (we need this at least to make the placeholder behave as expected)
import '../../css/Draft.css'
import classnames from 'classnames'
import _ from 'lodash'
import { getEntitySelectionState } from '../utils/get-entity-selection'
import { getEntityAtCursor } from '../utils/get-entity-at-cursor'
const STYLES = {
  BOLD: 'BOLD',
  ITALIC: 'ITALIC'
}
const { BOLD, ITALIC } = STYLES
const LINK = 'LINK'

// console.log(convertToRaw(this.state.editorState.getCurrentContent()))
// console.log(stateToMarkdown(this.state.editorState.getCurrentContent()))
//Add the `singleline` class to the block wrapper (in order to disable white
//space wrapping and hide overflow)
//We do it whatever the block is (we should only have one block if we use
//a single line input).
function singleLineFn() {
  return 'singleline'
}

function multilineFn() {
  return 'multiline'
}
/**
 * We decoupled the Editor and the controls to enable fine grained positioning.
 */
export default class RichLabel extends Component {

  constructor(props) {
    super(props)
    const editorState = EditorState.createWithContent(
      stateFromMarkdown(props.initialValue),
      new CompositeDecorator([{
        strategy: findLinkEntities,
        component: Link
      }, {
        strategy: findIconEntities,
        component: Icon
      }])
    )
    const content = editorState.getCurrentContent()

    this.state = {
      focus: false,
      editorState,
      linkEdited: false,
      linkInfo: '',
      iconEdited: false,
      iconInfo: '',
      content
    }
    this.onChange = (editorState) => {
      this.setState({ editorState })
      const newContent = editorState.getCurrentContent()
      if (newContent === this.state.content) return
    }
    this.handleKeyCommand = this.handleKeyCommand.bind(this);
    this.linkFocus = () => {}
    this.iconFocus = () => {}

    this.toggleLink = () => {
      const { editorState } = this.state;
      const selection = editorState.getSelection()
      const content = editorState.getCurrentContent()
      const entityKey = getEntityAtCursor(editorState)
      let url = ''
      if (entityKey) {
        const entity = Entity.get(entityKey)
        if (entity.getType() === 'LINK') url = entity.getData().url
      }
      if (!selection.isCollapsed()) {
        this.setState({
          linkEdited: true,
          linkInfo: url,
        }, () => {
          setTimeout(this.linkFocus(), 0);
        });
      }
    }
    this.confirmLink = () => {
      const { editorState, linkInfo } = this.state
      const entityKey = createLinkEntity(linkInfo)
      this.setState({
        editorState: RichUtils.toggleLink(
          editorState,
          editorState.getSelection(),
          entityKey
        ),
        linkEdited: false,
        linkInfo: '',
      }, () => {
        setTimeout(() => {
          this.refs.editor.focus()
        }, 0)
      });
    }

    this.toggleIcon = () => {
      const { editorState } = this.state;
      const selection = editorState.getSelection()
      const content = editorState.getCurrentContent()
      const entityKey = getEntityAtCursor(editorState)
      let info = ''
      if (entityKey) {
        const entity = Entity.get(entityKey)
        if (entity.getType() === 'ICON') info = entity.getData().info
      }
      if (!selection.isCollapsed()) {
        this.setState({
          iconEdited: true,
          iconInfo: info,
        }, () => {
          setTimeout(this.iconFocus(), 0);
        });
      }
    }
    this.confirmIcon = () => {
      const { editorState, iconInfo } = this.state
      const entityKey = createIconEntity(iconInfo)
      const modif = Modifier.applyEntity(
        editorState.getCurrentContent(),
        editorState.getSelection(),
        entityKey)
      const newEditorState = EditorState.push(editorState, modif, 'apply-entity')
      this.setState({
        editorState: newEditorState,
        iconEdited: false,
        iconInfo: '',
      }, () => {
        setTimeout(() => {
          this.refs.editor.focus()
        }, 0)
      });
    }

    this.controlActions = {
      BOLD: {
        toggle: this.toggleInlineStyle.bind(this, BOLD),
        isSet: () => this.state.editorState.getCurrentInlineStyle().has(BOLD)
      },
      ITALIC: {
        toggle: this.toggleInlineStyle.bind(this, ITALIC),
        isSet: () => this.state.editorState.getCurrentInlineStyle().has(ITALIC)
      },
      LINK: {
        toggle: this.toggleLink,
        isSet: () => this.state.linkEdited
      },
      ICONE: {
        toggle: this.toggleIcon,
        isSet: () => this.state.iconEdited
      }
    }
    this.linkInfoChange = text => this.setState({ linkInfo: text })
    this.iconInfoChange = text => this.setState({ iconInfo: text })
    this.handleBlur = this.handleBlur.bind(this)
  }

  toggleInlineStyle(inlineStyle) {
    this.onChange(
      RichUtils.toggleInlineStyle(
        this.state.editorState,
        inlineStyle
      )
    );
  }

  handleKeyCommand(command) {
    const newState = RichUtils.handleKeyCommand(this.state.editorState, command);
    if (newState) {
      this.onChange(newState);
      return true;
    }
    return false;
  }

  handleBlur() {
    this.setState({ focus: false })
    this.props.onChange(stateToMarkdown(this.state.editorState.getCurrentContent()))
  }

  render() {
    const {
      editorState, focus, linkEdited, linkInfo, iconEdited, iconInfo
    } = this.state
    const { locale, placeholder, canPaste, multiline } = this.props
    return (

          <RichControlGroup className="btn-group btn-group-xs"
            controls={this.controlActions} locale={locale} />
            { linkEdited &&

              <ContextualInput
                text={linkInfo}
                placeholder={'Entrez une url'}
                onChange={this.linkInfoChange}
                onEnter={this.confirmLink} />

            }
            { iconEdited &&

                <ContextualInput
                  text={iconInfo}
                  placeholder={'Entrez une information'}
                  onChange={this.iconInfoChange}
                  onEnter={this.confirmIcon} />

            }

 this.setState({ focus: true })}
          onBlur={this.handleBlur}>
          <Editor
            blockStyleFn={multiline ? multilineFn : singleLineFn}
            handlePastedText={(text, html) => {
              //TODO replace with newlines with whitespaces, and then update
              //editorState
              return false
            }}
            // disable new lines
            handleReturn={() => !multiline}
            placeholder={placeholder}
            ref='editor'
            editorState={this.state.editorState}
            //We want to avoid editor state serialization each time a key is
            //pressed, so `onChange` will not throw an aciton to the store). The
            //content will be saved when the editor loses focus (which neccarily
            //happens when we click on the save button for the questionnaire).
            //At first, we threw an action each time the editor state changed,
            //but sometimes the ui was not responding very well (but we
            //serialized a markdown representation of the state, ant it might
            //be this part of the serialiaztion which took too much time).
            onChange={this.onChange}
            handleKeyCommand={this.handleKeyCommand}/>

    )
  }
}

RichLabel.propTypes = {
  onChange: PropTypes.func.isRequired,
  initialValue: PropTypes.string.isRequired,
  placeholder: PropTypes.string.isRequired,
  canPaste: PropTypes.bool.isRequired,
  multiline: PropTypes.bool.isRequired,
  locale: PropTypes.object.isRequired
}<commit_msg>Add paste facility to rich label<commit_after>import React, { PropTypes, Component } from 'react'
import {
  Editor, EditorState, RichUtils, convertToRaw, Entity, Modifier,
  CompositeDecorator
} from 'draft-js'
import { stateToMarkdown } from 'draft-js-export-markdown'
import { stateFromMarkdown } from 'draft-js-import-markdown'
import RichControlGroup from './rich-control-group'
import ContextualInput from './contextual-input'
import Link, { createLinkEntity, findLinkEntities } from './rich-label-link'
import Icon, { createIconEntity, findIconEntities } from './rich-label-icon'
//default styles for the editor (we need this at least to make the placeholder behave as expected)
import '../../css/Draft.css'
import classnames from 'classnames'
import _ from 'lodash'
import { getEntitySelectionState } from '../utils/get-entity-selection'
import { getEntityAtCursor } from '../utils/get-entity-at-cursor'
const STYLES = {
  BOLD: 'BOLD',
  ITALIC: 'ITALIC'
}
const { BOLD, ITALIC } = STYLES
const LINK = 'LINK'

function replaceText(editorState, text) {
  var newContent = Modifier.replaceText(
    editorState.getCurrentContent(),
    editorState.getSelection(),
    text
  );
  return EditorState.push(
    editorState,
    newContent,
    'replace-text'
  );
}

//Add the `singleline` class to the block wrapper (in order to disable white
//space wrapping and hide overflow)
//We do it whatever the block is (we should only have one block if we use
//a single line input).
function singleLineFn() {
  return 'singleline'
}

function multilineFn() {
  return 'multiline'
}
/**
 * We decoupled the Editor and the controls to enable fine grained positioning.
 */
export default class RichLabel extends Component {

  constructor(props) {
    super(props)
    const editorState = EditorState.createWithContent(
      stateFromMarkdown(props.initialValue),
      new CompositeDecorator([{
        strategy: findLinkEntities,
        component: Link
      }, {
        strategy: findIconEntities,
        component: Icon
      }])
    )
    const content = editorState.getCurrentContent()

    this.state = {
      focus: false,
      editorState,
      linkEdited: false,
      linkInfo: '',
      iconEdited: false,
      iconInfo: '',
      content
    }
    this.onChange = (editorState) => {
      this.setState({ editorState })
      const newContent = editorState.getCurrentContent()
      if (newContent === this.state.content) return
    }
    this.handleKeyCommand = this.handleKeyCommand.bind(this);
    this.linkFocus = () => {}
    this.iconFocus = () => {}

    this.toggleLink = () => {
      const { editorState } = this.state;
      const selection = editorState.getSelection()
      const content = editorState.getCurrentContent()
      const entityKey = getEntityAtCursor(editorState)
      let url = ''
      if (entityKey) {
        const entity = Entity.get(entityKey)
        if (entity.getType() === 'LINK') url = entity.getData().url
      }
      if (!selection.isCollapsed()) {
        this.setState({
          linkEdited: true,
          linkInfo: url,
        }, () => {
          setTimeout(this.linkFocus(), 0);
        });
      }
    }
    this.confirmLink = () => {
      const { editorState, linkInfo } = this.state
      const entityKey = createLinkEntity(linkInfo)
      this.setState({
        editorState: RichUtils.toggleLink(
          editorState,
          editorState.getSelection(),
          entityKey
        ),
        linkEdited: false,
        linkInfo: '',
      }, () => {
        setTimeout(() => {
          this.refs.editor.focus()
        }, 0)
      });
    }

    this.toggleIcon = () => {
      const { editorState } = this.state;
      const selection = editorState.getSelection()
      const content = editorState.getCurrentContent()
      const entityKey = getEntityAtCursor(editorState)
      let info = ''
      if (entityKey) {
        const entity = Entity.get(entityKey)
        if (entity.getType() === 'ICON') info = entity.getData().info
      }
      if (!selection.isCollapsed()) {
        this.setState({
          iconEdited: true,
          iconInfo: info,
        }, () => {
          setTimeout(this.iconFocus(), 0);
        });
      }
    }
    this.confirmIcon = () => {
      const { editorState, iconInfo } = this.state
      const entityKey = createIconEntity(iconInfo)
      const modif = Modifier.applyEntity(
        editorState.getCurrentContent(),
        editorState.getSelection(),
        entityKey)
      const newEditorState = EditorState.push(editorState, modif, 'apply-entity')
      this.setState({
        editorState: newEditorState,
        iconEdited: false,
        iconInfo: '',
      }, () => {
        setTimeout(() => {
          this.refs.editor.focus()
        }, 0)
      });
    }

    this.controlActions = {
      BOLD: {
        toggle: this.toggleInlineStyle.bind(this, BOLD),
        isSet: () => this.state.editorState.getCurrentInlineStyle().has(BOLD)
      },
      ITALIC: {
        toggle: this.toggleInlineStyle.bind(this, ITALIC),
        isSet: () => this.state.editorState.getCurrentInlineStyle().has(ITALIC)
      },
      LINK: {
        toggle: this.toggleLink,
        isSet: () => this.state.linkEdited
      },
      ICONE: {
        toggle: this.toggleIcon,
        isSet: () => this.state.iconEdited
      }
    }
    this.linkInfoChange = text => this.setState({ linkInfo: text })
    this.iconInfoChange = text => this.setState({ iconInfo: text })
    this.pasteRawText = this.pasteRawText.bind(this)
    this.handleBlur = this.handleBlur.bind(this)
  }

  toggleInlineStyle(inlineStyle) {
    this.onChange(
      RichUtils.toggleInlineStyle(
        this.state.editorState,
        inlineStyle
      )
    );
  }

  pasteRawText(text, removeNewLines) {
    ///see draft-js/src/component/handlers/edit/editOnPaste.js
    text = removeNewLines ? text.replace(/[\n\r]/g, ' ') : text
    this.setState({ editorState: replaceText(this.state.editorState, text) })
  }

  handleKeyCommand(command) {
    const newState = RichUtils.handleKeyCommand(this.state.editorState, command);
    if (newState) {
      this.onChange(newState);
      return true;
    }
    return false;
  }

  handleBlur() {
    this.setState({ focus: false })
    this.props.onChange(stateToMarkdown(this.state.editorState.getCurrentContent()))
  }

  render() {
    const {
      editorState, focus, linkEdited, linkInfo, iconEdited, iconInfo
    } = this.state
    const { locale, placeholder, canPaste, multiline } = this.props
    return (

          <RichControlGroup className="btn-group btn-group-xs"
            controls={this.controlActions} locale={locale} />
            { linkEdited &&

              <ContextualInput
                text={linkInfo}
                placeholder={'Entrez une url'}
                onChange={this.linkInfoChange}
                onEnter={this.confirmLink} />

            }
            { iconEdited &&

                <ContextualInput
                  text={iconInfo}
                  placeholder={'Entrez une information'}
                  onChange={this.iconInfoChange}
                  onEnter={this.confirmIcon} />

            }

 this.setState({ focus: true })}
          onBlur={this.handleBlur}>
          <Editor
            blockStyleFn={multiline ? multilineFn : singleLineFn}
            handlePastedText={(text, html) => {
              this.pasteRawText(text, true)
              return true
            }}
            // disable new lines
            handleReturn={() => !multiline}
            placeholder={placeholder}
            ref='editor'
            editorState={this.state.editorState}
            //We want to avoid editor state serialization each time a key is
            //pressed, so `onChange` will not throw an aciton to the store). The
            //content will be saved when the editor loses focus (which neccarily
            //happens when we click on the save button for the questionnaire).
            //At first, we threw an action each time the editor state changed,
            //but sometimes the ui was not responding very well (but we
            //serialized a markdown representation of the state, ant it might
            //be this part of the serialiaztion which took too much time).
            onChange={this.onChange}
            handleKeyCommand={this.handleKeyCommand}/>

    )
  }
}

RichLabel.propTypes = {
  onChange: PropTypes.func.isRequired,
  initialValue: PropTypes.string.isRequired,
  placeholder: PropTypes.string.isRequired,
  canPaste: PropTypes.bool.isRequired,
  multiline: PropTypes.bool.isRequired,
  locale: PropTypes.object.isRequired
}<|endoftext|>