diff --git a/features.admin.inc b/features.admin.inc
index 4e12576..4d3bbf8 100644
--- a/features.admin.inc
+++ b/features.admin.inc
@@ -66,6 +66,74 @@ function features_settings_form($form, $form_state) {
'#description' => t('If you have a large site with many features, you may experience lag on full cache clear. If disabled, features will rebuild only when viewing the features list or saving the modules list.'),
);
+ $providers = features_diff_formatter_providers();
+ $form['diff'] = array(
+ '#title' => t('Diff formatter'),
+ '#type' => 'fieldset',
+ );
+
+ $form['diff']['diff'] = array(
+ '#type' => 'fieldset',
+ '#title' => 'Diff',
+ '#collapsible' => TRUE,
+ '#collapsed' => FALSE,
+ );
+ if (isset($providers['diff'])) {
+ $name = 'features_diff_formatter_provider__diff__context_lines';
+ $form['diff']['diff'][$name] = array(
+ '#type' => 'textfield',
+ '#title' => ('Lines'),
+ '#size' => 3,
+ '#default_value' => variable_get($name, 3),
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#attributes' => array(
+ 'class' => array('align-right'),
+ ),
+ );
+ }
+ else {
+ $form['diff']['diff']['install_guide'] = array(
+ '#markup' => '
' . t('Install the Diff module') . '
',
+ );
+ }
+
+ $form['diff']['jsdifflib'] = array(
+ '#type' => 'fieldset',
+ '#title' => 'jsdifflib',
+ '#collapsible' => TRUE,
+ '#collapsed' => FALSE,
+ );
+ if (isset($providers['jsdifflib'])) {
+ $name = 'features_diff_formatter_provider__jsdifflib__context_lines';
+ $form['diff']['jsdifflib'][$name] = array(
+ '#type' => 'textfield',
+ '#title' => ('Lines'),
+ '#size' => 3,
+ '#default_value' => variable_get($name, 3),
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#attributes' => array(
+ 'class' => array('align-right'),
+ ),
+ );
+
+ $name = 'features_diff_formatter_provider__jsdifflib__view_type';
+ $form['diff']['jsdifflib'][$name] = array(
+ '#type' => 'select',
+ '#title' => ('View type'),
+ '#required' => TRUE,
+ '#default_value' => variable_get($name, 'side_by_side'),
+ '#options' => array(
+ 'inline' => t('Inline'),
+ 'side_by_side' => t('Side by side'),
+ ),
+ );
+ }
+ else {
+ $form['diff']['jsdifflib']['install_guide'] = array(
+ '#markup' => '' . t('Extract the jsdifflib library to the libraries directory and rename it from jsdifflib-master to jsdifflib.') . '
',
+ );
+ }
+
return system_settings_form($form);
}
@@ -1332,39 +1400,333 @@ function features_cleanup_form($form, $form_state, $cache_clear = FALSE) {
* Themed display of what is different.
*/
function features_feature_diff($feature, $component = NULL) {
- drupal_add_css(drupal_get_path('module', 'features') . '/features.css');
module_load_include('inc', 'features', 'features.export');
+
drupal_set_title($feature->info['name']);
- $overrides = features_detect_overrides($feature);
+ $return = array();
- $output = '';
+ $overrides = features_detect_overrides($feature);
if (!empty($overrides)) {
+ $providers = features_diff_formatter_providers();
+ $provider = features_diff_formatter_default_provider();
+
+ // Add the diff formatter settings form if we have more than one formatter
+ // or have any settings of the single one.
+ if (!(count($providers) == 1 && !isset($provider['settings']))) {
+ $return['features_diff_formatter_settings_form'] = drupal_get_form('features_diff_formatter_settings_form');
+ $return['features_diff_formatter_settings_form']['#weight'] = 0;
+ }
+
// Filter overrides down to specified component.
if (isset($component) && isset($overrides[$component])) {
$overrides = array($component => $overrides[$component]);
}
- module_load_include('inc', 'diff', 'diff.engine');
- $formatter = new DrupalDiffFormatter();
+ if ($provider['name'] == 'diff') {
+ $return['feature_diff'] = features_feature_diff_diff($overrides, $provider);
+ }
+ elseif ($provider['name'] == 'jsdifflib') {
+ $return['feature_diff'] = features_feature_diff_jsdifflib($overrides, $provider);
+ }
+
+ if (isset($return['feature_diff'])) {
+ $return['feature_diff']['#weight'] = 2;
+ $return['feature_diff']['#formatter_provider'] = $provider;
+ }
+ }
+
+ return $return;
+}
+
+function features_feature_diff_diff($overrides, $provider) {
+ module_load_include('inc', 'diff', 'diff.engine');
+
+ $formatter = new DrupalDiffFormatter();
+ $formatter->leading_context_lines
+ = $formatter->trailing_context_lines
+ = $provider['settings']['context_lines'];
+
+ $path = drupal_get_path('module', 'features');
+ $return = array(
+ '#prefix' => '',
+ '#suffix' => '
',
+ '#attached' => array(
+ 'css' => array(
+ "$path/features.css",
+ ),
+ 'js' => array(
+ "$path/js/features.diff-formatter-diff.js",
+ ),
+ 'library' => array(
+ array('system', 'ui.tabs'),
+ ),
+ ),
+ );
+
+ $return['formatted'] = array(
+ '#prefix' => '',
+ '#suffix' => '
',
+ );
+
+ if (count($overrides) > 1) {
+ $return['formatted']['navigator'] = array(
+ '#type' =>'markup',
+ '#prefix' =>'',
+ '#markup' => '',
+ '#suffix' =>'
',
+ );
- $rows = array();
- foreach ($overrides as $component => $items) {
- $rows[] = array(array('data' => $component, 'colspan' => 4, 'header' => TRUE));
- $diff = new Diff(explode("\n", $items['default']), explode("\n", $items['normal']));
- $rows = array_merge($rows, $formatter->format($diff));
+ foreach (array_keys($overrides) as $component) {
+ $return['formatted']['navigator']['#markup'] .= format_string(
+ '@component',
+ array('@component' => $component)
+ );
}
- $header = array(
+ }
+
+ $table = array(
+ 'caption' => NULL,
+ 'sticky' => FALSE,
+ 'header' => array(
array('data' => t('Default'), 'colspan' => 2),
array('data' => t('Overrides'), 'colspan' => 2),
+ ),
+ 'rows' => array(),
+ 'attributes' => array('class' => array('diff', 'diff-indent', 'features-diff')),
+ );
+
+ foreach ($overrides as $component => $items) {
+ $return['#rows'][] = array(
+ array(
+ 'data' => $component,
+ 'colspan' => 4,
+ 'header' => TRUE,
+ ),
+ );
+
+ $return['formatted'][$component] = array(
+ '#prefix' => format_string('', array('@component' => $component)),
+ '#suffix' => '
',
+ );
+
+ $return['formatted'][$component]['title'] = array(
+ '#markup' => '' . $component . '
',
+ );
+
+ $diff = new Diff(explode("\n", $items['default']), explode("\n", $items['normal']));
+ $table['rows'] = $formatter->format($diff);
+ $return['formatted'][$component]['diff'] = array(
+ '#markup' => theme('table', $table),
);
- $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('class' => array('diff', 'features-diff'))));
}
- else {
- $output = "" . t('No changes have been made to this feature.') . "
";
+
+ return $return;
+}
+
+function features_feature_diff_jsdifflib($overrides, $provider) {
+ $jsdifflib_path = libraries_get_path('jsdifflib');
+ $path = drupal_get_path('module', 'features');
+ $return = array(
+ '#attached' => array(
+ 'css' => array(
+ "$jsdifflib_path/diffview.css",
+ ),
+ 'js' => array(
+ "$jsdifflib_path/difflib.js",
+ "$jsdifflib_path/diffview.js",
+ "$path/js/features.diff-formatter-jsdifflib.js",
+ ),
+ 'library' => array(
+ array('system', 'ui.tabs'),
+ ),
+ ),
+ '#prefix' => '',
+ '#suffix' => '
',
+ );
+
+ $return['components'] = array(
+ '#prefix' => '',
+ '#suffix' => '
',
+ );
+
+ $return['formatted'] = array(
+ '#prefix' => '',
+ '#suffix' => '
',
+ );
+
+ if (count($overrides) > 1) {
+ $return['formatted']['navigator'] = array(
+ '#type' =>'markup',
+ '#prefix' =>'',
+ '#markup' => '',
+ '#suffix' =>'
',
+ );
+
+ foreach (array_keys($overrides) as $component) {
+ $return['formatted']['navigator']['#markup'] .= format_string(
+ '@component',
+ array('@component' => $component)
+ );
+ }
+ }
+
+ foreach ($overrides as $component => $item) {
+ $attributes = array(
+ 'title' => $component,
+ 'class' => array(
+ 'component',
+ 'component-' . drupal_html_class($component),
+ ),
+ );
+ $return['components'][$component] = array(
+ '#prefix' => '',
+ '#suffix' => '
'
+ );
+
+ $return['components'][$component]['default'] = array(
+ '#markup' => '' . check_plain($item['default']) . '
',
+ );
+
+ $return['components'][$component]['normal'] = array(
+ '#markup' => '' . check_plain($item['normal']) . '
',
+ );
+
+ $return['formatted'][$component] = array(
+ '#type' => 'markup',
+ '#markup' => format_string(
+ '@component
',
+ array('@component' => $component)
+ ),
+ );
+ }
+
+ return $return;
+}
+
+/**
+ * Form builder function for 'lines of context' above diff output.
+ */
+function features_diff_formatter_settings_form($form, &$form_state) {
+ $form['#attributes']['class'][] = 'features';
+ $form['#attributes']['class'][] = 'features-diff-formatter-settings-form';
+ $form['#attributes']['class'][] = 'clearfix';
+ $form['#attached']['css'][] = drupal_get_path('module', 'features') . '/features.css';
+
+ $providers = features_diff_formatter_providers();
+
+ $default_provider = features_diff_formatter_default_provider();
+
+ $form['provider'] = array(
+ '#type' => 'radios',
+ '#required' => TRUE,
+ '#title' => t('Provider'),
+ '#title_display' => 'none',
+ '#default_value' => $default_provider['name'],
+ '#options' => features_diff_formatter_providers_options(),
+ '#attributes' => array(
+ 'class' => array('provider-selector'),
+ ),
+ );
+
+ if (count($form['provider']['#options']) == 1) {
+ $form['provider']['#attributes']['class'][] = 'element-invisible';
+ }
+
+ $form['providers'] = array(
+ '#tree' => TRUE,
+ );
+
+ if (isset($form['provider']['#options']['diff'])) {
+ $form['providers']['diff'] = array(
+ '#type' => 'container',
+ '#tree' => TRUE,
+ '#states' => array(
+ 'invisible' => array(
+ ':input[name="provider"]' => array('!value' => 'diff'),
+ ),
+ ),
+ '#attributes' => array(
+ 'class' => array('provider', 'provider-diff'),
+ ),
+ );
+
+ $form['providers']['diff']['context_lines'] = array(
+ '#type' => 'textfield',
+ '#title' => ('Lines'),
+ '#size' => 3,
+ '#default_value' => $providers['diff']['settings']['context_lines'],
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#attributes' => array(
+ 'class' => array('align-right'),
+ ),
+ );
+ }
+
+ if (isset($form['provider']['#options']['jsdifflib'])) {
+ $form['providers']['jsdifflib'] = array(
+ '#type' => 'container',
+ '#tree' => TRUE,
+ '#states' => array(
+ 'invisible' => array(
+ ':input[name="provider"]' => array('!value' => 'jsdifflib'),
+ ),
+ ),
+ '#attributes' => array(
+ 'class' => array('provider', 'provider-jsdifflib'),
+ ),
+ );
+
+ $form['providers']['jsdifflib']['context_lines'] = array(
+ '#type' => 'textfield',
+ '#title' => ('Lines'),
+ '#size' => 3,
+ '#default_value' => $providers['jsdifflib']['settings']['context_lines'],
+ '#element_validate' => array('element_validate_integer_positive'),
+ '#attributes' => array(
+ 'class' => array('align-right'),
+ ),
+ );
+
+ $form['providers']['jsdifflib']['view_type'] = array(
+ '#type' => 'select',
+ '#title' => ('View type'),
+ '#title_display' => 'none',
+ '#required' => TRUE,
+ '#default_value' => $providers['jsdifflib']['settings']['view_type'],
+ '#options' => array(
+ 'inline' => t('Inline'),
+ 'side_by_side' => t('Side by side'),
+ ),
+ );
+ }
+
+ $form['actions'] = array(
+ '#type' => 'actions',
+ 'submit' => array(
+ '#type' => 'submit',
+ '#value' => t('Reload page'),
+ ),
+ );
+
+ return $form;
+}
+
+/**
+ * Submit function for features_diff_formatter_settings_form.
+ */
+function features_diff_formatter_settings_form_submit($form, &$form_state) {
+ $provider_name = $form_state['values']['provider'];
+ $_SESSION['features_diff_formatter_provider'] = $provider_name;
+
+ $provider = features_diff_formatter_provider_load($provider_name);
+ if (isset($provider['settings'])) {
+ foreach (array_keys($provider['settings']) as $key) {
+ if (isset($form_state['values']['providers'][$provider_name][$key])) {
+ $_SESSION["features_diff_formatter_provider__{$provider_name}__{$key}"] = $form_state['values']['providers'][$provider_name][$key];
+ }
+ }
}
- $output = array('page' => array('#markup' => "{$output}
"));
- return $output;
}
/**
diff --git a/features.css b/features.css
index 16c7f72..5556421 100644
--- a/features.css
+++ b/features.css
@@ -69,6 +69,24 @@ div.features-empty {
color:#999;
}
+.features-diff-formatter-settings-form .provider-selector,
+.features-diff-formatter-settings-form .provider,
+.features-diff-formatter-settings-form .form-type-textfield,
+.features-diff-formatter-settings-form .form-type-textfield label,
+.features-diff-formatter-settings-form .form-type-textfield .form-text,
+.features-diff-formatter-settings-form .form-type-select {
+ float: left;
+}
+
+.features-diff-formatter-settings-form .provider-selector,
+.features-diff-formatter-settings-form .provider .form-item {
+ margin-right: 2em;
+}
+
+.features .align-right {
+ text-align: right;
+}
+
form div.buttons {
text-align:center;
}
@@ -277,18 +295,59 @@ span.admin-conflict {
color:#c30;
}
-table.features-diff td.diff-addedline,
+table.features-diff tbody td:nth-child(2):nth-last-child(1),
+table.features-diff tbody td:nth-child(1),
+table.features-diff tbody td:nth-child(3) {
+ background: #eed;
+ border: 1px solid #bbc;
+ padding: .3em .5em .1em 2em;
+ vertical-align: top;
+}
+
+table.diff tbody tr:nth-child(odd),
+table.features-diff tr.odd {
+ background-color: #ffffff;
+}
+
+table.diff tbody tr:nth-child(even),
+table.features-diff tr.even {
+ background-color: #fafafa;
+}
+
+table.features-diff tr.odd td.diff-deletedline {
+ background-color: #ee9999;
+}
+
+table.features-diff tr.even td.diff-deletedline {
+ background-color: #e89999;
+}
+
+table.features-diff tr.odd td.diff-addedline {
+ background-color: #99ee99;
+}
+
+table.features-diff tr.even td.diff-addedline {
+ background-color: #99e899;
+}
+
+table.features-diff .diffchange {
+ font-weight: bold;
+}
+
span.features-component-list .features-detected {
color:#68a;
background:#def;
}
-table.features-diff td.diff-deletedline,
span.features-component-list .features-dependency {
color:#999;
background:#f8f8f8;
}
+table.diff-indent td div {
+ white-space: pre;
+}
+
/**
* Features diff.
*/
@@ -304,7 +363,7 @@ table.features-diff td.diff-deletedline,
table.features-diff td.diff-addedline,
table.features-diff td.diff-context {
width:50%;
- font-family:'Andale Mono',monospace;
+ font-family: 'Andale Mono', 'DejaVu Sans Mono', 'Courier New', monospace;
}
/**
@@ -563,4 +622,4 @@ input.form-submit.features-refresh-button {
fieldset.features-export-component .fieldset-title .component-count {
font-size: 12px;
font-weight: bold;
-}
\ No newline at end of file
+}
diff --git a/features.install b/features.install
index 762a054..117b238 100644
--- a/features.install
+++ b/features.install
@@ -25,6 +25,14 @@ function features_uninstall() {
variable_del('features_default_export_path');
variable_del('features_semaphore');
variable_del('features_ignored_orphans');
+ foreach (features_diff_formatter_providers() as $provider) {
+ if (isset($provider['settings'])) {
+ foreach (array_keys($provider['settings']) as $key) {
+ variable_del("features_diff_formatter_provider__{$provider['name']}__{$key}");
+ }
+ }
+ }
+
if (db_table_exists('menu_custom')) {
db_delete('menu_custom')
->condition('menu_name', 'features')
diff --git a/features.module b/features.module
index ba5b9fe..69fb4ed 100644
--- a/features.module
+++ b/features.module
@@ -154,7 +154,7 @@ function features_menu() {
'file' => "features.admin.inc",
'weight' => 11,
);
- if (module_exists('diff')) {
+ if (features_diff_formatter_providers()) {
$items['admin/structure/features/%feature/diff'] = array(
'title' => 'Review overrides',
'description' => 'Compare default and current feature.',
@@ -1109,3 +1109,80 @@ function features_get_deprecated($components = array()) {
}
return $deprecated;
}
+
+function features_diff_formatter_provider_load($provider_name) {
+ $providers = features_diff_formatter_providers();
+
+ return isset($providers[$provider_name]) ? $providers[$provider_name] : NULL;
+}
+
+/**
+ * Collect the available diff providers.
+ *
+ * @return array
+ * Array of diff provider properties.
+ * - name: Machine name.
+ * - title: Human name.
+ */
+function features_diff_formatter_providers() {
+ $providers = array();
+
+ if (module_exists('diff')) {
+ $providers['diff'] = array(
+ 'name' => 'diff',
+ 'title' => t('Default'),
+ 'settings' => array(
+ 'context_lines' => 3,
+ ),
+ );
+ }
+
+ if (module_exists('libraries') && libraries_get_path('jsdifflib')) {
+ $providers['jsdifflib'] = array(
+ 'name' => 'jsdifflib',
+ 'title' => t('JS Diff lib'),
+ 'settings' => array(
+ 'context_lines' => 3,
+ 'view_type' => 1,
+ ),
+ );
+ }
+
+ foreach ($providers as &$provider) {
+ if (isset($provider['settings'])) {
+ foreach ($provider['settings'] as $key => $value) {
+ $name = "features_diff_formatter_provider__{$provider['name']}__{$key}";
+ $provider['settings'][$key] = (isset($_SESSION[$name])) ? $_SESSION[$name] : variable_get($name, $value);
+ }
+ }
+ }
+
+ return $providers;
+}
+
+function features_diff_formatter_providers_options() {
+ $options = array();
+
+ foreach (features_diff_formatter_providers() as $provider) {
+ $options[$provider['name']] = $provider['title'];
+ }
+
+ return $options;
+}
+
+function features_diff_formatter_default_provider() {
+ $providers = features_diff_formatter_providers();
+ $name = 'features_diff_formatter_provider';
+ if (isset($_SESSION[$name]) && isset($providers[$_SESSION[$name]])) {
+ return $providers[$_SESSION[$name]];
+ }
+ else {
+ $default_provider = variable_get($name, NULL);
+ if (isset($providers[$default_provider])) {
+ return $providers[$default_provider];
+ }
+ else {
+ return reset($providers);
+ }
+ }
+}
diff --git a/js/features.diff-formatter-diff.js b/js/features.diff-formatter-diff.js
new file mode 100644
index 0000000..facdfd4
--- /dev/null
+++ b/js/features.diff-formatter-diff.js
@@ -0,0 +1,27 @@
+/**
+ * @file
+ * Documentation missing.
+ */
+
+(function ($) {
+ 'use strict';
+ Drupal.behaviors.featuresDiffFormatterDiff = {
+ attach: function (context, settings) {
+ var $wrapper = $('.features-diff-formatter-provider-diff:not(.features-diff-formatter-diff-processed)', context);
+ if ($wrapper.length) {
+ $wrapper.addClass('features-diff-formatter-diff-processed');
+ if ($('> .formatted > .diff-navigator', $wrapper).length) {
+ $('> .formatted', $wrapper).tabs();
+ Drupal.featuresDiffFormatterDiff.hideTitles($wrapper);
+ }
+ }
+ }
+ };
+
+ Drupal.featuresDiffFormatterDiff = Drupal.featuresDiffFormatterDiff || {};
+
+ Drupal.featuresDiffFormatterDiff.hideTitles = function ($wrapper) {
+ $('h3', $wrapper).addClass('js-hide');
+ };
+
+}(jQuery));
diff --git a/js/features.diff-formatter-jsdifflib.js b/js/features.diff-formatter-jsdifflib.js
new file mode 100644
index 0000000..6623636
--- /dev/null
+++ b/js/features.diff-formatter-jsdifflib.js
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * Diff formatter by jsdifflib.
+ */
+
+(function ($) {
+ 'use strict';
+ Drupal.behaviors.featuresDiffFormatterJsdifflib = {
+ attach: function (context, settings) {
+ if (difflib === undefined || diffview === undefined) {
+ return;
+ }
+
+ if (Drupal.featuresDiffFormatterJsdifflib.settingsContainer === null) {
+ var $settingsContainer = $('form.features-diff-formatter-settings-form .provider-jsdifflib:not(.features-diff-formatter-jsdifflib-processed)');
+ if ($settingsContainer.length === 1) {
+ Drupal.featuresDiffFormatterJsdifflib.settingsContainer = $settingsContainer;
+ $settingsContainer.addClass('features-diff-formatter-jsdifflib-processed');
+ $(':input', $settingsContainer).change(Drupal.featuresDiffFormatterJsdifflib.settingsOnChange);
+ }
+ }
+
+ if (Drupal.featuresDiffFormatterJsdifflib.settings === null) {
+ Drupal.featuresDiffFormatterJsdifflib.settingsUpdate();
+ }
+
+ var $wrapper = $('.features-diff-formatter-provider-jsdifflib:not(.features-diff-formatter-jsdifflib-processed)', context);
+ if ($wrapper.length) {
+ $wrapper.addClass('features-diff-formatter-jsdifflib-processed');
+ Drupal.featuresDiffFormatterJsdifflib.formattedUpdate($wrapper);
+
+ if ($('> .formatted > .diff-navigator', $wrapper).length) {
+ $('> .formatted', $wrapper).tabs();
+ Drupal.featuresDiffFormatterJsdifflib.hideTitles($wrapper);
+ }
+ }
+ }
+ };
+
+ Drupal.featuresDiffFormatterJsdifflib = Drupal.featuresDiffFormatterJsdifflib || {};
+
+ Drupal.featuresDiffFormatterJsdifflib.settings = null;
+
+ Drupal.featuresDiffFormatterJsdifflib.settingsContainer = null;
+
+ Drupal.featuresDiffFormatterJsdifflib.settingsUpdate = function () {
+ var
+ settings = {
+ baseTextName: Drupal.t('Default', {}, {}),
+ newTextName: Drupal.t('Override', {}, {}),
+ contextSize: 3,
+ viewType: 0
+ },
+ $container = Drupal.featuresDiffFormatterJsdifflib.settingsContainer;
+
+ if ($container !== null) {
+ var
+ $contextSize = $(':input[name$="[context_lines]"]', $container),
+ $viewType = $(':input[name$="[view_type]"]', $container);
+
+ settings.contextSize = parseInt($contextSize.val(), 10);
+ if (isNaN(settings.contextSize) || settings.contextSize < 1) {
+ settings.contextSize = 3;
+ $contextSize.val(settings.contextSize);
+ }
+
+ settings.viewType = $viewType.val() === 'inline' ? 1 : 0;
+ }
+
+ Drupal.featuresDiffFormatterJsdifflib.settings = settings;
+ };
+
+ Drupal.featuresDiffFormatterJsdifflib.settingsOnChange = function () {
+ Drupal.featuresDiffFormatterJsdifflib.settingsUpdate();
+ Drupal.featuresDiffFormatterJsdifflib.formattedUpdate($('.features-diff-formatter-provider-jsdifflib'));
+ };
+
+ Drupal.featuresDiffFormatterJsdifflib.formattedUpdate = function ($containers) {
+ var options = Drupal.featuresDiffFormatterJsdifflib.settings;
+
+ $containers.each(function () {
+ var
+ $container = $(this),
+ $rawComponents = $('> .components > .component', $container),
+ $formatted = $('> .formatted', $container);
+
+ $formatted.find('> .diff-component > table.diff').remove();
+
+ $rawComponents.each(function () {
+ var
+ componentName = $(this).attr('title'),
+ textDefaultDecoded = $('').html($('> .diff-default', $(this)).html()).text(),
+ textOverrideDecoded = $('').html($('> .diff-normal', $(this)).html()).text();
+
+ options.baseTextLines = difflib.stringAsLines(textDefaultDecoded);
+ options.newTextLines = difflib.stringAsLines(textOverrideDecoded);
+
+ var sm = new difflib.SequenceMatcher(
+ options.baseTextLines,
+ options.newTextLines
+ );
+ options.opcodes = sm.get_opcodes();
+
+ $('> #diff-component-' + componentName, $formatted)
+ .append(diffview.buildView(options));
+ });
+ });
+ };
+
+ Drupal.featuresDiffFormatterJsdifflib.hideTitles = function ($wrapper) {
+ $('h3', $wrapper).addClass('js-hide');
+ };
+
+}(jQuery));