<commit_before>(function(jQuery) {
  "use strict";

  var $ = jQuery;

  if (window.top !== window) {
    // Don't run in iframes
    return;
  }

  /////////////////////////////////////////////////
  //
  // Utility functions

  // Adds http:// to a URL, if it's needed.
  // Returns false if the string is empty, null, etc.
  var verifyAndFixUrl = function(url) {
    // window.prompt returns null on some browsers,
    // empty string on others.
    if (typeof(url) === 'undefined' ||
        url === null ||
        url.trim() === "") {

      return false;
    }

    var hasProtocol = /^http(s)?:\/\//.test(url);
    if (!hasProtocol) {
      return "http://" + url;
    }
    else {
      return url;
    }
  };

  /////////////////////////////////////////////////
  //
  // Preview & parsing of Markdown

  // When the Markdown has changed, generate
  // new HTML.
  var updateHtmlFromMd = function(event) {
    var mdComment = event.data.mdComment;
    var htmlComment = event.data.htmlComment;

    var mdText = mdComment.val();
    var htmlText = md4mefi.md2html(mdText);
    htmlComment.val(htmlText);

    // The keyup event is the listener for
    // refreshing the live preview.
    htmlComment.trigger('keyup');
  };

  // Turn on the task that updates the live preview
  var turnOnLivePreview = function(event) {
    // Turn on live preview, by running the init_lp function.
    // That function is assigned to the onfocus handler.
    //
    // However, if we run jQuery's .trigger('focus')
    // function, we'll actually switch focus to the
    // htmlComment textarea. Therefore, run
    // the onfocus handler that's set through plain ol'
    // DOM manipulation.

    // TODO: Add error handling, for if we can't find the dom node.
    var htmlCommentDomNode = event.data.htmlComment.get(0);
    htmlCommentDomNode.onfocus();
  };

  var turnOffLivePreview = function(event) {
    // Turn off live preview, by running the kill_lp function,
    // which is the onBlur handler.

    var htmlCommentDomNode = event.data.htmlComment.get(0);
    htmlCommentDomNode.onblur();
  };

  /////////////////////////////////////////////////
  //
  // Event handlers

  var showHideHtml = function(event) {
    var htmlComment = event.data.htmlComment;
    var showHtml = event.data.showHtml;

    var isShow = showHtml.prop('checked');

    var animationOptions = {
      duration: 200,
      //easing: 'easeOut',
    };

    if (isShow) {
      htmlComment.show(animationOptions);
    }
    else {
      htmlComment.hide(animationOptions);
    }
  };

  // TODO: Clicking bold a 2nd time should turn off bold.
  var doBoldOrItalic = function(boldOrItalic, event) {
    var mdComment = event.data.mdComment;
    var selection = mdComment.selection();

    var wrapperChar, placeholderText;
    if (boldOrItalic === 'bold') {
      wrapperChar = '**';
      placeholderText = '**bold text**';
    }
    else {
      wrapperChar = '*';
      placeholderText = "*italic text*";
    }

    if (selection === "") {
      mdComment.selection('insert', {text: placeholderText, mode: 'before'});
    }
    else {
      mdComment.selection('insert', {text: wrapperChar, mode: 'before' });
      mdComment.selection('insert', {text: wrapperChar, mode: 'after' });
    }

    updateHtmlFromMd(event);
  };

  var doItalic = function(event) {
    doBoldOrItalic('italic', event);
  };

  var doBold = function(event) {
    doBoldOrItalic('bold', event);
  };

  var doLink = function(event) {
    var mdComment = event.data.mdComment;
    var selection = mdComment.selection();
    var mdText = mdComment.val();
    var nextLinkNumber = md4mefi.nextLinkNumber(mdText);
    var linkText, linkUrl, caretPos;

    linkUrl = window.prompt("Please enter the site you'd like to link.", "http://");
    console.log(linkUrl);
    linkUrl = verifyAndFixUrl(linkUrl);
    if (linkUrl === false) {
      return;
    }

    if (selection === "") {
      // Nothing selected, insert a link with placeholder text
      caretPos = mdComment.selection('getPos');

      var placeholderText = 'link text';
      linkText = "[" + placeholderText + "][" + nextLinkNumber + "]";
      mdComment.selection('insert', {text: linkText, mode: 'before'});

      // Highlight the placeholder text
      caretPos.start += 1; // move past the opening bracket
      caretPos.end = caretPos.start + placeholderText.length;
      mdComment.selection('setPos', caretPos);
    }
    else {
      // Wrap the selected text in a link

      mdComment.selection('insert', {text: '[', mode: 'before'});

      linkText = "][" + nextLinkNumber + "]";
      mdComment.selection('insert', {text: linkText, mode: 'after'});
    }

    // Insert the reference at the end of the text.
    caretPos = mdComment.selection('getPos');
    mdText = mdComment.val() + "\n\n[" + nextLinkNumber + "]: " + linkUrl + "\n";
    mdComment.val(mdText);
    // Inserting the text changed the selection. Move it back.
    mdComment.selection('setPos', caretPos);

    updateHtmlFromMd(event);
  };

  /////////////////////////////////////////////////
  //
  // Save markdown across server-side previews
  var getSessionStorageKey = function(form) {
    var linkIdInput = form.find('input[name="link_ID"]');
    var storageKeyPrefix = "md4mefi-";

    if (linkIdInput.length === 0) {
      // TODO: Don't return here! This page is probably
      // a submission page for a new post. Look it up
      // by some other key: URL maybe?
      return null;
    }
    else {
      return storageKeyPrefix + linkIdInput.val();
    }
  };

  var saveMarkdownForPreview = function(event) {
    var mdComment = event.data.mdComment;
    var mdExtended = event.data.mdExtended;
    var form = mdComment.closest('form');
    var storageKey = getSessionStorageKey(form);

    if (storageKey === null) {
      return;
    }

    var savedDataJson = {
      comment: mdComment.val(),
      extended: (mdExtended !== null) ? mdExtended.val() : "",
      url: window.location.href,
    };
    var savedDataString = JSON.stringify(savedDataJson);

    window.sessionStorage.setItem(storageKey, savedDataString);
  };

  // Get the saved markdown for this session.
  var restoreMarkdown = function(form) {
    // Only restore markdown if we're doing a preview. If we just, say, opened
    // a second browser tab to the thread's page, then don't restore.
    //
    // If we're at one of the following paths, we're okay.
    var allowedPaths = [
      // Preview a comment
      '/contribute/post_comment_preview.mefi',

      // MeFi FPP
      '/post_preview.mefi',

      // AskMe Question
      // FanFare post
      // MeFi projects
      // MeTa post
      '/post_preview.cfm',

      // TODO:
      // - Check the other subsites (IRL, jobs, music)
      // - Verify that all the sites go to the same comment preview page
    ];
    var isAllowedPath = allowedPaths.some(function(path) {
      return path === window.location.pathname;
    });
    if (!isAllowedPath) {
      return null;
    }

    var storageKey = getSessionStorageKey(form);
    if (storageKey === null) {
      return null;
    }

    var savedDataString = window.sessionStorage.getItem(storageKey);
    if (savedDataString === null) {
      return null;
    }
    window.storage.removeItem(storageKey);

    var savedDataJson = JSON.parse(savedDataString);
    return savedDataJson;
  };

  var deleteMarkdownForPreview = function(event) {
    var form = event.data.form;
    var storageKey = getSessionStorageKey(form);
    if (storageKey === null) {
      return;
    }
    window.storage.removeItem(storageKey);
  };

  /////////////////////////////////////////////////
  //
  // DOM manipulation

  var hideMeFiToolbar = function(htmlComment, commentTable) {
    // Hide MeFi's built-in formatting toolbar (B, I, link)
    // On the new post form, each textarea has its own
    // toolbar, both id'd #toolbar. So start searching
    // from the containing table.
    var mefiToolbar = commentTable.find('#toolbar');
    var mefiToolbarParentTr = mefiToolbar.closest('tr');

    // If the toolbar and the htmlComment are both
    // in the same tr. This can happen when doing
    // a server-side preview.
    if (mefiToolbarParentTr.has(htmlComment).length > 0) {
      mefiToolbar.hide();
    }
    else {
      // tooolbar and htmlComment are in different tr's.
      // Hide the whole tr containing the mefi toolbar,
      // to avoid taking up that vertical space.
      mefiToolbarParentTr.hide();
    }
  };

  // Replace the HTML Help link with a Markdown Help link.
  var replaceHelpLink = function() {
    var anchor = $('#commentform a[href$="//faq.metafilter.com/tags/html"]');
    anchor.attr('href', 'http://daringfireball.net/projects/markdown/basics');
    anchor.html('(Markdown help)');

    // Normal MeFi has target = self. I don't like that; open in a new tab.
    anchor.attr('target', '_blank');
  };

  // Add the Markdown comment box and (optionally) toolbar
  var setupMarkdown = function(htmlComment, includeToolbar) {
    // On a thread page, this is a td with id #commenttd.
    // But on an FPP page, the containing td is not named.
    var commentTd = htmlComment.closest('td');
    var commentTable = commentTd.closest('table');

    var mdComment = $('<textarea>')
      .attr('rows', htmlComment.attr('rows'))
      .attr('cols', htmlComment.attr('cols'))
      .css('width', htmlComment.css('width'))
      .css('height', htmlComment.css('height'));

    //htmlComment.attr('readonly', true);

    var showHtmlLabel = $('<input type=checkbox> Show HTML')
      .prop('checked', false);
    var showHtml = showHtmlLabel.find('input');

    var boldButton = $('
         \
                          **   \
                          Bold   \
                          **   \

                                            \
                       ');
    var italicButton  = $('
      \
                            *   \
                            Italic \
                            *   \

                                            \
                        ');
    var linkButton  = $('
          \
                            [   \
                            Link   \
                            ][1]   \

                                            \
                        ');
    var buttonbar   = $('

"');
    var title       = $('
Markdown for MeFi
');
    var toolbar     = $('

');

    buttonbar
      .append(boldButton)
      .append(italicButton)
      .append(linkButton);
    toolbar
      .append(showHtmlLabel)
      .append(title)
      .append(buttonbar);

    if (includeToolbar) {
      commentTd.prepend(toolbar);
    }
    commentTd.prepend(mdComment);
    htmlComment.hide();

    replaceHelpLink();
    hideMeFiToolbar(htmlComment, commentTable);

    // Set up event handlers
    var eventData = {
      htmlComment: htmlComment,
      mdComment: mdComment,
      showHtml: showHtml,
    };
    mdComment.on('input', eventData, updateHtmlFromMd);
    mdComment.on('focus', eventData, turnOnLivePreview);
    mdComment.on('blur',  eventData, turnOffLivePreview);

    showHtml.on('change', eventData, showHideHtml);

    if (includeToolbar) {
      italicButton.click(eventData, doItalic);
      boldButton.click(eventData, doBold);
      linkButton.click(eventData, doLink);
    }

    return mdComment;
  };

  $(document).ready(function() {
    var htmlComment = $('#comment');
    var htmlExtended = $('#extended');
    var mdComment, mdExtended;

    if (htmlComment.length === 0) {
      // No text entry on this page
      return;
    }
    else if (htmlExtended.length === 0) {
      // No textarea for extended text.
      // Must be a comment page.
      mdComment = setupMarkdown(htmlComment, true);
      mdExtended = null;
    }
    else {
      // Both comment & extended
      mdComment = setupMarkdown(htmlComment, false);
      mdExtended = setupMarkdown(htmlExtended, true);
    }

    // Restore saved markdown (for server-side preview)
    var form = mdComment.closest('form');
    var storedJson = restoreMarkdown(form);
    if (storedJson !== null) {
      if (storedJson.hasOwnProperty('comment')) {
        mdComment.val(storedJson.comment);
      }

      if (storedJson.hasOwnProperty('extended') &&
        mdExtended !== null) {
        mdExtended.val(storedJson.extended);
      }
    }

    // Handlers for saving markdown on server-side preview
    var previewButton = form.find('#previewButton');
    var postButton = form.find("#postButton");

    // We can't use .on('submit'), because there's no way to
    // tell if the submission is for a preview or a post.
    // Use click handlers, instead.
    // http://stackoverflow.com/a/13202018/939467
    var eventData = {
      mdComment: mdComment,
      mdExtended: mdExtended,
    };
    previewButton.click(eventData, saveMarkdownForPreview);
    postButton.click(eventData, deleteMarkdownForPreview);

    //form.submit(function(event) { event.preventDefault(); });
  });

// noConflict(true) will revert both $ and jQuery, making
// it possible to use this plugin in combination w/ other
// versions of jQuery. Maybe this isn't necessary b/c
// Safari protects plugins from conflicts w/ the page's
// Javascript, but it makes me feel safer.
//
// See: http://stackoverflow.com/a/7882550/939467
})(jQuery.noConflict(true));<commit_msg>Improve handling of server-side previews, for new post forms.<commit_after>(function(jQuery) {
  "use strict";

  var $ = jQuery;

  if (window.top !== window) {
    // Don't run in iframes
    return;
  }

  /////////////////////////////////////////////////
  //
  // Utility functions

  // Adds http:// to a URL, if it's needed.
  // Returns false if the string is empty, null, etc.
  var verifyAndFixUrl = function(url) {
    // window.prompt returns null on some browsers,
    // empty string on others.
    if (typeof(url) === 'undefined' ||
        url === null ||
        url.trim() === "") {

      return false;
    }

    var hasProtocol = /^http(s)?:\/\//.test(url);
    if (!hasProtocol) {
      return "http://" + url;
    }
    else {
      return url;
    }
  };

  /////////////////////////////////////////////////
  //
  // Preview & parsing of Markdown

  // When the Markdown has changed, generate
  // new HTML.
  var updateHtmlFromMd = function(event) {
    var mdComment = event.data.mdComment;
    var htmlComment = event.data.htmlComment;

    var mdText = mdComment.val();
    var htmlText = md4mefi.md2html(mdText);
    htmlComment.val(htmlText);

    // The keyup event is the listener for
    // refreshing the live preview.
    htmlComment.trigger('keyup');
  };

  // Turn on the task that updates the live preview
  var turnOnLivePreview = function(event) {
    // Turn on live preview, by running the init_lp function.
    // That function is assigned to the onfocus handler.
    //
    // However, if we run jQuery's .trigger('focus')
    // function, we'll actually switch focus to the
    // htmlComment textarea. Therefore, run
    // the onfocus handler that's set through plain ol'
    // DOM manipulation.

    // TODO: Add error handling, for if we can't find the dom node.
    var htmlCommentDomNode = event.data.htmlComment.get(0);
    htmlCommentDomNode.onfocus();
  };

  var turnOffLivePreview = function(event) {
    // Turn off live preview, by running the kill_lp function,
    // which is the onBlur handler.

    var htmlCommentDomNode = event.data.htmlComment.get(0);
    htmlCommentDomNode.onblur();
  };

  /////////////////////////////////////////////////
  //
  // Event handlers

  var showHideHtml = function(event) {
    var htmlComment = event.data.htmlComment;
    var showHtml = event.data.showHtml;

    var isShow = showHtml.prop('checked');

    var animationOptions = {
      duration: 200,
      //easing: 'easeOut',
    };

    if (isShow) {
      htmlComment.show(animationOptions);
    }
    else {
      htmlComment.hide(animationOptions);
    }
  };

  // TODO: Clicking bold a 2nd time should turn off bold.
  var doBoldOrItalic = function(boldOrItalic, event) {
    var mdComment = event.data.mdComment;
    var selection = mdComment.selection();

    var wrapperChar, placeholderText;
    if (boldOrItalic === 'bold') {
      wrapperChar = '**';
      placeholderText = '**bold text**';
    }
    else {
      wrapperChar = '*';
      placeholderText = "*italic text*";
    }

    if (selection === "") {
      mdComment.selection('insert', {text: placeholderText, mode: 'before'});
    }
    else {
      mdComment.selection('insert', {text: wrapperChar, mode: 'before' });
      mdComment.selection('insert', {text: wrapperChar, mode: 'after' });
    }

    updateHtmlFromMd(event);
  };

  var doItalic = function(event) {
    doBoldOrItalic('italic', event);
  };

  var doBold = function(event) {
    doBoldOrItalic('bold', event);
  };

  var doLink = function(event) {
    var mdComment = event.data.mdComment;
    var selection = mdComment.selection();
    var mdText = mdComment.val();
    var nextLinkNumber = md4mefi.nextLinkNumber(mdText);
    var linkText, linkUrl, caretPos;

    linkUrl = window.prompt("Please enter the site you'd like to link.", "http://");
    console.log(linkUrl);
    linkUrl = verifyAndFixUrl(linkUrl);
    if (linkUrl === false) {
      return;
    }

    if (selection === "") {
      // Nothing selected, insert a link with placeholder text
      caretPos = mdComment.selection('getPos');

      var placeholderText = 'link text';
      linkText = "[" + placeholderText + "][" + nextLinkNumber + "]";
      mdComment.selection('insert', {text: linkText, mode: 'before'});

      // Highlight the placeholder text
      caretPos.start += 1; // move past the opening bracket
      caretPos.end = caretPos.start + placeholderText.length;
      mdComment.selection('setPos', caretPos);
    }
    else {
      // Wrap the selected text in a link

      mdComment.selection('insert', {text: '[', mode: 'before'});

      linkText = "][" + nextLinkNumber + "]";
      mdComment.selection('insert', {text: linkText, mode: 'after'});
    }

    // Insert the reference at the end of the text.
    caretPos = mdComment.selection('getPos');
    mdText = mdComment.val() + "\n\n[" + nextLinkNumber + "]: " + linkUrl + "\n";
    mdComment.val(mdText);
    // Inserting the text changed the selection. Move it back.
    mdComment.selection('setPos', caretPos);

    updateHtmlFromMd(event);
  };

  /////////////////////////////////////////////////
  //
  // Save markdown across server-side previews
  var getSessionStorageKey = function(form) {
    var linkIdInput = form.find('input[name="link_ID"]');
    var storageKeyPrefix = "md4mefi-";

    // Pages for making a new post have forms that
    // lead to one of these two pages. Use regex to avoid
    // worrying if the path has a leading slash, or a hostname,
    // or any other weird stuff.
    var isNewPostPage =
      /post_preview.mefi$/.test(form.attr('action')) ||
      /post_preview.cfm$/.test(form.attr('action'));

    if (linkIdInput.length > 0) {
      // This is a comment thread
      return storageKeyPrefix + linkIdInput.val();
    }
    else if (isNewPostPage) {
      // Store up to one new post for each site.
      return storageKeyPrefix + '-new-post-' + window.location.hostname;
    }
    else {
      return null;
    }
  };

  var saveMarkdownForPreview = function(event) {
    var mdComment = event.data.mdComment;
    var mdExtended = event.data.mdExtended;
    var form = mdComment.closest('form');
    var storageKey = getSessionStorageKey(form);

    if (storageKey === null) {
      return;
    }

    var savedDataJson = {
      comment: mdComment.val(),
      extended: (mdExtended !== null) ? mdExtended.val() : "",
      url: window.location.href,
    };
    var savedDataString = JSON.stringify(savedDataJson);

    window.sessionStorage.setItem(storageKey, savedDataString);
  };

  // Get the saved markdown for this session.
  var restoreMarkdown = function(form) {
    // Only restore markdown if we're doing a preview. If we just, say, opened
    // a second browser tab to the thread's page, then don't restore.
    //
    // If we're at one of the following paths, we're okay.
    var allowedPaths = [
      // Preview a comment
      '/contribute/post_comment_preview.mefi',

      // MeFi FPP
      '/post_preview.mefi',

      // AskMe Question
      // FanFare post
      // MeFi projects
      // MeTa post
      '/post_preview.cfm',
      '/contribute/post_preview.cfm'

      // TODO:
      // - Check the other subsites (IRL, jobs, music)
      // - Verify that all the sites go to the same comment preview page
    ];
    var isAllowedPath = allowedPaths.some(function(path) {
      return path === window.location.pathname;
    });
    if (!isAllowedPath) {
      return null;
    }

    var storageKey = getSessionStorageKey(form);
    if (storageKey === null) {
      return null;
    }

    var savedDataString = window.sessionStorage.getItem(storageKey);
    if (savedDataString === null) {
      return null;
    }
    window.sessionStorage.removeItem(storageKey);

    var savedDataJson = JSON.parse(savedDataString);
    return savedDataJson;
  };

  var deleteMarkdownForPreview = function(event) {
    var form = event.data.form;
    var storageKey = getSessionStorageKey(form);
    if (storageKey === null) {
      return;
    }
    window.sessionStorage.removeItem(storageKey);
  };

  /////////////////////////////////////////////////
  //
  // DOM manipulation

  var hideMeFiToolbar = function(htmlComment, commentTable) {
    // Hide MeFi's built-in formatting toolbar (B, I, link)
    // On the new post form, each textarea has its own
    // toolbar, both id'd #toolbar. So start searching
    // from the containing table.
    var mefiToolbar = commentTable.find('#toolbar');
    var mefiToolbarParentTr = mefiToolbar.closest('tr');

    // If the toolbar and the htmlComment are both
    // in the same tr. This can happen when doing
    // a server-side preview.
    if (mefiToolbarParentTr.has(htmlComment).length > 0) {
      mefiToolbar.hide();
    }
    else {
      // tooolbar and htmlComment are in different tr's.
      // Hide the whole tr containing the mefi toolbar,
      // to avoid taking up that vertical space.
      mefiToolbarParentTr.hide();
    }
  };

  // Replace the HTML Help link with a Markdown Help link.
  var replaceHelpLink = function() {
    var anchor = $('#commentform a[href$="//faq.metafilter.com/tags/html"]');
    anchor.attr('href', 'http://daringfireball.net/projects/markdown/basics');
    anchor.html('(Markdown help)');

    // Normal MeFi has target = self. I don't like that; open in a new tab.
    anchor.attr('target', '_blank');
  };

  // Add the Markdown comment box and (optionally) toolbar
  var setupMarkdown = function(htmlComment, includeToolbar) {
    // On a thread page, this is a td with id #commenttd.
    // But on an FPP page, the containing td is not named.
    var commentTd = htmlComment.closest('td');
    var commentTable = commentTd.closest('table');

    var mdComment = $('<textarea>')
      .attr('rows', htmlComment.attr('rows'))
      .attr('cols', htmlComment.attr('cols'))
      .css('width', htmlComment.css('width'))
      .css('height', htmlComment.css('height'));

    //htmlComment.attr('readonly', true);

    var showHtmlLabel = $('<input type=checkbox> Show HTML')
      .prop('checked', false);
    var showHtml = showHtmlLabel.find('input');

    var boldButton = $('
         \
                          **   \
                          Bold   \
                          **   \

                                            \
                       ');
    var italicButton  = $('
      \
                            *   \
                            Italic \
                            *   \

                                            \
                        ');
    var linkButton  = $('
          \
                            [   \
                            Link   \
                            ][1]   \

                                            \
                        ');
    var buttonbar   = $('

"');
    var title       = $('
Markdown for MeFi
');
    var toolbar     = $('

');

    buttonbar
      .append(boldButton)
      .append(italicButton)
      .append(linkButton);
    toolbar
      .append(showHtmlLabel)
      .append(title)
      .append(buttonbar);

    if (includeToolbar) {
      commentTd.prepend(toolbar);
    }
    commentTd.prepend(mdComment);
    htmlComment.hide();

    replaceHelpLink();
    hideMeFiToolbar(htmlComment, commentTable);

    // Set up event handlers
    var eventData = {
      htmlComment: htmlComment,
      mdComment: mdComment,
      showHtml: showHtml,
    };
    mdComment.on('input', eventData, updateHtmlFromMd);
    mdComment.on('focus', eventData, turnOnLivePreview);
    mdComment.on('blur',  eventData, turnOffLivePreview);

    showHtml.on('change', eventData, showHideHtml);

    if (includeToolbar) {
      italicButton.click(eventData, doItalic);
      boldButton.click(eventData, doBold);
      linkButton.click(eventData, doLink);
    }

    return mdComment;
  };

  $(document).ready(function() {
    var htmlComment = $('#comment');
    var htmlExtended = $('#extended');
    var mdComment, mdExtended;

    if (htmlComment.length === 0) {
      // No text entry on this page
      return;
    }
    else if (htmlExtended.length === 0) {
      // No textarea for extended text.
      // Must be a comment page.
      mdComment = setupMarkdown(htmlComment, true);
      mdExtended = null;
    }
    else {
      // Both comment & extended
      mdComment = setupMarkdown(htmlComment, false);
      mdExtended = setupMarkdown(htmlExtended, true);
    }

    // Restore saved markdown (for server-side preview)
    var form = mdComment.closest('form');
    var storedJson = restoreMarkdown(form);
    if (storedJson !== null) {
      if (storedJson.hasOwnProperty('comment')) {
        mdComment.val(storedJson.comment);
      }

      if (storedJson.hasOwnProperty('extended') &&
        mdExtended !== null) {
        mdExtended.val(storedJson.extended);
      }
    }

    // There are lots of different ways the preview
    // and post buttons are represented. Hunt for
    // whatever is on this page.

    // Coment form
    var postButton = form.find("#postButton");
    // Second time visiting a new post page.
    // (First time, all you can do is preview.)
    if (postButton.length === 0) {
      postButton = form.find('input[type="submit"][value="Post"]');
    }

    // Comment form
    var previewButton = form.find('#previewButton');
    // First time visiting a new post page
    if (previewButton.length === 0) {
      previewButton = form.find('input[type="submit"][value="Step 2: Preview Your Post"]');
    }
    // Second time visiting a new post page
    if (previewButton.length === 0) {
      previewButton = form.find('input[type="submit"][value="Preview"]');
    }

    // We can't use .on('submit'), because there's no way to
    // tell if the submission is for a preview or a post.
    // Use click handlers, instead.
    // http://stackoverflow.com/a/13202018/939467
    var eventData = {
      mdComment: mdComment,
      mdExtended: mdExtended,
    };
    previewButton.click(eventData, saveMarkdownForPreview);
    postButton.click(eventData, deleteMarkdownForPreview);

    //form.submit(function(event) { event.preventDefault(); });
  });

// noConflict(true) will revert both $ and jQuery, making
// it possible to use this plugin in combination w/ other
// versions of jQuery. Maybe this isn't necessary b/c
// Safari protects plugins from conflicts w/ the page's
// Javascript, but it makes me feel safer.
//
// See: http://stackoverflow.com/a/7882550/939467
})(jQuery.noConflict(true));<|endoftext|>


<commit_before>// Dependencies
var chalk = require('chalk');
var sassLint = require('sass-lint');
var path = require('path');

// Helper Utils
var formatter = require('./formatter');

/**
 * Linter
 *
 * @param {String|Buffer} input Javascript string
 * @param {Object} config scsslint configuration
 * @param {Object} webpack webpack instance
 * @param {Function} callback optional callback for async loader
 * @return {void}
 */
function lint(input, options, webpack, callback) {
  // Run sassLint
  var report = sassLint.lintFiles(input, {}, options.configFile);

  var errorCount = 0,
    warningCount = 0;

  if (report.length) {
    report.forEach(function(file) {
      if (file.warningCount && options.quiet) {
        file.warningCount = 0;
        file.messages = report.messages
          .filter(function(message) {
            return message.severity !== 1;
          });
      }

      errorCount += file.errorCount || 0;
      warningCount += file.warningCount || 0;

      if (file.errorCount || file.warningCount) {
        console.log(formatter(file.messages, file.filePath));
      }
    });
  }

  if (options.failOnError && errorCount) {
    throw new Error('Failed because of a sasslint error.\n');
  } else if (options.failOnWarning && warningCount) {
    throw new Error('Failed because of a sasslint warning.\n');
  }

  if (callback) {
    callback(null, input);
  }
}

/**
 * Iternate through files for linter
 *
 * @param {String|Buffer} input Javascript string
 * @param {Object} config scsslint configuration
 * @param {Object} webpack webpack instance
 * @param {Function} callback optional callback for async loader
 * @return {void}
 */
module.exports = function(input, options, callback) {
  lint(input, options, (callback));
}<commit_msg>Partial working as a plugin<commit_after>// Dependencies
var chalk = require('chalk');
var sassLint = require('sass-lint');
var path = require('path');

// Helper Utils
var formatter = require('./formatter');

/**
 * Linter
 *
 * @param {String|Buffer} input Javascript string
 * @param {Object} config scsslint configuration
 * @param {Object} webpack webpack instance
 * @param {Function} callback optional callback for async loader
 * @return {void}
 */
function lint(input, options, webpack, callback) {
  // Run sassLint
  var report = sassLint.lintFiles(input, {}, options.configFile);

  var errorCount = 0,
    warningCount = 0;

  if (report.length) {
    report.forEach(function(file) {
      if (file.warningCount && options.quiet) {
        file.warningCount = 0;
        file.messages = report.messages
          .filter(function(message) {
            return message.severity !== 1;
          });
      }

      errorCount += file.errorCount || 0;
      warningCount += file.warningCount || 0;

      if (file.errorCount || file.warningCount) {
        console.log(formatter(file.messages, file.filePath));
      }
    });
  }

  if (options.failOnError && errorCount) {
    throw new Error('Failed because of a sasslint error.\n');
  } else if (options.failOnWarning && warningCount) {
    throw new Error('Failed because of a sasslint warning.\n');
  }

  if (callback) {
    callback(null, input);
  }
}

/**
 * Iternate through files for linter
 *
 * @param {String|Buffer} input Javascript string
 * @param {Object} config scsslint configuration
 * @param {Object} webpack webpack instance
 * @param {Function} callback optional callback for async loader
 * @return {void}
 */
module.exports = lint(input, options, callback);<|endoftext|>


<commit_before>'use strict';

var opentype = require('opentype.js');
var merge = require('webpack-merge');

var getDefaultOptions = require('./getDefaultOptions');
var getLoaderOptions = require('./getLoaderOptions');
var getOptionsFromQuery = require('./getOptionsFromQuery');
var getOptionsFromContent = require('./getOptionsFromContent');
var getGlyphStore = require('./getGlyphStore');
var renderText = require('./renderText');

/**
 * @param {string} content
 */
module.exports = function opentypeSVGLoader(content) {
  if (this.cacheable) {
    this.cacheable();
  }

  var callback = this.async();

  /**
   * @property {Object} fonts
   * @property {Object} data
   * @property {string|Array.<string>} text
   * @property {string} font
   * @property {number} size
   * @property {number} lineHeight
   * @property {number} letterSpacing
   * @property {'left'|'center'|'right} textAlign
   * @property {number} decimalPlaces
   * @property {Boolean} kerning
   * @property {Object} attrs
   * @property {string} sharedGlyphStore
   */
  var options = merge(
    getDefaultOptions(),
    getLoaderOptions(this),
    getOptionsFromQuery(this.resourceQuery),
    getOptionsFromContent(content)
  );

  if (!options.font) {
    if (options.fonts) {
      options.font = Object.keys(options.fonts)[0];
    }
  }

  opentype.load(options.fonts[options.font], function(error, font) {
    if (error) {
      callback(error);
    } else {
      var glyphStore = getGlyphStore(
        options.sharedGlyphStore,
        font,
        options.font
      );

      var texts = renderText(options.text, options.data);

      var lines = texts.map(function(lineString) {
        var lineDescriptor = {
          glyphs: [],
          width: 0,
        };

        var lineChars = lineString.split('');
        var x = 0;

        lineChars.forEach(function(char, index) {
          var currentGlyph = glyphStore.getGlyph(char);

          var glyphDescriptor = {
            id: glyphStore.getGlyphId(currentGlyph),
            x: x,
          };

          x += currentGlyph.advanceWidth;

          if (options.kerning && index + 1 < lineChars.length) {
            var nextGlyph = glyphStore.getGlyph(lineChars[index + 1]);
            x += font.getKerningValue(currentGlyph, nextGlyph) || 0;
          }

          if (options.letterSpacing) {
            x += options.letterSpacing * font.unitsPerEm;
          }

          lineDescriptor.width = glyphDescriptor.x +
            currentGlyph.leftSideBearing +
            (currentGlyph.xMax - currentGlyph.xMin);

          lineDescriptor.glyphs.push(glyphDescriptor);
        });

        return lineDescriptor;
      });

      var absoluteLineHeight = options.lineHeight * font.unitsPerEm;
      var firstBaseline = font.ascender + font.descender / 2;
      var bottomOffset = font.descender;
      var totalHeight = firstBaseline +
        (texts.length - 1) * absoluteLineHeight -
        bottomOffset;
      var totalWidth = lines.reduce(
        function(acc, line) {
          if (line.width > acc) {
            return line.width;
          } else {
            return acc;
          }
        },
        0
      );

      var glyphsString;
      var ownGlyphs = glyphStore.getOwnGlyphs();

      if (ownGlyphs.length) {
        glyphsString = '<defs>';

        glyphsString += ownGlyphs.reduce(
          function(acc, glyph) {
            var id = glyphStore.getGlyphId(glyph);
            var path = glyph.getPath(0, 0, font.unitsPerEm);
            return acc +
              '<path id="' +
              id +
              '" d="' +
              path.toPathData(options.decimalPlaces) +
              '"/>';
          },
          ''
        );

        glyphsString += '</defs>';
      } else {
        glyphsString = '';
      }

      var linesString = lines.reduce(
        function(acc, line, index) {
          var y = firstBaseline + absoluteLineHeight * index;

          var xOffset = 0;

          if (options.textAlign === 'right') {
            xOffset = totalWidth - line.width;
          } else if (options.textAlign === 'center') {
            xOffset = (totalWidth - line.width) * 0.5;
          }

          acc += '<g id="line-' + index + '">';

          line.glyphs.forEach(function(glyph) {
            var x = glyph.x + xOffset;
            acc += '<use x="' +
              x +
              '" y="' +
              y +
              '" xlink:href="#' +
              glyph.id +
              '" />';
          });

          acc += '</g>';

          return acc;
        },
        ''
      );

      var fontSizeScale = 1 / font.unitsPerEm * options.size;

      var attrs = merge({}, options.attrs, {
        width: Math.round(fontSizeScale * totalWidth),
        height: Math.round(fontSizeScale * totalHeight),
        viewBox: [0, 0, totalWidth, totalHeight].join(' '),
      });

      var attrsString = Object.keys(attrs).reduce(
        function(acc, tempKey) {
          return acc + tempKey + '=' + JSON.stringify(attrs[tempKey]) + ' ';
        },
        ''
      );

      var value = '<svg ' +
        attrsString +
        '>' +
        glyphsString +
        linesString +
        '</svg>';

      var result = 'module.exports = ' + JSON.stringify(value);
      callback(null, result);
    }
  });
};
<commit_msg>Added workaround for missing ascender/descender information<commit_after>'use strict';

var opentype = require('opentype.js');
var merge = require('webpack-merge');

var getDefaultOptions = require('./getDefaultOptions');
var getLoaderOptions = require('./getLoaderOptions');
var getOptionsFromQuery = require('./getOptionsFromQuery');
var getOptionsFromContent = require('./getOptionsFromContent');
var getGlyphStore = require('./getGlyphStore');
var renderText = require('./renderText');

/**
 * @param {string} content
 */
module.exports = function opentypeSVGLoader(content) {
  if (this.cacheable) {
    this.cacheable();
  }

  var callback = this.async();

  /**
   * @property {Object} fonts
   * @property {Object} data
   * @property {string|Array.<string>} text
   * @property {string} font
   * @property {number} size
   * @property {number} lineHeight
   * @property {number} letterSpacing
   * @property {'left'|'center'|'right} textAlign
   * @property {number} decimalPlaces
   * @property {Boolean} kerning
   * @property {Object} attrs
   * @property {string} sharedGlyphStore
   */
  var options = merge(
    getDefaultOptions(),
    getLoaderOptions(this),
    getOptionsFromQuery(this.resourceQuery),
    getOptionsFromContent(content)
  );

  if (!options.font) {
    if (options.fonts) {
      options.font = Object.keys(options.fonts)[0];
    }
  }

  opentype.load(options.fonts[options.font], function(error, font) {
    if (error) {
      callback(error);
    } else {
      var glyphStore = getGlyphStore(
        options.sharedGlyphStore,
        font,
        options.font
      );

      var texts = renderText(options.text, options.data);

      var lines = texts.map(function(lineString) {
        var lineDescriptor = {
          glyphs: [],
          width: 0,
          yMax: 0,
        };

        var lineChars = lineString.split('');
        var x = 0;

        lineChars.forEach(function(char, index) {
          var currentGlyph = glyphStore.getGlyph(char);

          var glyphDescriptor = {
            id: glyphStore.getGlyphId(currentGlyph),
            x: x,
          };

          x += currentGlyph.advanceWidth;

          if (options.kerning && index + 1 < lineChars.length) {
            var nextGlyph = glyphStore.getGlyph(lineChars[index + 1]);
            x += font.getKerningValue(currentGlyph, nextGlyph) || 0;
          }

          if (options.letterSpacing) {
            x += options.letterSpacing * font.unitsPerEm;
          }

          if (currentGlyph.yMax > lineDescriptor.yMax) {
            lineDescriptor.yMax = currentGlyph.yMax;
          }

          lineDescriptor.width = glyphDescriptor.x +
            currentGlyph.leftSideBearing +
            (currentGlyph.xMax - currentGlyph.xMin);

          lineDescriptor.glyphs.push(glyphDescriptor);
        });

        return lineDescriptor;
      });

      var absoluteLineHeight = options.lineHeight * font.unitsPerEm;
      var firstBaseline = font.ascender + font.descender / 2;
      if (firstBaseline < lines[0].yMax) {
        firstBaseline = lines[0].yMax;
      }

      var bottomOffset = font.descender;
      var totalHeight = firstBaseline +
        (texts.length - 1) * absoluteLineHeight -
        bottomOffset;
      var totalWidth = lines.reduce(
        function(acc, line) {
          if (line.width > acc) {
            return line.width;
          } else {
            return acc;
          }
        },
        0
      );

      var glyphsString;
      var ownGlyphs = glyphStore.getOwnGlyphs();

      if (ownGlyphs.length) {
        glyphsString = '<defs>';

        glyphsString += ownGlyphs.reduce(
          function(acc, glyph) {
            var id = glyphStore.getGlyphId(glyph);
            var path = glyph.getPath(0, 0, font.unitsPerEm);
            return acc +
              '<path id="' +
              id +
              '" d="' +
              path.toPathData(options.decimalPlaces) +
              '"/>';
          },
          ''
        );

        glyphsString += '</defs>';
      } else {
        glyphsString = '';
      }

      var linesString = lines.reduce(
        function(acc, line, index) {
          var y = firstBaseline + absoluteLineHeight * index;

          var xOffset = 0;

          if (options.textAlign === 'right') {
            xOffset = totalWidth - line.width;
          } else if (options.textAlign === 'center') {
            xOffset = (totalWidth - line.width) * 0.5;
          }

          acc += '<g id="line-' + index + '">';

          line.glyphs.forEach(function(glyph) {
            var x = glyph.x + xOffset;
            acc += '<use x="' +
              x +
              '" y="' +
              y +
              '" xlink:href="#' +
              glyph.id +
              '" />';
          });

          acc += '</g>';

          return acc;
        },
        ''
      );

      var fontSizeScale = 1 / font.unitsPerEm * options.size;

      var attrs = merge({}, options.attrs, {
        width: Math.round(fontSizeScale * totalWidth),
        height: Math.round(fontSizeScale * totalHeight),
        viewBox: [0, 0, totalWidth, totalHeight].join(' '),
      });

      var attrsString = Object.keys(attrs).reduce(
        function(acc, tempKey) {
          return acc + tempKey + '=' + JSON.stringify(attrs[tempKey]) + ' ';
        },
        ''
      );

      var value = '<svg ' +
        attrsString +
        '>' +
        glyphsString +
        linesString +
        '</svg>';

      var result = 'module.exports = ' + JSON.stringify(value);
      callback(null, result);
    }
  });
};
<|endoftext|>


<commit_before>module.exports = function(source) {
  if(this.cacheable) this.cacheable();

  var example = this.options.example || {};
  var requires = Object.keys(example).map((k) => `import ${k} from '${example[k]}';`).join('\n');

  var src = `
${requires}

module.exports = React.createClass({
  render() {
    return (

        ${source}

            {\`${source}\`}

    );
  }
});`;

  return src;
};
<commit_msg>add react import<commit_after>module.exports = function(source) {
  if(this.cacheable) this.cacheable();

  var example = this.options.example || {};
  var requires = Object.keys(example).map((k) => `import ${k} from '${example[k]}';`).join('\n');

  var src = `
import React from 'react';
${requires}

module.exports = React.createClass({
  render() {
    return (

        ${source}

            {\`${source}\`}

    );
  }
});`;

  return src;
};
<|endoftext|>


<commit_before>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var path = require('path')
  , fs = require('fs')
  , existsSync = typeof fs.existsSync == 'function' ?
      fs.existsSync : path.existsSync
  , utils = require('utilities')
  , CoffeeScript
  , Loader;

Loader = function () {

  var JAKEFILE_PAT = /\.jake(\.js|\.coffee)?$/;

  var _requireCoffee = function () {
        try {
          return require('coffee-script');
        }
        catch (e) {
          fail('CoffeeScript is missing! Try `npm install coffee-script`');
        }
      };

  this.loadFile = function (file) {
    var jakefile = file ?
            file.replace(/\.js$/, '').replace(/\.coffee$/, '') : 'Jakefile'
      , fileSpecified = !!file
      // Dear God, why?
      , isCoffee = false
      // Warning, recursive
      , exists
      , oldCwd = process.cwd();

    exists = function () {
      var cwd = process.cwd();
      if (existsSync(jakefile) || existsSync(jakefile + '.js') ||
        existsSync(jakefile + '.coffee')) {
        return true;
      }
      if (!fileSpecified) {
        process.chdir("..");
        if (cwd === process.cwd()) {
          // Restore the working directory on failure
          process.chdir(oldCwd);
          return false;
        }
        return exists();
      }
    };

    if (!exists()) {
      return false;
    }

    isCoffee = existsSync(jakefile + '.coffee');
    if (isCoffee) {
      CoffeeScript = _requireCoffee();
    }
    require(utils.file.absolutize(jakefile));
    return true;
  };

  this.loadDirectory = function (d) {
    var dirname = d || 'jakelib'
      , dirlist;
    dirname = utils.file.absolutize(dirname);
    if (existsSync(dirname)) {
      dirlist = fs.readdirSync(dirname);
      dirlist.forEach(function (filePath) {
        if (JAKEFILE_PAT.test(filePath)) {
          if (/\.coffee$/.test(filePath)) {
            CoffeeScript = _requireCoffee();
            CoffeeScript.register();
          }
          require(path.join(dirname, filePath));
        }
      });
      return true;
    }

    return false;
  };
};

module.exports.Loader = Loader;
<commit_msg>Avoid issues with older versions of CoffeeScript where compiler registration is not required<commit_after>/*
 * Jake JavaScript build tool
 * Copyright 2112 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

var path = require('path')
  , fs = require('fs')
  , existsSync = typeof fs.existsSync == 'function' ?
      fs.existsSync : path.existsSync
  , utils = require('utilities')
  , CoffeeScript
  , Loader;

Loader = function () {

  var JAKEFILE_PAT = /\.jake(\.js|\.coffee)?$/;

  var _requireCoffee = function () {
        try {
          return require('coffee-script');
        }
        catch (e) {
          fail('CoffeeScript is missing! Try `npm install coffee-script`');
        }
      };

  this.loadFile = function (file) {
    var jakefile = file ?
            file.replace(/\.js$/, '').replace(/\.coffee$/, '') : 'Jakefile'
      , fileSpecified = !!file
      // Dear God, why?
      , isCoffee = false
      // Warning, recursive
      , exists
      , oldCwd = process.cwd();

    exists = function () {
      var cwd = process.cwd();
      if (existsSync(jakefile) || existsSync(jakefile + '.js') ||
        existsSync(jakefile + '.coffee')) {
        return true;
      }
      if (!fileSpecified) {
        process.chdir("..");
        if (cwd === process.cwd()) {
          // Restore the working directory on failure
          process.chdir(oldCwd);
          return false;
        }
        return exists();
      }
    };

    if (!exists()) {
      return false;
    }

    isCoffee = existsSync(jakefile + '.coffee');
    if (isCoffee) {
      CoffeeScript = _requireCoffee();
    }
    require(utils.file.absolutize(jakefile));
    return true;
  };

  this.loadDirectory = function (d) {
    var dirname = d || 'jakelib'
      , dirlist;
    dirname = utils.file.absolutize(dirname);
    if (existsSync(dirname)) {
      dirlist = fs.readdirSync(dirname);
      dirlist.forEach(function (filePath) {
        if (JAKEFILE_PAT.test(filePath)) {
          if (/\.coffee$/.test(filePath)) {
            CoffeeScript = _requireCoffee();
            // Ensure we support CoffeeScript versions older than 1.7.0
            try {
              CoffeeScript.register();
            } catch(e) {}
          }
          require(path.join(dirname, filePath));
        }
      });
      return true;
    }

    return false;
  };
};

module.exports.Loader = Loader;
<|endoftext|>


<commit_before>'use strict';

var util = require('util');
var vmDebug = require('debug');
exports = module.exports = logger;

var RED     = '\x1b[31m';
var GREEN   = '\x1b[32m';
var YELLOW  = '\x1b[33m';
var BLUE    = '\x1b[34m';
var RESET   = '\x1b[0m';

exports.colors = {
  debug: BLUE,
  info: GREEN,
  warn: YELLOW,
  error: RED,
  reset: RESET
};

function getErrorMessage(e){
  var errorStrings = ['', ''];

  if(typeof e === 'undefined'){
    return errorStrings;
  }
  if(typeof e === 'string' || e instanceof String){
    errorStrings[0] = ' ' + e;
    return errorStrings;
  }
  if(e instanceof Error){
    errorStrings[0] = ' ' +  e.toString();
    if (e.stack){
      errorStrings[1] = '\n  stack trace: ' + e.stack;
    }
    return errorStrings;
  }
  if(e && typeof e.toString !== 'undefined'){
    errorStrings[0] = ' ' + e.toString();
  }
  errorStrings[1] = '\n  object: ' + util.inspect(e);
  return errorStrings;
};

function getFormattedMessage(message, e){
  var errorStrings = getErrorMessage(e);
  return message + errorStrings[0] + errorStrings[1];
}

function logger(namespace) {
  var log = vmDebug(namespace);
  var debug = vmDebug(namespace + ':debug');

  var loggerObj = {
    logger: log,
    debugLogger: debug,
    isDebugEnabled: function(){
      return debug.enabled;
    }
  };

  if(vmDebug.useColors){
    var colors = exports.colors;
    loggerObj.debug = function(message, e){
      debug(colors.debug +     'DEBUG  ' + colors.reset + getFormattedMessage(message, e));
    };
    loggerObj.info = function(message, e){
      log(colors.info +  '      INFO   ' + colors.reset + getFormattedMessage(message, e));
    };
    loggerObj.warn = function(message, e){
      log(colors.warn +  '      WARN   ' + colors.reset + getFormattedMessage(message, e));
    };
    loggerObj.error = function(message, e){
      log(colors.error + '      ERROR  ' + colors.reset + getFormattedMessage(message, e));
    };
  }
  else {
    loggerObj.debug = function(message, e){
      debug(    'DEBUG  ' + getFormattedMessage(message, e));
    };
    loggerObj.info = function(message, e){
      log('      INFO   ' + getFormattedMessage(message, e));
    };
    loggerObj.warn = function(message, e){
      log('      WARN   ' + getFormattedMessage(message, e));
    };
    loggerObj.error = function(message, e){
      log('      ERROR  ' + getFormattedMessage(message, e));
    };
  }

  return loggerObj;
};
<commit_msg>Logger: now levels are configurable, cleaner code<commit_after>'use strict';

var util = require('util');
var vmDebug = require('debug');
exports = module.exports = logger;

var RED     = '\x1b[31m';
var GREEN   = '\x1b[32m';
var YELLOW  = '\x1b[33m';
var BLUE    = '\x1b[34m';
var RESET   = '\x1b[0m';

exports.levels = {
  debug : {
    color : BLUE,
    prefix :       'DEBUG  ',
    debugLogger : true
  },
  info : {
    color : GREEN,
    prefix : '      INFO   '
  },
  warn : {
    color : YELLOW,
    prefix : '      WARN   '
  },
  error : {
    color : RED,
    prefix : '      ERROR  '
  }
};

function getErrorMessage(e) {
  var errorStrings = ['', ''];

  if ( typeof e === 'undefined') {
    return errorStrings;
  }
  if ( typeof e === 'string' || e instanceof String) {
    errorStrings[0] = ' ' + e;
    return errorStrings;
  }
  if ( e instanceof Error) {
    errorStrings[0] = ' ' + e.toString();
    if (e.stack) {
      errorStrings[1] = '\n  stack trace: ' + e.stack;
    }
    return errorStrings;
  }
  if (e && typeof e.toString !== 'undefined') {
    errorStrings[0] = ' ' + e.toString();
  }
  errorStrings[1] = '\n  object: ' + util.inspect(e);
  return errorStrings;
};

function getFormattedMessage(message, e) {
  var errorStrings = getErrorMessage(e);
  return message + errorStrings[0] + errorStrings[1];
}

function logger(namespace) {
  var log = vmDebug(namespace);
  var debug = vmDebug(namespace + ':debug');

  var loggerObj = {
    logger : log,
    debugLogger : debug,
    isEnabled : function() {
      return log.enabled;
    },
    isDebugEnabled : function() {
      return debug.enabled;
    }
  };

  var levels = exports.levels;
  Object.keys(levels).forEach(function(level) {
    var levelLog = levels[level].debugLogger ? debug : log;
    var color = vmDebug.useColors ? levels[level].color : '';
    var reset = vmDebug.useColors ? RESET : '';

    loggerObj[level] = function(message, e) {
      levelLog(color + levels[level].prefix + reset + getFormattedMessage(message, e));
    };
  });

  return loggerObj;
};
<|endoftext|>


<commit_before>"use strict";

// TODO allow this to be configurable
var DEBUG = false;

function log(message) {
    if (DEBUG) {
        console.log(message);
    }
}

function error(message) {
    if (DEBUG) {
        console.error(message);
    }
}

module.exports.log = log;
module.exports.error = error;
<commit_msg>Refactor logger<commit_after>"use strict";

/**
 * A simple logger used to manage all log messages for the module.
 * Restricts output to only occur when DEBUG is enabled
 */
function Logger() {
    // default debugging to false
    this.DEBUG = false;

    this.log = function(message) {
        if (this.DEBUG) {
            console.log(message);
        }
    };

    this.error = function(message) {
        if (this.DEBUG) {
            console.error(message);
        }
    };
}

// single instance of the logger for all consumers
var logger = new Logger();

module.exports = logger;
<|endoftext|>


<commit_before>"use strict";

var fs = require('nano-fs'),
    Path = require('path'),
    util = require('util');

/* ------------------------------------------------------------------------ */
function Logger(stage, job) {

	var opts = job.sched.opts,
	    dumps_folder = opts.dumps_folder || './.logs/',
	    console = opts.console || /* istanbul ignore next */ global.console,
	    quiet = opts.quiet || {};

	var context = job.sched.name + ':' + job.name + '#' + stage;

	this.stage = stage;
	this.job = job;

	this.log = function (code, format, a, b, etc) {
		/* istanbul ignore else */
		if (!(code in quiet))
			console.log('%s: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.trace = function () {
		this.log.apply(this, Array.prototype.concat.apply(['trace'], arguments));
	};

	this.warn = function (code, format, a, b, etc) {
		/* istanbul ignore else */
		if (!(code in quiet))
			console.warn('%s: warning: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.error = function (format, a, b, etc) {
		console.error('%s: error: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.fail = function (format, a, b, etc) {
		console.log('%s: FAIL: %s', context, util.format.apply(util.format, arguments));
	};

	this.writeListing = function (name, data) {
		var json = require('nano-json'),
		    text = typeof data === 'object' ? json.render(data, { }) : json.js2str(data),
		    self = this;

		return fs.mkpath(dumps_folder).then(function () {
			return fs.writeFile(Path.join(dumps_folder, context+'.dump.json'), text, { encoding: 'utf8' });
		});
	};
}

Logger.prototype = {
};

module.exports = Logger;
<commit_msg>log.msg() function instead of log.log<commit_after>"use strict";

var fs = require('nano-fs'),
    Path = require('path'),
    util = require('util');

/* ------------------------------------------------------------------------ */
function Logger(stage, job) {

	var opts = job.sched.opts,
	    dumps_folder = opts.dumps_folder || './.logs/',
	    console = opts.console || /* istanbul ignore next */ global.console,
	    quiet = opts.quiet || {};

	var context = job.sched.name + ':' + job.name + '#' + stage;

	this.stage = stage;
	this.job = job;

	this.log = function (code, format, a, b, etc) {
		/* istanbul ignore else */
		if (!(code in quiet))
			console.log('%s: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.msg = this.log;

	this.trace = function () {
		this.log.apply(this, Array.prototype.concat.apply(['trace'], arguments));
	};

	this.warn = function (code, format, a, b, etc) {
		/* istanbul ignore else */
		if (!(code in quiet))
			console.warn('%s: warning: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.error = function (format, a, b, etc) {
		console.error('%s: error: %s', context, util.format.apply(util.format, Array.prototype.slice.call(arguments, 1)));
	};

	this.fail = function (format, a, b, etc) {
		console.log('%s: FAIL: %s', context, util.format.apply(util.format, arguments));
	};

	this.writeListing = function (name, data) {
		var json = require('nano-json'),
		    text = typeof data === 'object' ? json.render(data, { }) : json.js2str(data),
		    self = this;

		return fs.mkpath(dumps_folder).then(function () {
			return fs.writeFile(Path.join(dumps_folder, context+'.dump.json'), text, { encoding: 'utf8' });
		});
	};
}

Logger.prototype = {
};

module.exports = Logger;
<|endoftext|>


<commit_before>'use strict';

var util = require('util')
  , cluster = require('cluster')
  , path = require('path')
  , fs = require('fs')
  , _ = require('lodash')
  , template = require('lodash.template')
  , events = require('events')
  , moment = require('moment')
  , chalk = require('chalk')
  , log = {}
  , that, logger;

that = {
  defaults: {
    levels: { NONE: 0, ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4, TRACE: 5, ALL: Number.MAX_VALUE },
    level: process.env['NODE_ENV'] === 'production' ? 'WARN' : 'INFO',
    template: {},
    out: console.log,
    caller: true,
    useArgFunction: false
  },
  enabledLevels: {},
  lastTime: Date.now(),
  buildLogMethod: function(level) {
    return function() {
      var args = Array.prototype.slice.call(arguments)
        , data, caller;
      if (that.config.caller) {
        try {
          caller = that.getCaller();
        } catch (err) {
          log.warn(err.stack);
          caller = null;
        }
      }
      if (that.config.useArgFunction) {
        args = args.map(function(arg) {
          return typeof arg === 'function' ? arg() : arg;
        });
      }
      data = {
        time: moment(),
        lastTime: that.lastTime,
        level: level,
        levelValue: that.getLevelValue(level),
        args: args,
        caller: caller
      };
      that.lastTime = data.time;
      that.sendLog(that.buildLogMessage(data));
    };
  },
  express: function(options) {
    var onFinished = require('on-finished')
      , levelName, level;
    options = options || {};
    levelName = 'http';
    level = {};
    level[levelName] = that.getLevelValue('info') + 0.5;
    that.registerLevels(level);
    return typeof options.logger === 'function' || function(req, res, next) {
      var reqInfo
        , doLog = function(res) {
        var contentLength = res.get('content-length');
        log.http('%s - %s %s - %s - %s',
          reqInfo.ip, reqInfo.method, reqInfo.url,
          typeof res.statusCode !== 'undefined' ? res.statusCode : '?',
          typeof contentLength !== 'undefined' ? contentLength : '?'
        );
      };
      reqInfo = {
        ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress,
        method: req.method,
        url: req.url
      };
      if (options.logBefore) {
        doLog(res);
      } else {
        onFinished(res, function(err, res) {
          doLog(res);
        });
      }
      next();
    };
  },
  flush: function(cb) {
    if (typeof that.config.out === 'object' && that.config.out instanceof fs.WriteStream) {
      that.config.out.end(cb);
    }
  },
  getCaller: function() {
    var stackTrace = require('stack-trace')
      , trace = stackTrace.get()
      , callsite = trace[Math.min(2, trace.length)];
    return {
      file: callsite.getFileName(),
      line: callsite.getLineNumber()
    };
  },
  getLevel: function() {
    return that.config.level;
  },
  getLevels: function() {
    return _
      .keys(that.config.levels)
      .sort(function(a, b) {
        return that.config.levels[a] - that.config.levels[b];
      });
  },
  getLevelsMaxLength: function() {
    return that.config.levelsMaxLength;
  },
  getLevelValue: function(level) {
    level = that.normalizeLevel(level);
    return that.config.levels.hasOwnProperty(level) ? that.config.levels[level] : null;
  },
  getLogMethodName: function(level) {
    return _.camelCase(level);
  },
  hasLevel: function(level) {
    return _.has(that.config.levels, that.normalizeLevel(level));
  },
  init: function(options) {
    options = options || {};
    that.config = _.extend(_.cloneDeep(that.defaults), _.omit(options, 'extraLevels'));
    if (process.env['HW_LOG_COLORS']) {
      that.config.colors = process.env['HW_LOG_COLORS'] === 'true';
    }
    that.chalk = new chalk.constructor({ enabled: typeof that.config.colors === 'boolean' ? that.config.colors : true });
    if (!that.config.format) {
      that.config.formatFile = path.resolve(path.join(__dirname, '..', 'templates'), that.config.formatFile || 'default.tpl');
      if (!path.extname(that.config.formatFile)) {
        that.config.formatFile = that.config.formatFile + '.tpl';
      }
      that.config.template.filename = that.config.formatFile;
      that.config.format = fs.readFileSync(that.config.formatFile, 'utf8');
    }
    if (typeof that.config.format === 'function') {
      that.buildLogMessage = that.render = that.config.format;
    } else if (typeof that.config.format === 'string') {
      that.render = template(that.config.format, that.config.template);
      that.buildLogMessage = function(data) {
        return that.render({
          data: data,
          chalk: that.chalk,
          util: util,
          cluster: cluster,
          path: path,
          config: that.config
        });
      };
    } else {
      throw new Error('format not supported');
    }
    if (typeof that.config.out === 'string') {
      that.config.out = fs.createWriteStream(that.config.out);
    }
    if (typeof that.config.out === 'object') {
      if (that.config.out instanceof fs.WriteStream) {
        that.sendLog = function(data) {
          that.config.out.write(data + '\n', 'utf8');
        };
      } else if (that.config.out instanceof events.EventEmitter) {
        that.sendLog = function(data) {
          that.config.out.emit('data', data + '\n');
        };
      } else {
        throw new Error('Output object instance not supported');
      }
    } else if (typeof that.config.out === 'function') {
      that.sendLog = that.config.out;
    } else {
      throw new Error('Output not supported');
    }
    if (options.extraLevels) {
      that.registerLevels(options.extraLevels);
    } else {
      that.resetLevels();
    }
    if (process.env['HW_LOG_LEVEL'] && that.hasLevel(process.env['HW_LOG_LEVEL'])) {
      that.config.level = process.env['HW_LOG_LEVEL'];
    }
    that.setLevel(that.config.level);
  },
  isEnabled: function(level) {
    return that.hasLevel(level) && that.getLevelValue(level) <= that.config.levelValue;
  },
  normalizeLevel: function(level) {
    return _.snakeCase(level).toUpperCase();
  },
  registerLevels: function(levels) {
    _.forOwn(levels, function(index, key) {
      that.config.levels[that.normalizeLevel(key)] = levels[key];
    });
    that.resetLevels();
  },
  resetLevels: function() {
    _.forOwn(log, function(n, key) {
      delete log[key];
    });
    if (Array.isArray(that.config.levels)) {
      that.config.levels = that.config.levels.reduce(function(result, level, index) {
        result[that.normalizeLevel(level)] = index + 1;
        return result;
      }, {});
    }
    that.setLevel();
    that.config.levelValue = that.getLevelValue(that.config.level);
    that.config.levelsMaxLength = _.max(_.map(_.keys(that.config.levels), 'length'));
    _.keys(_.extend(that.config.levels, { NONE: 0, ALL: Number.MAX_VALUE }))
      .sort(function(a, b) {
        return that.getLevelValue(a) - that.getLevelValue(b);
      })
      .forEach(function(level, index) {
        level = that.normalizeLevel(level);
        if (level === 'NONE') {
          that.config.levels[level] = 0;
        } else if (level === 'ALL') {
          that.config.levels[level] = Number.MAX_VALUE;
        } else {
          that.config.levels[level] = index;
          that.resetLogMethod(level, that.isEnabled(level));
        }
      });
  },
  resetLogMethod: function(level, enabled) {
    var methodName = _.camelCase(level);
    log[methodName] = that[methodName] = enabled ? that.buildLogMethod(that.normalizeLevel(level)) : _.noop;
  },
  setLevel: function(level) {
    if (typeof level === 'number') {
      level = _.findKey(that.config.levels, function(value) {
        return value === level;
      });
    }
    if (level) {
      if (!that.hasLevel(level)) {
        throw new Error(util.format('log level %s is not supported', level));
      }
      that.config.level = level;
    }
    that.config.level = that.normalizeLevel(that.config.level);
    that.config.levelValue = that.getLevelValue(that.config.level);
    _.forOwn(that.config.levels, function(index, level) {
      if (level !== 'NONE' && level !== 'ALL') {
        that.resetLogMethod(level, that.enabledLevels[level.toLowerCase()] = that.isEnabled(level));
      }
    });
  }
};

logger = {
  init: that.init,
  express: that.express,
  flush: that.flush,
  getLevel: that.getLevel,
  getLevels: that.getLevels,
  getLevelsMaxLength: that.getLevelsMaxLength,
  getLevelValue: that.getLevelValue,
  getLogMethodName: that.getLogMethodName,
  isEnabled: that.isEnabled,
  enabled: that.isEnabled,
  enabledLevels: that.enabledLevels,
  log: log,
  registerLevels: that.registerLevels,
  setLevel: that.setLevel
};

that.init();

module.exports = logger;<commit_msg>remove ending line feed from template<commit_after>'use strict';

var util = require('util')
  , cluster = require('cluster')
  , path = require('path')
  , fs = require('fs')
  , _ = require('lodash')
  , template = require('lodash.template')
  , events = require('events')
  , moment = require('moment')
  , chalk = require('chalk')
  , log = {}
  , that, logger;

that = {
  defaults: {
    levels: { NONE: 0, ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4, TRACE: 5, ALL: Number.MAX_VALUE },
    level: process.env['NODE_ENV'] === 'production' ? 'WARN' : 'INFO',
    template: {},
    out: console.log,
    caller: true,
    useArgFunction: false
  },
  enabledLevels: {},
  lastTime: Date.now(),
  buildLogMethod: function(level) {
    return function() {
      var args = Array.prototype.slice.call(arguments)
        , data, caller;
      if (that.config.caller) {
        try {
          caller = that.getCaller();
        } catch (err) {
          log.warn(err.stack);
          caller = null;
        }
      }
      if (that.config.useArgFunction) {
        args = args.map(function(arg) {
          return typeof arg === 'function' ? arg() : arg;
        });
      }
      data = {
        time: moment(),
        lastTime: that.lastTime,
        level: level,
        levelValue: that.getLevelValue(level),
        args: args,
        caller: caller
      };
      that.lastTime = data.time;
      that.sendLog(that.buildLogMessage(data));
    };
  },
  express: function(options) {
    var onFinished = require('on-finished')
      , levelName, level;
    options = options || {};
    levelName = 'http';
    level = {};
    level[levelName] = that.getLevelValue('info') + 0.5;
    that.registerLevels(level);
    return typeof options.logger === 'function' || function(req, res, next) {
      var reqInfo
        , doLog = function(res) {
        var contentLength = res.get('content-length');
        log.http('%s - %s %s - %s - %s',
          reqInfo.ip, reqInfo.method, reqInfo.url,
          typeof res.statusCode !== 'undefined' ? res.statusCode : '?',
          typeof contentLength !== 'undefined' ? contentLength : '?'
        );
      };
      reqInfo = {
        ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress,
        method: req.method,
        url: req.url
      };
      if (options.logBefore) {
        doLog(res);
      } else {
        onFinished(res, function(err, res) {
          doLog(res);
        });
      }
      next();
    };
  },
  flush: function(cb) {
    if (typeof that.config.out === 'object' && that.config.out instanceof fs.WriteStream) {
      that.config.out.end(cb);
    }
  },
  getCaller: function() {
    var stackTrace = require('stack-trace')
      , trace = stackTrace.get()
      , callsite = trace[Math.min(2, trace.length)];
    return {
      file: callsite.getFileName(),
      line: callsite.getLineNumber()
    };
  },
  getLevel: function() {
    return that.config.level;
  },
  getLevels: function() {
    return _
      .keys(that.config.levels)
      .sort(function(a, b) {
        return that.config.levels[a] - that.config.levels[b];
      });
  },
  getLevelsMaxLength: function() {
    return that.config.levelsMaxLength;
  },
  getLevelValue: function(level) {
    level = that.normalizeLevel(level);
    return that.config.levels.hasOwnProperty(level) ? that.config.levels[level] : null;
  },
  getLogMethodName: function(level) {
    return _.camelCase(level);
  },
  hasLevel: function(level) {
    return _.has(that.config.levels, that.normalizeLevel(level));
  },
  init: function(options) {
    options = options || {};
    that.config = _.extend(_.cloneDeep(that.defaults), _.omit(options, 'extraLevels'));
    if (process.env['HW_LOG_COLORS']) {
      that.config.colors = process.env['HW_LOG_COLORS'] === 'true';
    }
    that.chalk = new chalk.constructor({ enabled: typeof that.config.colors === 'boolean' ? that.config.colors : true });
    if (!that.config.format) {
      that.config.formatFile = path.resolve(path.join(__dirname, '..', 'templates'), that.config.formatFile || 'default.tpl');
      if (!path.extname(that.config.formatFile)) {
        that.config.formatFile = that.config.formatFile + '.tpl';
      }
      that.config.template.filename = that.config.formatFile;
      that.config.format = fs.readFileSync(that.config.formatFile, 'utf8').replace(/[\n]*$/, '');
    }
    if (typeof that.config.format === 'function') {
      that.buildLogMessage = that.render = that.config.format;
    } else if (typeof that.config.format === 'string') {
      that.render = template(that.config.format, that.config.template);
      that.buildLogMessage = function(data) {
        return that.render({
          data: data,
          chalk: that.chalk,
          util: util,
          cluster: cluster,
          path: path,
          config: that.config
        });
      };
    } else {
      throw new Error('format not supported');
    }
    if (typeof that.config.out === 'string') {
      that.config.out = fs.createWriteStream(that.config.out);
    }
    if (typeof that.config.out === 'object') {
      if (that.config.out instanceof fs.WriteStream) {
        that.sendLog = function(data) {
          that.config.out.write(data + '\n', 'utf8');
        };
      } else if (that.config.out instanceof events.EventEmitter) {
        that.sendLog = function(data) {
          that.config.out.emit('data', data + '\n');
        };
      } else {
        throw new Error('Output object instance not supported');
      }
    } else if (typeof that.config.out === 'function') {
      that.sendLog = that.config.out;
    } else {
      throw new Error('Output not supported');
    }
    if (options.extraLevels) {
      that.registerLevels(options.extraLevels);
    } else {
      that.resetLevels();
    }
    if (process.env['HW_LOG_LEVEL'] && that.hasLevel(process.env['HW_LOG_LEVEL'])) {
      that.config.level = process.env['HW_LOG_LEVEL'];
    }
    that.setLevel(that.config.level);
  },
  isEnabled: function(level) {
    return that.hasLevel(level) && that.getLevelValue(level) <= that.config.levelValue;
  },
  normalizeLevel: function(level) {
    return _.snakeCase(level).toUpperCase();
  },
  registerLevels: function(levels) {
    _.forOwn(levels, function(index, key) {
      that.config.levels[that.normalizeLevel(key)] = levels[key];
    });
    that.resetLevels();
  },
  resetLevels: function() {
    _.forOwn(log, function(n, key) {
      delete log[key];
    });
    if (Array.isArray(that.config.levels)) {
      that.config.levels = that.config.levels.reduce(function(result, level, index) {
        result[that.normalizeLevel(level)] = index + 1;
        return result;
      }, {});
    }
    that.setLevel();
    that.config.levelValue = that.getLevelValue(that.config.level);
    that.config.levelsMaxLength = _.max(_.map(_.keys(that.config.levels), 'length'));
    _.keys(_.extend(that.config.levels, { NONE: 0, ALL: Number.MAX_VALUE }))
      .sort(function(a, b) {
        return that.getLevelValue(a) - that.getLevelValue(b);
      })
      .forEach(function(level, index) {
        level = that.normalizeLevel(level);
        if (level === 'NONE') {
          that.config.levels[level] = 0;
        } else if (level === 'ALL') {
          that.config.levels[level] = Number.MAX_VALUE;
        } else {
          that.config.levels[level] = index;
          that.resetLogMethod(level, that.isEnabled(level));
        }
      });
  },
  resetLogMethod: function(level, enabled) {
    var methodName = _.camelCase(level);
    log[methodName] = that[methodName] = enabled ? that.buildLogMethod(that.normalizeLevel(level)) : _.noop;
  },
  setLevel: function(level) {
    if (typeof level === 'number') {
      level = _.findKey(that.config.levels, function(value) {
        return value === level;
      });
    }
    if (level) {
      if (!that.hasLevel(level)) {
        throw new Error(util.format('log level %s is not supported', level));
      }
      that.config.level = level;
    }
    that.config.level = that.normalizeLevel(that.config.level);
    that.config.levelValue = that.getLevelValue(that.config.level);
    _.forOwn(that.config.levels, function(index, level) {
      if (level !== 'NONE' && level !== 'ALL') {
        that.resetLogMethod(level, that.enabledLevels[level.toLowerCase()] = that.isEnabled(level));
      }
    });
  }
};

logger = {
  init: that.init,
  express: that.express,
  flush: that.flush,
  getLevel: that.getLevel,
  getLevels: that.getLevels,
  getLevelsMaxLength: that.getLevelsMaxLength,
  getLevelValue: that.getLevelValue,
  getLogMethodName: that.getLogMethodName,
  isEnabled: that.isEnabled,
  enabled: that.isEnabled,
  enabledLevels: that.enabledLevels,
  log: log,
  registerLevels: that.registerLevels,
  setLevel: that.setLevel
};

that.init();

module.exports = logger;<|endoftext|>


<commit_before>/**
 *  LogDNA NPM Module
 *      - supports levels (Debug, Info, Notice, Warning, Error, Critical, Alert, Emerge)
 *      - supports winston
 */

var sizeof = require('object-sizeof');
var configs = require('./configs');
var request = require('request');
var util = require('util');
var debug = util.debuglog('logdna');
var _ = require('lodash');
var os = require('os');

function isInt(value) {
    return !isNaN(value) && (function(x) { return (x | 0) === x; })(parseFloat(value));
}

function checkParam(param, name, optional) {
    if (optional && !param) return;
    if (!param || !_.isString(param)) {
        throw new Error(name + ' is undefined or not passed as a String');
    } else if (param.length > configs.MAX_INPUT_LENGTH) {
        throw new Error(name + ' cannot be longer than ' + configs.MAX_INPUT_LENGTH + ' chars');
    }
}

function Logger(key, options) {
    if (!(this instanceof Logger)) return new Logger(key, options);
    checkParam(key, 'LogDNA API Key', false);
    checkParam(options.hostname, 'Hostname', true);
    checkParam(options.mac, 'MAC Address', true);
    checkParam(options.ip, 'IP Address', true);
    checkParam(options.level, 'Level', true);
    checkParam(options.app, 'App', true);
    if (options.timeout) {
        if (!isInt(options.timeout)) {
            throw new Error('Timeout must be an Integer');
        }
        if (options.timeout > configs.MAX_REQUEST_TIMEOUT) {
            throw new Error('Timeout cannot be longer than' + configs.MAX_REQUEST_TIMEOUT);
        }
    }
    if (options.mac && !configs.MAC_ADDR_CHECK.test(options.mac)) {
        throw new Error('Invalid MAC Address format');
    }
    if (options.ip && !configs.IP_ADDR_CHECK.test(options.ip)) {
        throw new Error('Invalid IP Address format');
    }

    this._flushLimit = configs.FLUSH_BYTE_LIMIT;
    this._url = configs.LOGDNA_URL;
    var protocol = configs.AGENT_PROTOCOL;
    this._agent = new protocol(configs.AGENT_SETTING);
    this._bufByteLength = 0;
    this._buf = [];
    this._err;

    this.source = {
        hostname: options.hostname || os.hostname(),
        app: options.app || 'default',
        level: options.level || 'Info'
    };

    this._req = request.defaults({
        auth: { username: key },
        agent: this._agent,
        headers: configs.DEFAULT_REQUEST_HEADER,
        qs: {
            hostname: this.source.hostname,
            mac: options.mac,
            ip: options.ip
        },
        timeout: options.timeout || configs.DEFAULT_REQUEST_TIMEOUT
    });
}

Logger.prototype.log = function(statement, opts) {
    this._err = false;
    var message = {
        timestamp: Date.now(),
        line: statement,
        level: this.source.level,
        app: this.source.app
    };
    if (opts) {
        if (_.isString(opts)) {
            if (opts.length > configs.MAX_INPUT_LENGTH) {
                debug('Level had more than ' + configs.MAX_INPUT_LENGTH + ' chars, was truncated');
                opts = opts.substring(0, configs.MAX_INPUT_LENGTH);
            }
            message.level = opts;
        } else {
            if (typeof opts != 'object') {
                this._err = true;
                debug('Can only pass a String or JSON object as additional parameter');
            }
            message.level = opts.level || message.level;
            message.app = opts.app || message.app;
        }
    }
    if (this._err) {
        return this._err;
    }
    this._bufferLog(message);
};

Logger.prototype._bufferLog = function(message) {
    if (!message || !message.line) return;

    if (message.line.length > configs.MAX_LINE_LENGTH) {
        message.line = message.line.substring(0, configs.MAX_LINE_LENGTH) + ' (cut off, too long...)';
        debug('Line was longer than ' + configs.MAX_LINE_LENGTH + ' chars and was truncated.');
    }

    this._bufByteLength += sizeof(message);
    // this._bufLength += getBytes(JSON.stringify(message));

    this._buf.push(message);

    if (this._bufByteLength >= this._flushLimit) {
        this._flush();
        return;
    }

    if (!this._flusher) {
        this._flusher = setTimeout(_.bind(this._flush, this), configs.FLUSH_INTERVAL);
    }

};

Logger.prototype._flush = function() {
    if (!this._req || this._buf.length === 0) { return; }

    var sendbuf = { e: 'ls', ls: this._buf };
    var data = JSON.stringify(sendbuf);

    this._bufByteLength = 0;
    this._buf.length = 0;

    this._req.post(this._url, {
        body: data,
        qs: { now: Date.now() }
    }, function(err) {
        if (err) { debug('Encountered an Error in POST Request: %j', err); }
    });
    clearTimeout(this._flusher);
    this._flusher = null;
};

/*
 *  Populate short-hand for each supported Log Level
 */
_.forEach(configs.LOG_LEVELS, function(level) {
    var l = level.toLowerCase();
    Logger.prototype[l] = function(statement, opts) {
        opts = opts || {};
        opts.level = level;
        this.log(statement, opts);
    };
});

exports.Logger = Logger;
exports.createLogger = function(key, options) {
    return new Logger(key, options);
};
<commit_msg>Release 1.0.5 Accepts objects using util.inspect<commit_after>/**
 *  LogDNA NPM Module
 *      - supports levels (Debug, Info, Notice, Warning, Error, Critical, Alert, Emerge)
 *      - supports winston
 */

var sizeof = require('object-sizeof');
var configs = require('./configs');
var request = require('request');
var util = require('util');
var debug = util.debuglog('logdna');
var _ = require('lodash');
var os = require('os');

function isInt(value) {
    return !isNaN(value) && (function(x) { return (x | 0) === x; })(parseFloat(value));
}

function checkParam(param, name, optional) {
    if (optional && !param) return;
    if (!param || !_.isString(param)) {
        throw new Error(name + ' is undefined or not passed as a String');
    } else if (param.length > configs.MAX_INPUT_LENGTH) {
        throw new Error(name + ' cannot be longer than ' + configs.MAX_INPUT_LENGTH + ' chars');
    }
}

function Logger(key, options) {
    if (!(this instanceof Logger)) return new Logger(key, options);
    checkParam(key, 'LogDNA API Key', false);
    checkParam(options.hostname, 'Hostname', true);
    checkParam(options.mac, 'MAC Address', true);
    checkParam(options.ip, 'IP Address', true);
    checkParam(options.level, 'Level', true);
    checkParam(options.app, 'App', true);
    if (options.timeout) {
        if (!isInt(options.timeout)) {
            throw new Error('Timeout must be an Integer');
        }
        if (options.timeout > configs.MAX_REQUEST_TIMEOUT) {
            throw new Error('Timeout cannot be longer than' + configs.MAX_REQUEST_TIMEOUT);
        }
    }
    if (options.mac && !configs.MAC_ADDR_CHECK.test(options.mac)) {
        throw new Error('Invalid MAC Address format');
    }
    if (options.ip && !configs.IP_ADDR_CHECK.test(options.ip)) {
        throw new Error('Invalid IP Address format');
    }

    this._flushLimit = configs.FLUSH_BYTE_LIMIT;
    this._url = configs.LOGDNA_URL;
    var protocol = configs.AGENT_PROTOCOL;
    this._agent = new protocol(configs.AGENT_SETTING);
    this._bufByteLength = 0;
    this._buf = [];
    this._err;

    this.source = {
        hostname: options.hostname || os.hostname(),
        app: options.app || 'default',
        level: options.level || 'Info'
    };

    this._req = request.defaults({
        auth: { username: key },
        agent: this._agent,
        headers: configs.DEFAULT_REQUEST_HEADER,
        qs: {
            hostname: this.source.hostname,
            mac: options.mac,
            ip: options.ip
        },
        timeout: options.timeout || configs.DEFAULT_REQUEST_TIMEOUT
    });
}

Logger.prototype.log = function(statement, opts) {
    this._err = false;
    if (typeof statement === 'object') {
        statement = util.inspect(statement);
    }
    var message = {
        timestamp: Date.now(),
        line: statement,
        level: this.source.level,
        app: this.source.app
    };
    if (opts) {
        if (_.isString(opts)) {
            if (opts.length > configs.MAX_INPUT_LENGTH) {
                debug('Level had more than ' + configs.MAX_INPUT_LENGTH + ' chars, was truncated');
                opts = opts.substring(0, configs.MAX_INPUT_LENGTH);
            }
            message.level = opts;
        } else {
            if (typeof opts != 'object') {
                this._err = true;
                debug('Can only pass a String or JSON object as additional parameter');
            }
            message.level = opts.level || message.level;
            message.app = opts.app || message.app;
        }
    }
    if (this._err) {
        return this._err;
    }
    this._bufferLog(message);
};

Logger.prototype._bufferLog = function(message) {
    if (!message || !message.line) return;

    if (message.line.length > configs.MAX_LINE_LENGTH) {
        message.line = message.line.substring(0, configs.MAX_LINE_LENGTH) + ' (cut off, too long...)';
        debug('Line was longer than ' + configs.MAX_LINE_LENGTH + ' chars and was truncated.');
    }

    this._bufByteLength += sizeof(message);
    // this._bufLength += getBytes(JSON.stringify(message));

    this._buf.push(message);

    if (this._bufByteLength >= this._flushLimit) {
        this._flush();
        return;
    }

    if (!this._flusher) {
        this._flusher = setTimeout(_.bind(this._flush, this), configs.FLUSH_INTERVAL);
    }

};

Logger.prototype._flush = function() {
    if (!this._req || this._buf.length === 0) { return; }

    var sendbuf = { e: 'ls', ls: this._buf };
    var data = JSON.stringify(sendbuf);

    this._bufByteLength = 0;
    this._buf.length = 0;

    this._req.post(this._url, {
        body: data,
        qs: { now: Date.now() }
    }, function(err) {
        if (err) { debug('Encountered an Error in POST Request: %j', err); }
    });
    clearTimeout(this._flusher);
    this._flusher = null;
};

/*
 *  Populate short-hand for each supported Log Level
 */
_.forEach(configs.LOG_LEVELS, function(level) {
    var l = level.toLowerCase();
    Logger.prototype[l] = function(statement, opts) {
        opts = opts || {};
        opts.level = level;
        this.log(statement, opts);
    };
});

exports.Logger = Logger;
exports.createLogger = function(key, options) {
    return new Logger(key, options);
};
<|endoftext|>


<commit_before>'use strict';

var defineProperty = Object.defineProperty
  , copy           = require('es5-ext/lib/Array/prototype/copy')
  , partial        = require('es5-ext/lib/Function/prototype/partial')
  , dcr            = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend-properties')
  , ee             = require('event-emitter');

var o = ee(exports = {
	init: function () {
		this.msg = defineProperty([], 'copy', dcr('', copy));
		this.passed = [];
		this.errored = [];
		this.failed = [];
		this.started = new Date();
		return this;
	},
	in: function (msg) {
		this.msg.push(msg);
	},
	out: function () {
		this.msg.pop();
	},
	log: function (type, data) {
		var o = { type: type, time: new Date(), data: data, msg: this.msg.copy() };
		this.push(o);
		this[type + 'ed'].push(o);
		this.emit('data', o);
	},
	end: function () {
		this.emit('end');
	}
});

o.log.partial = partial;
o.error = o.log.partial('error');
o.pass = o.log.partial('pass');
o.fail = o.log.partial('fail');

module.exports = function () {
	return extend([], o).init();
};
<commit_msg>Do not use descriptors when it's not needed<commit_after>'use strict';

var copy           = require('es5-ext/lib/Array/prototype/copy')
  , partial        = require('es5-ext/lib/Function/prototype/partial')
  , dcr            = require('es5-ext/lib/Object/descriptor')
  , extend         = require('es5-ext/lib/Object/extend-properties')
  , ee             = require('event-emitter');

var o = ee(exports = {
	init: function () {
		(this.msg = []).copy = copy;
		this.passed = [];
		this.errored = [];
		this.failed = [];
		this.started = new Date();
		return this;
	},
	in: function (msg) {
		this.msg.push(msg);
	},
	out: function () {
		this.msg.pop();
	},
	log: function (type, data) {
		var o = { type: type, time: new Date(), data: data, msg: this.msg.copy() };
		this.push(o);
		this[type + 'ed'].push(o);
		this.emit('data', o);
	},
	end: function () {
		this.emit('end');
	}
});

o.log.partial = partial;
o.error = o.log.partial('error');
o.pass = o.log.partial('pass');
o.fail = o.log.partial('fail');

module.exports = function () {
	return extend([], o).init();
};
<|endoftext|>


<commit_before>define([
    "./base/Logger"
], function(Logger) {
    var cache = {};

    return {
        module: 'logger',
        load: function(id, require, load) {
            var parts = id.split("!"),
                    category = parts[0] || 'elenajs',
                    result;

            if (category in cache) {
                result = cache[category];
            } else {

                result = new Logger({category:category});
                cache[category] = result;
            }

            load(result);
        }
    };
});
<commit_msg>case sensitivity fix for mac and win2<commit_after>define([
    "./_base/Logger"
], function(Logger) {
    var cache = {};

    return {
        module: 'logger',
        load: function(id, require, load) {
            var parts = id.split("!"),
                    category = parts[0] || 'elenajs',
                    result;

            if (category in cache) {
                result = cache[category];
            } else {

                result = new Logger({category:category});
                cache[category] = result;
            }

            load(result);
        }
    };
});
<|endoftext|>


<commit_before>'use strict';
var lazy = require('lazy-cache')(require);
var bach = lazy('bach');
var noop = require('./noop');

/**
 * Lookup a task and it's dependencies and compose
 * the final task function.
 *
 * ```js
 * // bind the composer to the lookup call
 * var fn = lookup.call(this, 'default');
 * ```
 *
 * @param  {String} `name` Name of the task to lookup
 * @return {Function} Composed function comprising the task and it's dependenices
 */

module.exports = function lookup (name) {
  var task = this.tasks[name];
  if (!task) {
    throw new Error('Invalid task `' + name + '`. Register `' + name + '` before calling run.');
  }
  var flow = bach()[task.options.flow || 'series'];
  var deps = task.deps.map(lookup.bind(this));
  return flow.apply(bach(), deps.concat([task.run.bind(task)]));
};
<commit_msg>unused dep<commit_after>'use strict';
var lazy = require('lazy-cache')(require);
var bach = lazy('bach');

/**
 * Lookup a task and it's dependencies and compose
 * the final task function.
 *
 * ```js
 * // bind the composer to the lookup call
 * var fn = lookup.call(this, 'default');
 * ```
 *
 * @param  {String} `name` Name of the task to lookup
 * @return {Function} Composed function comprising the task and it's dependenices
 */

module.exports = function lookup (name) {
  var task = this.tasks[name];
  if (!task) {
    throw new Error('Invalid task `' + name + '`. Register `' + name + '` before calling run.');
  }
  var flow = bach()[task.options.flow || 'series'];
  var deps = task.deps.map(lookup.bind(this));
  return flow.apply(bach(), deps.concat([task.run.bind(task)]));
};
<|endoftext|>