<commit_before>/*
 * Copyright <NAME> 2012
 * MIT License : http://adampritchard.mit-license.org/
 */

/*
 * This file is the heart of Markdown Here. It decides whether we're rendering
 * or revert; whether we're doing a selection or the whole thing; and actually
 * does it (calling out for the final render).
 */

;(function() {

// For debugging purposes. An external service is required to log with Firefox.
var mylog = function() {};

// Used to create unique IDs for each Markdown Here wrapper.
var markdownHereWrapperIdCounter = 1;

// Finds and returns the page element that currently has focus. Drills down into
// iframes if necessary.
function findFocusedElem(document) {
  var focusedElem = document.activeElement;

  // If the focus is within an iframe, we'll have to drill down to get to the
  // actual element.
  while (focusedElem && focusedElem.contentDocument) {
    focusedElem = focusedElem.contentDocument.activeElement;
  }

  return focusedElem;
}

// Returns true if the given element can be properly rendered (i.e., if it's 
// a rich-edit compose element).
function elementCanBeRendered(elem) {
  // See here for more info about what we're checking:
  // http://stackoverflow.com/a/3333679/729729
  return elem.contentEditable === true || elem.contentEditable === 'true'
         || elem.contenteditable === true || elem.contenteditable === 'true'
         || (elem.ownerDocument && elem.ownerDocument.designMode === 'on');  
}

// Get the currectly selected range. If there is no selected range (i.e., it is
// collapsed), then contents of the currently focused element will be selected.
// Returns null if no range is selected nor can be selected.
function getOperationalRange(focusedElem) {
  var selection, range, sig;

  selection = focusedElem.ownerDocument.getSelection();
  if (selection.rangeCount < 1) {
    return null;
  }

  range = selection.getRangeAt(0);

  if (range.collapsed) {
    // If there's no actual selection, select the contents of the focused element.
    range.selectNodeContents(focusedElem);
  }
  
  // Does our range include a signature? If so, remove it.
  sig = findSignatureStart(focusedElem);
  if (sig) {
    if (range.isPointInRange(sig, 0)) {
      range.setEndBefore(sig);
    }
  }

  return range;
}

// A signature is indicated by a `'-- '` text node (or something like it).
// Returns the sig start element, or null if one is not found.
function findSignatureStart(startElem) {
  var i, child, recurseReturn;

  for (i = 0; i < startElem.childNodes.length; i++) {
    child = startElem.childNodes[i];
    if (child.nodeType === child.TEXT_NODE) {
      // Thunderbird wraps the sig in a `<pre>`, so there's a newline.
      // Hand-written sigs (including Hotmail and Yahoo) are `'--&nbsp;'` (aka \u00a0).
      // Gmail auto-inserted sigs are `'-- '` (plain space).
      if (child.nodeValue.search(/^--[\s\u00a0]+(\n|$)/) === 0) {

        // Assume that the entire parent element belongs to the sig only if the
        // `'--'` bit is the at the very start of the parent.
        if (startElem.firstChild === child) {
          return startElem;
        }
        
        return child;
      }
    }
    else {
      recurseReturn = findSignatureStart(child, true);

      // Did the recursive call find it?
      if (recurseReturn) {
        return recurseReturn;
      }
    }
  }

  return null;
}

// Replaces the contents of `range` with the HTML string in `html`.
// Returns the element that is created from `html`.
function replaceRange(range, html) {
  var documentFragment, newElement;

  range.deleteContents();

  // Create a DocumentFragment to insert and populate it with HTML
  documentFragment = range.createContextualFragment(html);

  // After inserting the node contents, the node is empty. So we need to save a
  // reference to the element that we need to return.
  newElement = documentFragment.firstChild;

  range.insertNode(documentFragment);

  // Make sure the replacement is selected. This isn't strictly necessary, but
  // in order to make Chrome and Firefox consistent, we either need to remove
  // the selection in Chrome, or set it in Firefox. We'll do the latter.
  range.selectNode(newElement);

  return newElement;
}

// Returns the stylesheet for our styles.
function getMarkdownStylesheet(elem, css) {
  var styleElem, stylesheet, i;

  // We have to actually create a style element in the document, then pull the
  // stylesheet out of it (and remove the element).

  // Create a style element
  styleElem = elem.ownerDocument.createElement('style');
  styleElem.setAttribute('title', 'markdown-here-styles');

  // Set the CSS in the style element
  styleElem.appendChild(elem.ownerDocument.createTextNode(css));

  // Put the style element in the DOM under `elem`
  elem.appendChild(styleElem);

  // Find the stylesheet that we just created
  for (i = 0; i < elem.ownerDocument.styleSheets.length; i++) {
    if (elem.ownerDocument.styleSheets[i].title === 'markdown-here-styles') {
      stylesheet = elem.ownerDocument.styleSheets[i];
      break;
    }
  }

  if (!stylesheet) {
    throw 'Markdown Here stylesheet not found!';
  }

  // Take the stylesheet element out of the DOM
  elem.removeChild(styleElem);

  return stylesheet;
}

// Applies our styling explicitly to the elements under `elem`.
function makeStylesExplicit(wrapperElem, css) {
  var stylesheet, rule, selectorMatches, i, j;

  stylesheet = getMarkdownStylesheet(wrapperElem, css);

  for (i = 0; i < stylesheet.cssRules.length; i++) {
    rule = stylesheet.cssRules[i];

    // Special case for the selector: If the selector is '.markdown-here-wrapper',
    // then we want to apply the rules to the wrapper (not just to its ancestors,
    // which is what querySelectorAll gives us).
    // Note that the CSS should not have any rules that use "body" or "html".

    if (rule.selectorText === '.markdown-here-wrapper') {
      wrapperElem.setAttribute('style', rule.style.cssText);
    }
    else {
      selectorMatches = wrapperElem.querySelectorAll(rule.selectorText);
      for (j = 0; j < selectorMatches.length; j++) {
        selectorMatches[j].setAttribute('style', rule.style.cssText);
      }
    }
  }
}

// Find the wrapper element that's above the current cursor position and returns
// it. Returns falsy if there is no wrapper.
function findMarkdownHereWrapper(focusedElem) {
  var selection, range, wrapper = null, match, i;

  selection = focusedElem.ownerDocument.getSelection();

  if (selection.rangeCount < 1) {
    return null;
  }

  range = selection.getRangeAt(0);

  wrapper = range.commonAncestorContainer;
  while (wrapper) {
    match = false;
    for (i = 0; wrapper.attributes && i < wrapper.attributes.length; i++) {
      if (wrapper.attributes[i].value === 'markdown-here-wrapper') {<commit_msg>Fixed the make-styles-explicit code. It wasn't properly merging styles. (The styling itself is still badwrong, though.)<commit_after>/*
 * Copyright <NAME> 2012
 * MIT License : http://adampritchard.mit-license.org/
 */

/*
 * This file is the heart of Markdown Here. It decides whether we're rendering
 * or revert; whether we're doing a selection or the whole thing; and actually
 * does it (calling out for the final render).
 */

;(function() {

// For debugging purposes. An external service is required to log with Firefox.
var mylog = function() {};

// Used to create unique IDs for each Markdown Here wrapper.
var markdownHereWrapperIdCounter = 1;

// Finds and returns the page element that currently has focus. Drills down into
// iframes if necessary.
function findFocusedElem(document) {
  var focusedElem = document.activeElement;

  // If the focus is within an iframe, we'll have to drill down to get to the
  // actual element.
  while (focusedElem && focusedElem.contentDocument) {
    focusedElem = focusedElem.contentDocument.activeElement;
  }

  return focusedElem;
}

// Returns true if the given element can be properly rendered (i.e., if it's 
// a rich-edit compose element).
function elementCanBeRendered(elem) {
  // See here for more info about what we're checking:
  // http://stackoverflow.com/a/3333679/729729
  return elem.contentEditable === true || elem.contentEditable === 'true'
         || elem.contenteditable === true || elem.contenteditable === 'true'
         || (elem.ownerDocument && elem.ownerDocument.designMode === 'on');  
}

// Get the currectly selected range. If there is no selected range (i.e., it is
// collapsed), then contents of the currently focused element will be selected.
// Returns null if no range is selected nor can be selected.
function getOperationalRange(focusedElem) {
  var selection, range, sig;

  selection = focusedElem.ownerDocument.getSelection();
  if (selection.rangeCount < 1) {
    return null;
  }

  range = selection.getRangeAt(0);

  if (range.collapsed) {
    // If there's no actual selection, select the contents of the focused element.
    range.selectNodeContents(focusedElem);
  }
  
  // Does our range include a signature? If so, remove it.
  sig = findSignatureStart(focusedElem);
  if (sig) {
    if (range.isPointInRange(sig, 0)) {
      range.setEndBefore(sig);
    }
  }

  return range;
}

// A signature is indicated by a `'-- '` text node (or something like it).
// Returns the sig start element, or null if one is not found.
function findSignatureStart(startElem) {
  var i, child, recurseReturn;

  for (i = 0; i < startElem.childNodes.length; i++) {
    child = startElem.childNodes[i];
    if (child.nodeType === child.TEXT_NODE) {
      // Thunderbird wraps the sig in a `<pre>`, so there's a newline.
      // Hand-written sigs (including Hotmail and Yahoo) are `'--&nbsp;'` (aka \u00a0).
      // Gmail auto-inserted sigs are `'-- '` (plain space).
      if (child.nodeValue.search(/^--[\s\u00a0]+(\n|$)/) === 0) {

        // Assume that the entire parent element belongs to the sig only if the
        // `'--'` bit is the at the very start of the parent.
        if (startElem.firstChild === child) {
          return startElem;
        }
        
        return child;
      }
    }
    else {
      recurseReturn = findSignatureStart(child, true);

      // Did the recursive call find it?
      if (recurseReturn) {
        return recurseReturn;
      }
    }
  }

  return null;
}

// Replaces the contents of `range` with the HTML string in `html`.
// Returns the element that is created from `html`.
function replaceRange(range, html) {
  var documentFragment, newElement;

  range.deleteContents();

  // Create a DocumentFragment to insert and populate it with HTML
  documentFragment = range.createContextualFragment(html);

  // After inserting the node contents, the node is empty. So we need to save a
  // reference to the element that we need to return.
  newElement = documentFragment.firstChild;

  range.insertNode(documentFragment);

  // Make sure the replacement is selected. This isn't strictly necessary, but
  // in order to make Chrome and Firefox consistent, we either need to remove
  // the selection in Chrome, or set it in Firefox. We'll do the latter.
  range.selectNode(newElement);

  return newElement;
}

// Returns the stylesheet for our styles.
function getMarkdownStylesheet(elem, css) {
  var styleElem, stylesheet, i;

  // We have to actually create a style element in the document, then pull the
  // stylesheet out of it (and remove the element).

  // Create a style element
  styleElem = elem.ownerDocument.createElement('style');
  styleElem.setAttribute('title', 'markdown-here-styles');

  // Set the CSS in the style element
  styleElem.appendChild(elem.ownerDocument.createTextNode(css));

  // Put the style element in the DOM under `elem`
  elem.appendChild(styleElem);

  // Find the stylesheet that we just created
  for (i = 0; i < elem.ownerDocument.styleSheets.length; i++) {
    if (elem.ownerDocument.styleSheets[i].title === 'markdown-here-styles') {
      stylesheet = elem.ownerDocument.styleSheets[i];
      break;
    }
  }

  if (!stylesheet) {
    throw 'Markdown Here stylesheet not found!';
  }

  // Take the stylesheet element out of the DOM
  elem.removeChild(styleElem);

  return stylesheet;
}

// Applies our styling explicitly to the elements under `elem`.
function makeStylesExplicit(wrapperElem, css) {
  var stylesheet, rule, selectorMatches, i, j;

  stylesheet = getMarkdownStylesheet(wrapperElem, css);

  for (i = 0; i < stylesheet.cssRules.length; i++) {
    rule = stylesheet.cssRules[i];

    // Special case for the selector: If the selector is '.markdown-here-wrapper',
    // then we want to apply the rules to the wrapper (not just to its ancestors,
    // which is what querySelectorAll gives us).
    // Note that the CSS should not have any rules that use "body" or "html".

    function applyStyleToElement(style, element) {
      var i = 0;
      for (i = 0; i < style.length; i++) {
        element.style[style[i]] = style[style[i]];
      }
    }

    if (rule.selectorText === '.markdown-here-wrapper') {
      applyStyleToElement(rule.style, wrapperElem);
    }
    else {
      selectorMatches = wrapperElem.querySelectorAll(rule.selectorText);
      for (j = 0; j < selectorMatches.length; j++) {
        applyStyleToElement(rule.style, selectorMatches[j]);
      }
    }
  }
}

// Find the wrapper element that's above the current cursor position and returns
// it. Returns falsy if there is no wrapper.
function findMarkdownHereWrapper(focusedElem) {
  var selection, range, wrapper = null, match, i;

  selection = focusedElem.ownerDocument.getSelection();

  if (selection.rangeCount < 1) {
    return null;
  }

  range = selection.getRangeAt(0);

  wrapper = range.commonAncestorContainer;
  while (wrapper) {
    match = false;
    for (i = 0; wrapper.attributes && i < wrapper.attributes.length; i++) {
      if (wrapper.attributes[i].value === 'markdown-here-wrapper') {
{'fraction_non_alphanumeric': 0.10896284050887659, 'fraction_numerical': 0.005175016171925537, 'mean_word_length': 3.2112590799031477, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1976865', 'n_tokens_mistral': 4134, 'n_tokens_neox': 3943, 'n_words': 1445}