(function($) {

/**
 * Initialize editor instances.
 *
 * @see Drupal.wysiwyg.editor.init.ckeditor()
 */
Drupal.wysiwyg.editor.init.tinymce = function(settings, pluginInfo) {
  // Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
  var $drupalToolbars = $('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.document : document);
  tinyMCE.onAddEditor.add(function (mgr, ed) {
    if (ed.id == 'mce_fullscreen') {
      $drupalToolbars.hide();
    }
  });
  tinyMCE.onRemoveEditor.add(function (mgr, ed) {
    if (ed.id == 'mce_fullscreen') {
      $drupalToolbars.show();
    }
    else {
      // Free our reference to the private instance to not risk memory leaks.
      delete ed._drupalWysiwygInstance;
    }
  });
  // Register new plugins.
  Drupal.wysiwyg.editor.update.tinymce(settings, pluginInfo);
};

/**
 * Update the editor library when new settings are available.
 *
 * @see Drupal.wysiwyg.editor.update.ckeditor()
 */
Drupal.wysiwyg.editor.update.tinymce = function(settings, pluginInfo) {
  // Load native external plugins.
  // Array syntax required; 'native' is a predefined token in JavaScript.
  for (var plugin in pluginInfo['native']) {
    if (!(plugin in tinymce.PluginManager.lookup || plugin in tinymce.PluginManager.urls)) {
      tinymce.PluginManager.load(plugin, pluginInfo['native'][plugin]);
    }
  }
  // Load Drupal plugins.
  for (var plugin in pluginInfo.drupal) {
    if (!(plugin in tinymce.PluginManager.lookup)) {
      Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, pluginInfo.drupal[plugin]);
    }
  }
};

/**
 * Attach this editor to a target element.
 *
 * See Drupal.wysiwyg.editor.attach.none() for a full description of this hook.
 */
Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
  // Configure editor settings for this input format.
  var ed = new tinymce.Editor(params.field, settings);
  ed._drupalWysiwygInstance = this;
  // Reset active instance id on any event.
  ed.onEvent.add(function(ed, e) {
    Drupal.wysiwyg.activeId = ed.id;
  });
  // Indicate that the DOM has been loaded (in case of Ajax).
  tinymce.dom.Event.domLoaded = true;
  // Make toolbar buttons wrappable (required for IE).
  ed.onPostRender.add(function (ed) {
    var $toolbar = $('<div class="wysiwygToolbar"></div>');
    $('#' + ed.editorContainer + ' table.mceToolbar > tbody > tr > td').each(function () {
      $('<div></div>').addClass(this.className).append($(this).children()).appendTo($toolbar);
    });
    $('#' + ed.editorContainer + ' table.mceLayout td.mceToolbar').append($toolbar);
    $('#' + ed.editorContainer + ' table.mceToolbar').remove();
  });

  // Remove TinyMCE's internal mceItem class, which was incorrectly added to
  // submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
  // for placeholder elements. If preemptively set, the class prevents (native)
  // editor plugins from gaining an active state, so we have to manually remove
  // it prior to attaching the editor. This is done on the client-side instead
  // of the server-side, as Wysiwyg has no way to figure out where content is
  // stored, and the class only affects editing.
  var $field = $('#' + params.field);
  $field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));

  // Attach editor.
  ed.render();
  if (tinymce.minorVersion == '5.7') {
    // Work around a TinyMCE bug hiding new instances when switching to them.
    // @see http://www.tinymce.com/develop/bugtracker_view.php?id=5510
    setTimeout(function () {
      tinymce.DOM.show(ed.editorContainer);
    }, 1);
  }
};

/**
 * Detach a single editor instance.
 *
 * See Drupal.wysiwyg.editor.detach.none() for a full description of this hook.
 */
Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
  var instance = tinyMCE.get(params.field);
  if (!instance) {
    return;
  }
  instance.save();
  if (trigger !== 'serialize') {
    // The onRemove event fires before this returns.
    instance.remove();
  }
};

Drupal.wysiwyg.editor.instance.tinymce = {
  addPlugin: function(plugin, pluginSettings) {
    if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
      return;
    }
    tinymce.create('tinymce.plugins.drupal_' + plugin, {
      /**
       * Initialize the plugin, executed after the plugin has been created.
       *
       * @param ed
       *   The tinymce.Editor instance the plugin is initialized in.
       * @param url
       *   The absolute URL of the plugin location.
       */
      init: function(ed, url) {
        // Register an editor command for this plugin, invoked by the plugin's button.
        ed.addCommand('drupal_' + plugin, function() {
          if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
            var data = { format: 'html', node: ed.selection.getNode(), content: ed.selection.getContent() };
            // TinyMCE creates a completely new instance for fullscreen mode.
            var instanceId = ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id;
            Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, instanceId);
          }
        });

        // Register the plugin button.
        ed.addButton('drupal_' + plugin, {
          title : pluginSettings.title,
          cmd : 'drupal_' + plugin,
          image : pluginSettings.icon
        });

        // Load custom CSS for editor contents on startup.
        ed.onInit.add(function() {
          if (pluginSettings.css) {
            ed.dom.loadCSS(pluginSettings.css);
          }
        });

        // Attach: Replace plain text with HTML representations.
        ed.onBeforeSetContent.add(function(ed, data) {
          var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
          if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
            data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, editorId);
            data.content = ed._drupalWysiwygInstance.prepareContent(data.content);
          }
        });

        // Detach: Replace HTML representations with plain text.
        ed.onGetContent.add(function(ed, data) {
          var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
          if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
            data.content = Drupal.wysiwyg.plugins[plugin].detach(data.content, pluginSettings, editorId);
          }
        });

        // isNode: Return whether the plugin button should be enabled for the
        // current selection.
        ed.onNodeChange.add(function(ed, command, node) {
          if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
            command.setActive(plugin, Drupal.wysiwyg.plugins[plugin].isNode(node));
          }
        });
      },

      /**
       * Return information about the plugin as a name/value array.
       */
      getInfo: function() {
        return {
          longname: pluginSettings.title
        };
      }
    });

    // Register plugin.
    tinymce.PluginManager.add('drupal_' + plugin, tinymce.plugins['drupal_' + plugin]);
  },

  openDialog: function(dialog, params) {
    var instanceId = this.getInstanceId();
    var editor = tinyMCE.get(instanceId);
    editor.windowManager.open({
      file: dialog.url + '/' + instanceId,
      width: dialog.width,
      height: dialog.height,
      inline: 1
    }, params);
  },

  closeDialog: function(dialog) {
    var editor = tinyMCE.get(this.getInstanceId());
    editor.windowManager.close(dialog);
  },

  prepareContent: function(content) {
    // Certain content elements need to have additional DOM properties applied
    // to prevent this editor from highlighting an internal button in addition
    // to the button of a Drupal plugin.
    var specialProperties = {
      img: { 'class': 'mceItem' }
    };
    var $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
    // Find all placeholder/replacement content of Drupal plugins.
    $content.find('.drupal-content').each(function() {
      // Recursively process DOM elements below this element to apply special
      // properties.
      var $drupalContent = $(this);
      $.each(specialProperties, function(element, properties) {
        $drupalContent.find(element).andSelf().each(function() {
          for (var property in properties) {
            if (property == 'class') {
              $(this).addClass(properties[property]);
            }
            else {
              $(this).attr(property, properties[property]);
            }
          }
        });
      });
    });
    return $content.html();
  },

  insert: function(content) {
    content = this.prepareContent(content);
    tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceInsertContent', false, content);
  },

  setContent: function (content) {
    content = this.prepareContent(content);
    tinyMCE.execInstanceCommand(this.getInstanceId(), 'mceSetContent', false, content);
  },

  getContent: function () {
    return tinyMCE.get(this.getInstanceId()).getContent();
  },

  isFullscreen: function() {
    // TinyMCE creates a completely new instance for fullscreen mode.
    return tinyMCE.activeEditor.id == 'mce_fullscreen' && tinyMCE.activeEditor.getParam('fullscreen_editor_id') == this.field;
  },

  getInstanceId: function () {
    return this.isFullscreen() ? 'mce_fullscreen' : this.field;
  }
};

})(jQuery);
