Skip to content
filter.filter_html.admin.js 6.97 KiB
Newer Older
/**
 * @file
 * Attaches behavior for updating filter_html's settings automatically.
 */

(function ($, _, document, window) {


    /**
     * Implement a live setting parser to prevent text editors from automatically
     * enabling buttons that are not allowed by this filter's configuration.
     *
     * @namespace
     */
    Drupal.filterConfiguration.liveSettingParsers.filter_html = {
       *   An array of filter rules.
      getRules: function () {
        var currentValue = $('#edit-filters-filter-html-settings-allowed-html').val();

        // Build a FilterHTMLRule that reflects the hard-coded behavior that
        // strips all "style" attribute and all "on*" attributes.
        rule = new Drupal.FilterHTMLRule();
        rule.restrictedTags.tags = ['*'];
        rule.restrictedTags.forbidden.attributes = ['style', 'on*'];
        rules.push(rule);

        // Build a FilterHTMLRule that reflects the current settings.
        rule = new Drupal.FilterHTMLRule();
        var behavior = Drupal.behaviors.filterFilterHtmlUpdating;
        rule.allow = true;
        rule.tags = behavior._parseSetting(currentValue);
        rules.push(rule);

        return rules;
      }
    };
  }
   * Displays and updates what HTML tags are allowed to use in a filter.
   *
   * @type {Drupal~behavior}
   *
   * @todo Remove everything but 'attach' and 'detach' and make a proper object.
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches behavior for updating allowed HTML tags.
  Drupal.behaviors.filterFilterHtmlUpdating = {
    // The form item contains the "Allowed HTML tags" setting.
    // The description for the "Allowed HTML tags" field.
    $allowedHTMLDescription: null,
    // The user-entered tag list of $allowedHTMLFormItem.
    userTags: null,
    // The auto-created tag list thus far added.
    autoTags: null,
    // Track which new features have been added to the text editor.
    newFeatures: {},
    attach: function (context, settings) {
      var that = this;
      $(context).find('[name="filters[filter_html][settings][allowed_html]"]').once('filter-filter_html-updating').each(function () {
        that.$allowedHTMLFormItem = $(this);
        that.$allowedHTMLDescription = that.$allowedHTMLFormItem.closest('.form-item').find('.description');
        that.userTags = that._parseSetting(this.value);
        // Update the new allowed tags based on added text editor features.
        $(document)
          .on('drupalEditorFeatureAdded', function (e, feature) {
            that.newFeatures[feature.name] = feature.rules;
            that._updateAllowedTags();
          })
          .on('drupalEditorFeatureModified', function (e, feature) {
            if (that.newFeatures.hasOwnProperty(feature.name)) {
              that.newFeatures[feature.name] = feature.rules;
              that._updateAllowedTags();
            }
          })
          .on('drupalEditorFeatureRemoved', function (e, feature) {
            if (that.newFeatures.hasOwnProperty(feature.name)) {
              delete that.newFeatures[feature.name];
              that._updateAllowedTags();
            }
          });

        // When the allowed tags list is manually changed, update userTags.
        that.$allowedHTMLFormItem.on('change.updateUserTags', function () {
          that.userTags = _.difference(that._parseSetting(this.value), that.autoTags);
    },

    /**
     * Updates the "Allowed HTML tags" setting and shows an informative message.
     */
    _updateAllowedTags: function () {
      // Update the list of auto-created tags.
      this.autoTags = this._calculateAutoAllowedTags(this.userTags, this.newFeatures);

      // Remove any previous auto-created tag message.
      this.$allowedHTMLDescription.find('.editor-update-message').remove();

      // If any auto-created tags: insert message and update form item.
      if (this.autoTags.length > 0) {
        this.$allowedHTMLDescription.append(Drupal.theme('filterFilterHTMLUpdateMessage', this.autoTags));
        this.$allowedHTMLFormItem.val(this._generateSetting(this.userTags) + ' ' + this._generateSetting(this.autoTags));
      }
      // Restore to original state.
      else {
        this.$allowedHTMLFormItem.val(this._generateSetting(this.userTags));
      }
    },

    /**
     * Calculates which HTML tags the added text editor buttons need to work.
     *
     * The filter_html filter is only concerned with the required tags, not with
     * any properties, nor with each feature's "allowed" tags.
     *
     *   The list of user-defined allowed tags.
     * @param {object} newFeatures
     *   A list of {@link Drupal.EditorFeature} objects' rules, keyed by
     *   their name.
     *   A list of new allowed tags.
     */
    _calculateAutoAllowedTags: function (userAllowedTags, newFeatures) {
      return _
        .chain(newFeatures)
        // Reduce multiple features' rules.
        .reduce(function (memo, featureRules) {
          // Reduce a single features' rules' required tags.
          return _.union(memo, _.reduce(featureRules, function (memo, featureRule) {
            return _.union(memo, featureRule.required.tags);
          }, []));
        }, [])
        // All new features' required tags are "new allowed tags", except
        // for those that are already allowed in the original allowed tags.
        .difference(userAllowedTags)
        .value();
    },

    /**
     * Parses the value of this.$allowedHTMLFormItem.
     *
     *   The string representation of the setting. e.g. "<p> <br> <a>"
     *
     *   The array representation of the setting. e.g. ['p', 'br', 'a']
     */
    _parseSetting: function (setting) {
      return setting.length ? setting.substring(1, setting.length - 1).split('> <') : [];
    },

    /**
     * Generates the value of this.$allowedHTMLFormItem.
     *
     *   The array representation of the setting. e.g. ['p', 'br', 'a']
     *
     *   The string representation of the setting. e.g. "<p> <br> <a>"
     */
    _generateSetting: function (tags) {
      return tags.length ? '<' + tags.join('> <') + '>' : '';
   * Theme function for the filter_html update message.
   *   An array of the new tags that are to be allowed.
  Drupal.theme.filterFilterHTMLUpdateMessage = function (tags) {
    var html = '';
    var tagList = '<' + tags.join('> <') + '>';
    html += '<p class="editor-update-message">';
    html += Drupal.t('Based on the text editor configuration, these tags have automatically been added: <strong>@tag-list</strong>.', {'@tag-list': tagList});