<commit_before>/**
 *
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * 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.
 */
APP.Main = (function() {

  var LAZY_LOAD_THRESHOLD = 300;
  var $ = document.querySelector.bind(document);

  var stories = null;
  var storyStart = 0;
  var count = 100;
  var main = $('main');
  var inDetails = false;
  var storyLoadCount = 0;
  var localeData = {
    data: {
      intl: {
        locales: 'en-US'
      }
    }
  };

  var tmplStory = $('#tmpl-story').textContent;
  var tmplStoryDetails = $('#tmpl-story-details').textContent;
  var tmplStoryDetailsComment = $('#tmpl-story-details-comment').textContent;

  if (typeof HandlebarsIntl !== 'undefined') {
    HandlebarsIntl.registerWith(Handlebars);
  } else {

    // Remove references to formatRelative, because Intl isn't supported.
    var intlRelative = /, {{ formatRelative time }}/;
    tmplStory = tmplStory.replace(intlRelative, '');
    tmplStoryDetails = tmplStoryDetails.replace(intlRelative, '');
    tmplStoryDetailsComment = tmplStoryDetailsComment.replace(intlRelative, '');
  }

  var storyTemplate =
      Handlebars.compile(tmplStory);
  var storyDetailsTemplate =
      Handlebars.compile(tmplStoryDetails);
  var storyDetailsCommentTemplate =
      Handlebars.compile(tmplStoryDetailsComment);

  /**
   * As every single story arrives in shove its
   * content in at that exact moment. Feels like something
   * that should really be handled more delicately, and
   * probably in a requestAnimationFrame callback.
   */
  function onStoryData (key, details) {

    var storyElement = document.querySelector('#s-' + key);

    if (storyElement !== null) {
      details.time *= 1000;
      var story = storyElement;
      var html = storyTemplate(details);
      story.innerHTML = html;
      story.addEventListener('click', onStoryClick.bind(this, details));
      story.classList.add('clickable');

      // Tick down. When zero we can batch in the next load.
      storyLoadCount--;
    }

    // Colorize on complete.
    if (storyLoadCount === 0)
      colorizeAndScaleStories();
  }

  function onStoryClick(details) {

    var storyDetails = $('sd-' + details.id);

    // Wait a little time then show the story details.
    setTimeout(showStory.bind(this, details.id), 60);

    // Create and append the story. A visual change...
    // perhaps that should be in a requestAnimationFrame?
    // And maybe, since they're all the same, I don't
    // need to make a new element every single time? I mean,
    // it inflates the DOM and I can only see one at once.
    if (!storyDetails) {

      if (details.url)
        details.urlobj = new URL(details.url);

      var comment;
      var commentsElement;
      var storyHeader;
      var storyContent;

      var storyDetailsHtml = storyDetailsTemplate(details);
      var kids = details.kids;
      var commentHtml = storyDetailsCommentTemplate({
        by: '', text: 'Loading comment...'
      });

      storyDetails = document.createElement('section');
      storyDetails.setAttribute('id', 'sd-' + details.id);
      storyDetails.classList.add('story-details');
      storyDetails.innerHTML = storyDetailsHtml;

      document.body.appendChild(storyDetails);

      commentsElement = storyDetails.querySelector('.js-comments');
      storyHeader = storyDetails.querySelector('.js-header');
      storyContent = storyDetails.querySelector('.js-content');

      var closeButton = storyDetails.querySelector('.js-close');
      closeButton.addEventListener('click', hideStory.bind(this, details.id));

      var headerHeight = storyHeader.getBoundingClientRect().height;
      storyContent.style.paddingTop = headerHeight + 'px';

      if (typeof kids === 'undefined')
        return;

      for (var k = 0; k < kids.length; k++) {

        comment = document.createElement('aside');
        comment.setAttribute('id', 'sdc-' + kids[k]);
        comment.classList.add('story-details__comment');
        comment.innerHTML = commentHtml;
        commentsElement.appendChild(comment);

        // Update the comment with the live data.
        APP.Data.getStoryComment(kids[k], function(commentDetails) {

          commentDetails.time *= 1000;

          var comment = commentsElement.querySelector(
              '#sdc-' + commentDetails.id);
          comment.innerHTML = storyDetailsCommentTemplate(
              commentDetails,
              localeData);
        });
      }
    }

  }

  function showStory(id) {

    if (inDetails)
      return;

    inDetails = true;

    var storyDetails = $('#sd-' + id);
    var left = null;

    if (!storyDetails)
      return;

    document.body.classList.add('details-active');
    storyDetails.style.opacity = 1;

    function animate () {

      // Find out where it currently is.
      var storyDetailsPosition = storyDetails.getBoundingClientRect();

      // Set the left value if we don't have one already.
      if (left === null)
        left = storyDetailsPosition.left;

      // Now figure out where it needs to go.
      left += (0 - storyDetailsPosition.left) * 0.1;

      // Set up the next bit of the animation if there is more to do.
      if (Math.abs(left) > 0.5)
        setTimeout(animate, 4);
      else
        left = 0;

      // And update the styles. Wait, is this a read-write cycle?
      // I hope I don't trigger a forced synchronous layout!
      storyDetails.style.left = left + 'px';
    }

    // We want slick, right, so let's do a setTimeout
    // every few milliseconds. That's going to keep
    // it all tight. Or maybe we're doing visual changes
    // and they should be in a requestAnimationFrame
    setTimeout(animate, 4);
  }

  function hideStory(id) {

    if (!inDetails)
      return;

    var storyDetails = $('#sd-' + id);
    var left = 0;

    document.body.classList.remove('details-active');
    storyDetails.style.opacity = 0;

    function animate () {

      // Find out where it currently is.
      var mainPosition = main.getBoundingClientRect();
      var storyDetailsPosition = storyDetails.getBoundingClientRect();
      var target = mainPosition.width + 100;

      // Now figure out where it needs to go.
      left += (target - storyDetailsPosition.left) * 0.1;

      // Set up the next bit of the animation if there is more to do.
      if (Math.abs(left - target) > 0.5) {
        setTimeout(animate, 4);
      } else {
        left = target;
        inDetails = false;
      }

      // And update the styles. Wait, is this a read-write cycle?
      // I hope I don't trigger a forced synchronous layout!
      storyDetails.style.left = left + 'px';
    }

    // We want slick, right, so let's do a setTimeout
    // every few milliseconds. That's going to keep
    // it all tight. Or maybe we're doing visual changes
    // and they should be in a requestAnimationFrame
    setTimeout(animate, 4);
  }

  /**
   * Does this really add anything? Can we do this kind
   * of work in a cheaper way?
   */
  function colorizeAndScaleStories() {

    var storyElements = document.querySelectorAll('.story');

    // It does seem awfully broad to change all the
    // colors every time!
    for (var s = 0; s < storyElements.length; s++) {

      var story = storyElements[s];
      var score = story.querySelector('.story__score');
      var title = story.querySelector('.story__title');

      // Base the scale on the y position of the score.
      var height = main.offsetHeight;
      var mainPosition = main.getBoundingClientRect();
      var scoreLocation = score.getBoundingClientRect().top -
          document.body.getBoundingClientRect().top;
      var scale = Math.min(1, 1 - (0.05 * ((scoreLocation - 170) / height)));
      var opacity = Math.min(1, 1 - (0.5 * ((scoreLocation - 170) / height)));

      score.style.width = (scale * 40) + 'px';
      score.style.height = (scale * 40) + 'px';
      score.style.lineHeight = (scale * 40) + 'px';

      // Now figure out how wide it is and use that to saturate it.
      scoreLocation = score.getBoundingClientRect();
      var saturation = (100 * ((scoreLocation.width - 38) / 2));

      score.style.backgroundColor = 'hsl(42, ' + saturation + '%, 50%)';
      title.style.opacity = opacity;
    }
  }

  main.addEventListener('touchstart', function(evt) {

    // I just wanted to test what happens if touchstart
    // gets canceled. Hope it doesn't block scrolling on mobiles...
    if (Math.random() > 0.97) {
      evt.preventDefault();
    }

  });

  main.addEventListener('scroll', function() {

    var header = $('header');
    var headerTitles = header.querySelector('.header__title-wrapper');
    var scrollTopCapped = Math.min(70, main.scrollTop);
    var scaleString = 'scale(' + (1 - (scrollTopCapped / 300)) + ')';

    // colorizeAndScaleStories(); not ok

    header.style.height = (156 - scrollTopCapped) + 'px';
    headerTitles.style.webkitTransform = scaleString;
    headerTitles.style.transform = scaleString;

    // Add a shadow to the header.
    if (main.scrollTop > 70)
      document.body.classList.add('raised');
    else
      document.body.classList.remove('raised');

    // Check if we need to load the next batch of stories.
    var loadThreshold = (main.scrollHeight - main.offsetHeight -
        LAZY_LOAD_THRESHOLD);
    if (main.scrollTop > loadThreshold)
      loadStoryBatch();
  });

  function loadStoryBatch() {

    if (storyLoadCount > 0)
      return;

    storyLoadCount = count;

    var end = storyStart + count;
    for (var i = storyStart; i < end; i++) {

      if (i >= stories.length)
        return;

      var key = String(stories[i]);
      var story = document.createElement('div');
      story.setAttribute('id', 's-' + key);
      story.classList.add('story');
      story.innerHTML = storyTemplate({
        title: '...',
        score: '-',
        by: '...',
        time: 0
      });
      main.appendChild(story);

      APP.Data.getStoryById(stories[i], onStoryData.bind(this, key));
    }

    storyStart += count;

  }

  // Bootstrap in the stories.
  APP.Data.getTopStories(function(data) {
    stories = data;
    loadStoryBatch();
    main.classList.remove('loading');
  });

})();
<commit_msg>removed scroll event random stop on touch<commit_after>/**
 *
 * Copyright 2015 Google Inc. All rights reserved.
 *
 * 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.
 */
APP.Main = (function() {

  var LAZY_LOAD_THRESHOLD = 300;
  var $ = document.querySelector.bind(document);

  var stories = null;
  var storyStart = 0;
  var count = 100;
  var main = $('main');
  var inDetails = false;
  var storyLoadCount = 0;
  var localeData = {
    data: {
      intl: {
        locales: 'en-US'
      }
    }
  };

  var tmplStory = $('#tmpl-story').textContent;
  var tmplStoryDetails = $('#tmpl-story-details').textContent;
  var tmplStoryDetailsComment = $('#tmpl-story-details-comment').textContent;

  if (typeof HandlebarsIntl !== 'undefined') {
    HandlebarsIntl.registerWith(Handlebars);
  } else {

    // Remove references to formatRelative, because Intl isn't supported.
    var intlRelative = /, {{ formatRelative time }}/;
    tmplStory = tmplStory.replace(intlRelative, '');
    tmplStoryDetails = tmplStoryDetails.replace(intlRelative, '');
    tmplStoryDetailsComment = tmplStoryDetailsComment.replace(intlRelative, '');
  }

  var storyTemplate =
      Handlebars.compile(tmplStory);
  var storyDetailsTemplate =
      Handlebars.compile(tmplStoryDetails);
  var storyDetailsCommentTemplate =
      Handlebars.compile(tmplStoryDetailsComment);

  /**
   * As every single story arrives in shove its
   * content in at that exact moment. Feels like something
   * that should really be handled more delicately, and
   * probably in a requestAnimationFrame callback.
   */
  function onStoryData (key, details) {

    var storyElement = document.querySelector('#s-' + key);

    if (storyElement !== null) {
      details.time *= 1000;
      var story = storyElement;
      var html = storyTemplate(details);
      story.innerHTML = html;
      story.addEventListener('click', onStoryClick.bind(this, details));
      story.classList.add('clickable');

      // Tick down. When zero we can batch in the next load.
      storyLoadCount--;
    }

    // Colorize on complete.
    if (storyLoadCount === 0)
      colorizeAndScaleStories();
  }

  function onStoryClick(details) {

    var storyDetails = $('sd-' + details.id);

    // Wait a little time then show the story details.
    setTimeout(showStory.bind(this, details.id), 60);

    // Create and append the story. A visual change...
    // perhaps that should be in a requestAnimationFrame?
    // And maybe, since they're all the same, I don't
    // need to make a new element every single time? I mean,
    // it inflates the DOM and I can only see one at once.
    if (!storyDetails) {

      if (details.url)
        details.urlobj = new URL(details.url);

      var comment;
      var commentsElement;
      var storyHeader;
      var storyContent;

      var storyDetailsHtml = storyDetailsTemplate(details);
      var kids = details.kids;
      var commentHtml = storyDetailsCommentTemplate({
        by: '', text: 'Loading comment...'
      });

      storyDetails = document.createElement('section');
      storyDetails.setAttribute('id', 'sd-' + details.id);
      storyDetails.classList.add('story-details');
      storyDetails.innerHTML = storyDetailsHtml;

      document.body.appendChild(storyDetails);

      commentsElement = storyDetails.querySelector('.js-comments');
      storyHeader = storyDetails.querySelector('.js-header');
      storyContent = storyDetails.querySelector('.js-content');

      var closeButton = storyDetails.querySelector('.js-close');
      closeButton.addEventListener('click', hideStory.bind(this, details.id));

      var headerHeight = storyHeader.getBoundingClientRect().height;
      storyContent.style.paddingTop = headerHeight + 'px';

      if (typeof kids === 'undefined')
        return;

      for (var k = 0; k < kids.length; k++) {

        comment = document.createElement('aside');
        comment.setAttribute('id', 'sdc-' + kids[k]);
        comment.classList.add('story-details__comment');
        comment.innerHTML = commentHtml;
        commentsElement.appendChild(comment);

        // Update the comment with the live data.
        APP.Data.getStoryComment(kids[k], function(commentDetails) {

          commentDetails.time *= 1000;

          var comment = commentsElement.querySelector(
              '#sdc-' + commentDetails.id);
          comment.innerHTML = storyDetailsCommentTemplate(
              commentDetails,
              localeData);
        });
      }
    }

  }

  function showStory(id) {

    if (inDetails)
      return;

    inDetails = true;

    var storyDetails = $('#sd-' + id);
    var left = null;

    if (!storyDetails)
      return;

    document.body.classList.add('details-active');
    storyDetails.style.opacity = 1;

    function animate () {

      // Find out where it currently is.
      var storyDetailsPosition = storyDetails.getBoundingClientRect();

      // Set the left value if we don't have one already.
      if (left === null)
        left = storyDetailsPosition.left;

      // Now figure out where it needs to go.
      left += (0 - storyDetailsPosition.left) * 0.1;

      // Set up the next bit of the animation if there is more to do.
      if (Math.abs(left) > 0.5)
        setTimeout(animate, 4);
      else
        left = 0;

      // And update the styles. Wait, is this a read-write cycle?
      // I hope I don't trigger a forced synchronous layout!
      storyDetails.style.left = left + 'px';
    }

    // We want slick, right, so let's do a setTimeout
    // every few milliseconds. That's going to keep
    // it all tight. Or maybe we're doing visual changes
    // and they should be in a requestAnimationFrame
    setTimeout(animate, 4);
  }

  function hideStory(id) {

    if (!inDetails)
      return;

    var storyDetails = $('#sd-' + id);
    var left = 0;

    document.body.classList.remove('details-active');
    storyDetails.style.opacity = 0;

    function animate () {

      // Find out where it currently is.
      var mainPosition = main.getBoundingClientRect();
      var storyDetailsPosition = storyDetails.getBoundingClientRect();
      var target = mainPosition.width + 100;

      // Now figure out where it needs to go.
      left += (target - storyDetailsPosition.left) * 0.1;

      // Set up the next bit of the animation if there is more to do.
      if (Math.abs(left - target) > 0.5) {
        setTimeout(animate, 4);
      } else {
        left = target;
        inDetails = false;
      }

      // And update the styles. Wait, is this a read-write cycle?
      // I hope I don't trigger a forced synchronous layout!
      storyDetails.style.left = left + 'px';
    }

    // We want slick, right, so let's do a setTimeout
    // every few milliseconds. That's going to keep
    // it all tight. Or maybe we're doing visual changes
    // and they should be in a requestAnimationFrame
    setTimeout(animate, 4);
  }

  /**
   * Does this really add anything? Can we do this kind
   * of work in a cheaper way?
   */
  function colorizeAndScaleStories() {

    var storyElements = document.querySelectorAll('.story');

    // It does seem awfully broad to change all the
    // colors every time!
    for (var s = 0; s < storyElements.length; s++) {

      var story = storyElements[s];
      var score = story.querySelector('.story__score');
      var title = story.querySelector('.story__title');

      // Base the scale on the y position of the score.
      var height = main.offsetHeight;
      var mainPosition = main.getBoundingClientRect();
      var scoreLocation = score.getBoundingClientRect().top -
          document.body.getBoundingClientRect().top;
      var scale = Math.min(1, 1 - (0.05 * ((scoreLocation - 170) / height)));
      var opacity = Math.min(1, 1 - (0.5 * ((scoreLocation - 170) / height)));

      score.style.width = (scale * 40) + 'px';
      score.style.height = (scale * 40) + 'px';
      score.style.lineHeight = (scale * 40) + 'px';

      // Now figure out how wide it is and use that to saturate it.
      scoreLocation = score.getBoundingClientRect();
      var saturation = (100 * ((scoreLocation.width - 38) / 2));

      score.style.backgroundColor = 'hsl(42, ' + saturation + '%, 50%)';
      title.style.opacity = opacity;
    }
  }

  main.addEventListener('scroll', function() {

    var header = $('header');
    var headerTitles = header.querySelector('.header__title-wrapper');
    var scrollTopCapped = Math.min(70, main.scrollTop);
    var scaleString = 'scale(' + (1 - (scrollTopCapped / 300)) + ')';

    // colorizeAndScaleStories(); not ok

    header.style.height = (156 - scrollTopCapped) + 'px';
    headerTitles.style.webkitTransform = scaleString;
    headerTitles.style.transform = scaleString;

    // Add a shadow to the header.
    if (main.scrollTop > 70)
      document.body.classList.add('raised');
    else
      document.body.classList.remove('raised');

    // Check if we need to load the next batch of stories.
    var loadThreshold = (main.scrollHeight - main.offsetHeight -
        LAZY_LOAD_THRESHOLD);
    if (main.scrollTop > loadThreshold)
      loadStoryBatch();
  });

  function loadStoryBatch() {

    if (storyLoadCount > 0)
      return;

    storyLoadCount = count;

    var end = storyStart + count;
    for (var i = storyStart; i < end; i++) {

      if (i >= stories.length)
        return;

      var key = String(stories[i]);
      var story = document.createElement('div');
      story.setAttribute('id', 's-' + key);
      story.classList.add('story');
      story.innerHTML = storyTemplate({
        title: '...',
        score: '-',
        by: '...',
        time: 0
      });
      main.appendChild(story);

      APP.Data.getStoryById(stories[i], onStoryData.bind(this, key));
    }

    storyStart += count;

  }

  // Bootstrap in the stories.
  APP.Data.getTopStories(function(data) {
    stories = data;
    loadStoryBatch();
    main.classList.remove('loading');
  });

})();
<|endoftext|>


<commit_before>var pusher;
var channel;
var roomMembers = ['12345678'];
$(function(){

	// var pusher = new Pusher('b1676ed848f98417d9d5');
 //    var channel = pusher.subscribe('testtable_channel');
 //    channel.bind('debug_event', function(data) {
 //        alert(data.message);
 //    });

 //    channel.bind('userJoined', function(data) {
 //        addUserImage(data);
 //    });

});

function loadPusherMessaging(user)
{
    pusher = new Pusher('b1676ed848f98417d9d5', { authEndpoint: 'http://testtable.mykebates.com/push/auth?username='+user.username+'&name='+user.name+'&image='+user.image });
	channel = pusher.subscribe('presence-testtable');

	// Enable pusher logging - don't include this in production
    // Pusher.log = function(message) {
    //   if (window.console && window.console.log) {
    //     window.console.log(message);
    //   }
    // };

    channel.bind('pusher:subscription_succeeded', function(members) {
        addUserImage(members.me.info);

        _.each(members, function(member){
            if(member.info){
                addMemberToRoom(member);
            }
        });
        var data = {
            'members': encodeURIComponent(roomMembers)
        }
        ajaxCall('http://testtable.dev/room/setdjs', data);
    });

    pusher.connection.bind('connected', function() { //bind a function after we've connected to Pusher
        //console.log('connected');
    });

    channel.bind('debug_event', function(data) {
        alert(data.message);
    });

    channel.bind('pusher:member_added', function(member) {
        addUserImage(member.info);
    });

   channel.bind('pusher:member_removed', function(member) {
        removeUserImage(member.info);
    });

}

function addUserImage(data){
	var userImage = '

';
	$('#user_list').append(userImage);
}

function removeUserImage(data){
	$('#user_list li[data-username="user_'+data.username+'"]').fadeOut();
}

function addMemberToRoom(member){
    // have access to the member details here but just pusing the memberid for now
    roomMembers.push(member.id);
}

function ajaxCall(ajax_url, ajax_data, successCallback)
{
    $.ajax({
        type : "GET",
        url : ajax_url,
        dataType : "json",
        data: ajax_data,
        time : 10,
        success : function(msg) {
            if( msg.success ) {
                console.log('sdfsd');
            }else{
                alert(msg.errormsg);
            }
        },
        error: function(msg) {
            // log something
        }
    });
}<commit_msg>updates<commit_after>var pusher;
var channel;
var roomMembers = [];
$(function(){

	// var pusher = new Pusher('b1676ed848f98417d9d5');
 //    var channel = pusher.subscribe('testtable_channel');
 //    channel.bind('debug_event', function(data) {
 //        alert(data.message);
 //    });

 //    channel.bind('userJoined', function(data) {
 //        addUserImage(data);
 //    });

});

function loadPusherMessaging(user)
{
    pusher = new Pusher('b1676ed848f98417d9d5', { authEndpoint: 'http://testtable.mykebates.com/push/auth?username='+user.username+'&name='+user.name+'&image='+user.image });
	channel = pusher.subscribe('presence-testtable');

	// Enable pusher logging - don't include this in production
    // Pusher.log = function(message) {
    //   if (window.console && window.console.log) {
    //     window.console.log(message);
    //   }
    // };

    channel.bind('pusher:subscription_succeeded', function(members) {
        addUserImage(members.me);
        var userCount = 0;

        _.each(members, function(member){
            if(member.info){
                userCount ++;
                addToDJList(member);
            }
        });

        // This should fire for the first person in the room
        if(userCount == 1){
            var data = {
                'member': members.me.id,
                'isFirstDJ': true
            }
            ajaxCall('http://testtable.dev/room/djadd', data);
        }
    });

    pusher.connection.bind('connected', function() { //bind a function after we've connected to Pusher
        //console.log('connected');
    });

    channel.bind('debug_event', function(data) {
        alert(data.message);
    });

    channel.bind('pusher:member_added', function(member) {
        addUserImage(member);
    });

   channel.bind('pusher:member_removed', function(member) {
        removeUserImage(member.info);
    });

}

function addUserImage(data){
	var userImage = '

';
	$('#user_list').append(userImage);
}

function removeUserImage(data){
	$('#user_list li[data-userid="'+data.id+'"]').fadeOut();
}

function addToDJList(member){
    // have access to the member details here but just pusing the memberid for now
    roomMembers.push(member.id);
}

function reportAddedMember(member){
    var data = {
        'member': member.id
    }
    ajaxCall('http://testtable.dev/room/djadd', data);
}

function ajaxCall(ajax_url, ajax_data, successCallback)
{
    $.ajax({
        type : "GET",
        url : ajax_url,
        dataType : "json",
        data: ajax_data,
        time : 10,
        success : function(msg) {

        },
        error: function(msg) {
            // log something
        }
    });
}<|endoftext|>


<commit_before>/* global describe, it */

// The point of these test cases is to check that we are providing
// helpful error messages to users that can guide them in the
// right direction. The Reddit API is a bit of a mystery when
// starting out.

var when = require('when');

var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;

var Snoocore = require('../../Snoocore');
var config = require('../config');
var util = require('./util');

describe('Snoocore Error Test', function () {

  this.timeout(config.testTimeout);

  it('should handle data.json.errors field', function() {

    var reddit = util.getScriptInstance([ 'identity', 'modconfig' ]);

    return reddit.auth().then(function() {
      return reddit('/r/$subreddit/about/edit.json').get({
	$subreddit: config.reddit.testSubreddit
      });
    }).then(function(result) {

      var data = result.data;

      data.api_type = 'json'; // must be the string 'json'

      return reddit('/api/site_admin').post(data);

    }).catch(function(error) {
      expect(error.message.indexOf('BAD_SR_NAME')).to.not.equal(-1);
    });
  });

  it('should handle data.errors field', function() {
    var reddit = util.getScriptInstance([ 'modconfig' ]);

    return reddit.auth().then(function() {
      return reddit('/r/$subreddit/api/upload_sr_img').post({
	$subreddit: config.reddit.testSubreddit,
	file: Snoocore.file('fakename', 'image/png', 'fake image data'),
	header: 0,
	img_type: 'png',
	name: 'test'
      });
    }).catch(function(error) {
      expect(error.message.indexOf('IMAGE_ERROR')).to.not.equal(-1);
    });

  });

  it('should explain what scope was missing', function() {

    var reddit = util.getScriptInstance([ 'identity' ]);

    return reddit.auth().then(function() {
      return reddit('/comments/$article').get({
	sort: 'hot',
	context: 8,
	$article: '2j8u16'
      });
    }).then(function() {
      throw new Error('expected this to fail with invalid scope');
    }).catch(function(error) {
      return expect(error.message.indexOf('missing required scope(s): read')).to.not.equal(-1);
    });
  });

  it('should spit out more information when we come to an error (url & args used)', function() {
    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    return reddit.auth().then(function() {
      return reddit('/comments/$article').get({
	sort: 'hot',
	context: 8,
	$article: 'invalid_article_error'
      });
    }).then(function() {
      throw new Error('expected this to fail with invalid scope');
    }).catch(function(error) {
      expect(error.message.indexOf('Reddit Response')).to.not.equal(-1);
      expect(error.message.indexOf('Endpoint URL')).to.not.equal(-1);
      expect(error.message.indexOf('Endpoint method')).to.not.equal(-1);
      expect(error.message.indexOf('Arguments')).to.not.equal(-1);
    });
  });

  it('should retry an endpoint on HTTP 5xx', function() {
    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    // create a reference to the actual reddit servers
    var redditServers = {
      oauth: 'https://oauth.reddit.com',
      www: 'https://www.reddit.com',
      ssl: 'https://ssl.reddit.com'
    };

    return reddit.auth().then(function() {

      // Switch servers to use error test server (returns 500 errors every time)
      reddit._server = {
	oauth: 'https://localhost:' + config.testServer.serverErrorPort,
	www: 'https://localhost:' + config.testServer.serverErrorPort,
	ssl: 'https://localhost:' + config.testServer.serverErrorPort
      };

      return when.promise(function(resolve, reject) {
	var hotPromise;

	// resolve once we get the server error instance
	reddit.on('server_error', function(error) {

	  expect(error instanceof Error);
	  expect(error.retryAttemptsLeft).to.equal(9);
	  expect(error.status).to.equal(500);
	  expect(error.url).to.equal('https://localhost:3001/hot.json');
	  expect(error.args).to.eql({});
	  expect(error.body).to.equal('');

	  // don't allow self signed certs again
	  delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;

	  // switch to the proper reddit servers
	  reddit._server = redditServers;
	  expect(reddit._server).to.eql(redditServers);

	  // this should resolve now that the servers are correct
	  hotPromise.done(resolve);
	});

	process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; // allow self signed certs
	hotPromise = reddit('/hot').get();
      });

    });
  });

  it('should retry an endpoint 3 times then fail', function() {

    // allow self signed certs for our test server
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;

    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    return reddit.auth().then(function() {

      // Switch servers to use error test server (returns 500 errors every time)
      reddit._server = {
	oauth: 'https://localhost:' + config.testServer.serverErrorPort,
	www: 'https://localhost:' + config.testServer.serverErrorPort,
	ssl: 'https://localhost:' + config.testServer.serverErrorPort
      };

      var retryAttempts = 3; // let's only retry 3 times to keep it short

      return when.promise(function(resolve, reject) {
	var hotPromise;

	// resolve once we get the server error instance
	reddit.on('server_error', function(error) {

	  expect(error instanceof Error);
	  expect(error.retryAttemptsLeft).to.equal(--retryAttempts);
	  expect(error.status).to.equal(500);
	  expect(error.url).to.equal('https://localhost:3001/hot.json');
	  expect(error.args).to.eql({});
	  expect(error.body).to.equal('');

	  // resolve once we have reached our retry attempt and get an error
	  // we should not resolve this promise! We expect it to fail!!
	  hotPromise.done(reject, resolve);
	});

	hotPromise = reddit('/hot').get(void 0, {
	  retryAttempts: retryAttempts,
	  retryDelay: 500 // no need to make this take longer than necessary
	});
      });

    }).then(function(error) {
      expect(error.message).to.eql(
	'All retry attempts exhausted. Failed to access the reddit servers (HTTP 500).');
    }).finally(function() {
      // don't allow self signed certs again
      delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
    });

  });

});
<commit_msg>change test to reflect new retry values; issue #90<commit_after>/* global describe, it */

// The point of these test cases is to check that we are providing
// helpful error messages to users that can guide them in the
// right direction. The Reddit API is a bit of a mystery when
// starting out.

var when = require('when');

var chai = require('chai');
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var expect = chai.expect;

var Snoocore = require('../../Snoocore');
var config = require('../config');
var util = require('./util');

describe('Snoocore Error Test', function () {

  this.timeout(config.testTimeout);

  it('should handle data.json.errors field', function() {

    var reddit = util.getScriptInstance([ 'identity', 'modconfig' ]);

    return reddit.auth().then(function() {
      return reddit('/r/$subreddit/about/edit.json').get({
	$subreddit: config.reddit.testSubreddit
      });
    }).then(function(result) {

      var data = result.data;

      data.api_type = 'json'; // must be the string 'json'

      return reddit('/api/site_admin').post(data);

    }).catch(function(error) {
      expect(error.message.indexOf('BAD_SR_NAME')).to.not.equal(-1);
    });
  });

  it('should handle data.errors field', function() {
    var reddit = util.getScriptInstance([ 'modconfig' ]);

    return reddit.auth().then(function() {
      return reddit('/r/$subreddit/api/upload_sr_img').post({
	$subreddit: config.reddit.testSubreddit,
	file: Snoocore.file('fakename', 'image/png', 'fake image data'),
	header: 0,
	img_type: 'png',
	name: 'test'
      });
    }).catch(function(error) {
      expect(error.message.indexOf('IMAGE_ERROR')).to.not.equal(-1);
    });

  });

  it('should explain what scope was missing', function() {

    var reddit = util.getScriptInstance([ 'identity' ]);

    return reddit.auth().then(function() {
      return reddit('/comments/$article').get({
	sort: 'hot',
	context: 8,
	$article: '2j8u16'
      });
    }).then(function() {
      throw new Error('expected this to fail with invalid scope');
    }).catch(function(error) {
      return expect(error.message.indexOf('missing required scope(s): read')).to.not.equal(-1);
    });
  });

  it('should spit out more information when we come to an error (url & args used)', function() {
    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    return reddit.auth().then(function() {
      return reddit('/comments/$article').get({
	sort: 'hot',
	context: 8,
	$article: 'invalid_article_error'
      });
    }).then(function() {
      throw new Error('expected this to fail with invalid scope');
    }).catch(function(error) {
      expect(error.message.indexOf('Reddit Response')).to.not.equal(-1);
      expect(error.message.indexOf('Endpoint URL')).to.not.equal(-1);
      expect(error.message.indexOf('Endpoint method')).to.not.equal(-1);
      expect(error.message.indexOf('Arguments')).to.not.equal(-1);
    });
  });

  it('should retry an endpoint on HTTP 5xx', function() {
    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    // create a reference to the actual reddit servers
    var redditServers = {
      oauth: 'https://oauth.reddit.com',
      www: 'https://www.reddit.com',
      ssl: 'https://ssl.reddit.com'
    };

    return reddit.auth().then(function() {

      // Switch servers to use error test server (returns 500 errors every time)
      reddit._server = {
	oauth: 'https://localhost:' + config.testServer.serverErrorPort,
	www: 'https://localhost:' + config.testServer.serverErrorPort,
	ssl: 'https://localhost:' + config.testServer.serverErrorPort
      };

      return when.promise(function(resolve, reject) {
	var hotPromise;

	// resolve once we get the server error instance
	reddit.on('server_error', function(error) {

	  expect(error instanceof Error);
	  expect(error.retryAttemptsLeft).to.equal(59);
	  expect(error.status).to.equal(500);
	  expect(error.url).to.equal('https://localhost:3001/hot.json');
	  expect(error.args).to.eql({});
	  expect(error.body).to.equal('');

	  // don't allow self signed certs again
	  delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;

	  // switch to the proper reddit servers
	  reddit._server = redditServers;
	  expect(reddit._server).to.eql(redditServers);

	  // this should resolve now that the servers are correct
	  hotPromise.done(resolve);
	});

	process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; // allow self signed certs
	hotPromise = reddit('/hot').get();
      });

    });
  });

  it('should retry an endpoint 3 times then fail', function() {

    // allow self signed certs for our test server
    process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;

    var reddit = util.getScriptInstance([ 'identity', 'read' ]);

    return reddit.auth().then(function() {

      // Switch servers to use error test server (returns 500 errors every time)
      reddit._server = {
	oauth: 'https://localhost:' + config.testServer.serverErrorPort,
	www: 'https://localhost:' + config.testServer.serverErrorPort,
	ssl: 'https://localhost:' + config.testServer.serverErrorPort
      };

      var retryAttempts = 3; // let's only retry 3 times to keep it short

      return when.promise(function(resolve, reject) {
	var hotPromise;

	// resolve once we get the server error instance
	reddit.on('server_error', function(error) {

	  expect(error instanceof Error);
	  expect(error.retryAttemptsLeft).to.equal(--retryAttempts);
	  expect(error.status).to.equal(500);
	  expect(error.url).to.equal('https://localhost:3001/hot.json');
	  expect(error.args).to.eql({});
	  expect(error.body).to.equal('');

	  // resolve once we have reached our retry attempt and get an error
	  // we should not resolve this promise! We expect it to fail!!
	  hotPromise.done(reject, resolve);
	});

	hotPromise = reddit('/hot').get(void 0, {
	  retryAttempts: retryAttempts,
	  retryDelay: 500 // no need to make this take longer than necessary
	});
      });

    }).then(function(error) {
      expect(error.message).to.eql(
	'All retry attempts exhausted. Failed to access the reddit servers (HTTP 500).');
    }).finally(function() {
      // don't allow self signed certs again
      delete process.env.NODE_TLS_REJECT_UNAUTHORIZED;
    });

  });

});
<|endoftext|>


<commit_before>var db = require('./lib/listdb').getDB('btc')
var request = require('request')
var entities = require('./lib/entities')
var fs = require('fs')
var exec = require('child_process').exec
var tr = require('tor-request')

function getATH() {
  var messages = db.getAll();
  if (!messages.length) messages[0] = "0 0 0"
  var market = messages[0].split(" ")
  var ath = {
    usd: market[0],
    eur: market[1],
    date: market[2]
  }
  return ath
}

function setATH(ath){
  var messages = db.getAll();
  if (!messages.length) messages[0] = "0 0 0"
  var market = messages[0]
  db.remove(market)
  db.add(ath.usd + " " + " " + ath.eur + " " + ath.date.getTime())
}

function niceDateATH(date){
  var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false };
  return date.toLocaleDateString("en-US",options);
}

function checkATH(btc, replyTo){
  var ATH = getATH()
  if (btc) {
    if (btc.usd > ATH.usd) {
      ATH.usd = btc.usd
      ATH.eur = btc.eur
      ATH.date = new Date()
      setATH(ATH)
      if (replyTo)
        showATH(replyTo)
      return true
    }
  }
  return false
}

function showATH(replyTo){
  var ATH = getATH()
  var output =
    'ATH: 1 BTC =' +
    ' USD ' + (ATH.usd).toFixed(2) + ' ~' +
    ' EUR ' + (ATH.eur).toFixed(2) + ' ~' +
    ' ' + niceDateATH(ATH.date);
  irc.privmsg(replyTo,output);
}

function checkATHRequest(replyTo){
  var url = 'https://blockchain.info/ticker';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var market = JSON.parse(body);
      var btc = {
        "eur": parseFloat(market["EUR"]["15m"]),
        "usd": parseFloat(market["USD"]["15m"])
      }
      if (btc) {
        var ATH = getATH()
        if (btc.usd > ATH.usd) {
          ATH.usd = btc.usd
          ATH.eur = btc.eur
          ATH.date = new Date()
          setATH(ATH)
          if (replyTo) showATH(replyTo)
        }
      }
    }
  });
}

listen(regexFactory.startsWith(["ath"]), function (match, data, replyTo, from) {
  checkATHRequest(replyTo)
});

function printHelp(replyTo){
  irc.privmsg(replyTo, '~btc [amount(default 1)] [eur||usd]');
}

function calc(replyTo, market, amount, fiat){
  var btc = {
    "eur": parseFloat(market["EUR"]["15m"]),
    "usd": parseFloat(market["USD"]["15m"])
  }
  var output = '';
  var amount = parseFloat(amount);
  if (!fiat) fiat = "";
  fiat = fiat.toLowerCase() || "";
  if(fiat === 'eur' || fiat === 'usd') {
    output = '' + amount.toFixed(2) + ' ' + fiat.toUpperCase() + ' ~ ' + (1.0/btc[fiat]*amount).toFixed(8) + ' BTC';
  } else {
    output =
      '' + amount + ' BTC =' +
      ' USD ' + (btc.usd * amount).toFixed(2) + ' ~' +
      ' EUR ' + (btc.eur * amount).toFixed(2) + ' ';

  }

  if (checkATH(btc, true)) {
    output = output + ' ~ *NEW ALL TIME HIGH*'
  }

  irc.privmsg(replyTo, output.toString("utf8"))
}

listen(regexFactory.startsWith(["btc"]), function (match, data, replyTo, from) {

  var url = 'https://blockchain.info/ticker';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var market = JSON.parse(body);

      // default
      if (match[1].trim().length === 0) {
        calc(replyTo, market,1)
        return;
      }

      var params = match[1].split(' ');
      if (params.length !== 1 && params.length !== 2) {
        printHelp(replyTo)
        return;
      }

      if (params[0] === 'help') {
        printHelp(replyTo)
        return;
      }

      if (params[0]) params[0] = params[0].replace(',','.');
      if (params[1]) params[1] = params[1].replace(',','.');

      if (isNumber(params[0]) && !isNumber(params[1])){

        calc(
          replyTo,
          market,
          parseFloat(params[0]),
          params[1]
          );
        return;

      }
      if (!isNumber(params[0]) && isNumber(params[1])){

        calc(
          replyTo,
          market,
          parseFloat(params[1]),
          params[0]
          );
        return;

      }

      if (!isNumber(params[0]) && !params[1]){
        if (params[0].toUpperCase() !== 'EUR' && params[0].toUpperCase() !== 'USD') {
          printHelp(replyTo)
          return;
        } else {
          calc(
            replyTo,
            market,
            parseFloat(1),
            params[0]
            );
          return;
        }
      }

      if (!isNumber(params[0]) && !isNumber(params[1])){
        console.log('not number 1 + 2param ')
        printHelp(replyTo)
        return;
      }

      calc(
        replyTo,
        market,
        parseFloat(params[0]),
        params[1]
        );

      // if (!isNumber(params[0])){
      //   printHelp(replyTo)
      //   return;
      // }

    } else {
      irc.privmsg(replyTo,"error: could not get market");
    }
  });

});

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

listen(regexFactory.startsWith(["blocks"]), function (match, data, replyTo, from) {

  var url = 'https://blockchain.info/stats?format=json';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var result = JSON.parse(body)
      if (result && result.n_blocks_total) {
        var blocks = parseInt(result.n_blocks_total)
        var blocksleft = 630000 - blocks
        var blockslefttext = ""
        if (blocksleft) {
          blockslefttext = " | blocks to go: " + blocksleft
        }
        var text = "btc blockcount: " + blocks + "" + blockslefttext
        irc.privmsg(replyTo,text);

      }
    } else {
      irc.privmsg(replyTo,"error: could not get blockcount");
    }
  });

});
<commit_msg>~btc fix<commit_after>var db = require('./lib/listdb').getDB('btc')
var request = require('request')
var entities = require('./lib/entities')
var fs = require('fs')
var exec = require('child_process').exec
var tr = require('tor-request')

function getATH() {
  var messages = db.getAll();
  if (!messages.length) messages[0] = "0 0 0"
  var market = messages[0].split(" ")
  var ath = {
    usd: market[0],
    eur: market[1],
    date: market[2]
  }
  return ath
}

function setATH(ath){
  var messages = db.getAll();
  if (!messages.length) messages[0] = "0 0 0"
  var market = messages[0]
  db.remove(market)
  db.add(ath.usd + " " + " " + ath.eur + " " + ath.date.getTime())
}

function niceDateATH(date){
  var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour:'2-digit',minute:'2-digit',second:'2-digit',hour12:false };
  return date.toLocaleDateString("en-US",options);
}

function checkATH(btc, replyTo){
  var ATH = getATH()
  if (btc) {
    if (btc.usd > ATH.usd) {
      ATH.usd = btc.usd
      ATH.eur = btc.eur
      ATH.date = new Date()
      setATH(ATH)
      if (replyTo)
        showATH(replyTo)
      return true
    }
  }
  return false
}

function showATH(replyTo){
  var ATH = getATH()
  var output =
    'ATH: 1 BTC =' +
    ' USD ' + (ATH.usd).toFixed(2) + ' ~' +
    ' EUR ' + (ATH.eur).toFixed(2) + ' ~' +
    ' ' + niceDateATH(ATH.date);
  irc.privmsg(replyTo,output);
}

function checkATHRequest(replyTo){
  var url = 'https://blockchain.info/ticker';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var market = JSON.parse(body);
      var btc = {
        "eur": parseFloat(market["EUR"]["15m"]),
        "usd": parseFloat(market["USD"]["15m"])
      }
      if (btc) {
        var ATH = getATH()
        if (btc.usd > ATH.usd) {
          ATH.usd = btc.usd
          ATH.eur = btc.eur
          ATH.date = new Date()
          setATH(ATH)
        }
        if (replyTo) showATH(replyTo)
      }
    }
  });
}

listen(regexFactory.startsWith(["btc-ath"]), function (match, data, replyTo, from) {
  checkATHRequest(replyTo)
});

function printHelp(replyTo){
  irc.privmsg(replyTo, '~btc [amount(default 1)] [eur||usd]');
}

function calc(replyTo, market, amount, fiat){
  var btc = {
    "eur": parseFloat(market["EUR"]["15m"]),
    "usd": parseFloat(market["USD"]["15m"])
  }
  var output = '';
  var amount = parseFloat(amount);
  if (!fiat) fiat = "";
  fiat = fiat.toLowerCase() || "";
  if(fiat === 'eur' || fiat === 'usd') {
    output = '' + amount.toFixed(2) + ' ' + fiat.toUpperCase() + ' ~ ' + (1.0/btc[fiat]*amount).toFixed(8) + ' BTC';
  } else {
    output =
      '' + amount + ' BTC =' +
      ' USD ' + (btc.usd * amount).toFixed(2) + ' ~' +
      ' EUR ' + (btc.eur * amount).toFixed(2) + ' ';

  }

  if (checkATH(btc, true)) {
    output = output + ' ~ *NEW ALL TIME HIGH*'
  }

  irc.privmsg(replyTo, output.toString("utf8"))
}

listen(regexFactory.startsWith(["btc"]), function (match, data, replyTo, from) {

  var url = 'https://blockchain.info/ticker';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var market = JSON.parse(body);

      // default
      if (match[1].trim().length === 0) {
        calc(replyTo, market,1)
        return;
      }

      var params = match[1].split(' ');
      if (params.length !== 1 && params.length !== 2) {
        printHelp(replyTo)
        return;
      }

      if (params[0] === 'help') {
        printHelp(replyTo)
        return;
      }

      if (params[0]) params[0] = params[0].replace(',','.');
      if (params[1]) params[1] = params[1].replace(',','.');

      if (isNumber(params[0]) && !isNumber(params[1])){

        calc(
          replyTo,
          market,
          parseFloat(params[0]),
          params[1]
          );
        return;

      }
      if (!isNumber(params[0]) && isNumber(params[1])){

        calc(
          replyTo,
          market,
          parseFloat(params[1]),
          params[0]
          );
        return;

      }

      if (!isNumber(params[0]) && !params[1]){
        if (params[0].toUpperCase() !== 'EUR' && params[0].toUpperCase() !== 'USD') {
          printHelp(replyTo)
          return;
        } else {
          calc(
            replyTo,
            market,
            parseFloat(1),
            params[0]
            );
          return;
        }
      }

      if (!isNumber(params[0]) && !isNumber(params[1])){
        console.log('not number 1 + 2param ')
        printHelp(replyTo)
        return;
      }

      calc(
        replyTo,
        market,
        parseFloat(params[0]),
        params[1]
        );

      // if (!isNumber(params[0])){
      //   printHelp(replyTo)
      //   return;
      // }

    } else {
      irc.privmsg(replyTo,"error: could not get market");
    }
  });

});

function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

listen(regexFactory.startsWith(["blocks"]), function (match, data, replyTo, from) {

  var url = 'https://blockchain.info/stats?format=json';
  var requestObject = {
    uri: url,
    strictSSL: false,
    timeout: 2000,
    encoding: null,
    headers: {
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31 Nodebot'
    }
  };

  request(requestObject, function(error, response, body) {
    if(response.statusCode == 200) {
      var result = JSON.parse(body)
      if (result && result.n_blocks_total) {
        var blocks = parseInt(result.n_blocks_total)
        var blocksleft = 630000 - blocks
        var blockslefttext = ""
        if (blocksleft) {
          blockslefttext = " | blocks to go: " + blocksleft
        }
        var text = "btc blockcount: " + blocks + "" + blockslefttext
        irc.privmsg(replyTo,text);

      }
    } else {
      irc.privmsg(replyTo,"error: could not get blockcount");
    }
  });

});
<|endoftext|>


<commit_before>var test = require('tape');
var MBTiles = require('mbtiles');
var tilelive = require('..');
var Tile = require('../lib/stream-util').Tile;
var Info = require('../lib/stream-util').Info;
var DeserializationError = require('../lib/stream-util').DeserializationError;
var fs = require('fs');
var path = require('path');
var tmp = require('os').tmpdir();
var assert = require('assert');
var unzip = require('zlib').createGunzip();

var filepath = path.join(tmp, 'list.mbtiles');
var tmpSerial = path.join(tmp, 'tilelive.serialized');
var tmpDst = path.join(tmp, 'tilelive.dstMbtiles');

var src, dst;

test('deserialize: src', function(t) {
    new MBTiles(__dirname + '/fixtures/plain_1.mbtiles', function(err, s) {
        t.ifError(err);
        src = s;
        t.end();
    });
});

test('deserialize: dst', function(t) {
    try { fs.unlinkSync(filepath); } catch(e) {}
    new MBTiles(filepath, function(err, d) {
        t.ifError(err);
        dst = d;
        dst._batchSize = 1;
        t.end();
    });
});

test('deserialize: list', function(t) {
    var file = fs.createReadStream(path.join(__dirname,'fixtures','filescheme.flat'));
    var get = tilelive.createReadStream(src, {type:'list'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];

    file.pipe(get).pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 77, 'correct number of tiles deserialized');
            t.end();
        });
});

test('deserialize: scanline', function(t) {
    var get = tilelive.createReadStream(src, {type:'scanline'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];
    t.plan(6);
    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .once('tile', function(tile) {
            t.ok(tile instanceof Tile, 'emitted Tile event');
            t.ok(Object.keys(tile).length > 0, 'emitted tile object is deserialized');
        })
        .on('info', function(info) {
            t.ok(info instanceof Info, 'emitted Info event');
            t.ok(Object.keys(info).length > 0, 'emitted info object is deserialized');
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 286, 'correct number of tiles deserialized');
        });
});

test('deserialize: pyramid', function(t) {
    var get = tilelive.createReadStream(src, {type:'pyramid'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];
    t.plan(6);
    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .once('tile', function(tile) {
            t.ok(tile instanceof Tile, 'emitted Tile event');
            t.ok(Object.keys(tile).length > 0, 'emitted tile object is deserialized');
        })
        .on('info', function(info) {
            t.ok(info instanceof Info, 'emitted Info event');
            t.ok(Object.keys(info).length > 0, 'emitted info object is deserialized');
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 286, 'correct number of tiles deserialized');
        });
});

test('deserialize: put', function(t) {
    var get = tilelive.createReadStream(src, {type:'scanline'});
    var put = tilelive.createWriteStream(dst);
    get.on('error', function(err) { t.ifError(err); });
    put.on('error', function(err) { t.ifError(err); });
    put.on('stop', function() {
        t.deepEqual(get.stats, { ops:285, total: 285, skipped: 0, done: 285 });
        t.end();
    });

    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize()).pipe(put);
});

test('deserialize: verify put', function(t) {
    var errors = [];
    tilelive.createReadStream(dst, {type: 'scanline'})
        .on('error', function(err) { t.ifError(err); })
        .on('data', function(data) {
            if (data instanceof Tile || data instanceof Info) return;
            errors.push(new Error('Not a valid type of object'));
        })
        .on('end', function() {
            t.equal(errors.length, 0, 'put objects are valid Tile or Info objects');
            t.end();
        });
});

test('deserialize: garbage', function(t) {
    t.plan(1);
    fs.createReadStream(path.join(__dirname,'fixtures','filescheme.flat'))
        .pipe(tilelive.deserialize())
        .on('data', function(d) { t.notOk(d, 'no data should be received'); })
        .on('end', function() { t.notOk(true, 'error should\'ve occurred'); })
        .on('error', function(err) {
            t.ok(err instanceof DeserializationError, 'deserialization error should be thrown');
        });
});

test('de/serialize: round-trip', function(t) {
    try { fs.unlinkSync(tmpSerial); } catch(e) {
        console.log(e);
    }
    try { fs.unlinkSync(tmpDst); } catch(e) {
        console.log(e);
    }

    var original = tilelive.createReadStream(src, {type: 'scanline'})
        .on('error', function(err) { t.ifError(err); });
    var serialize = tilelive.serialize()
        .on('error', function(err) { t.ifError(err); });
    var deserialize = tilelive.deserialize()
        .on('error', function(err) { t.ifError(err); });

    new MBTiles(tmpDst, function(err, outMbtiles) {
        t.ifError(err);
        original.pipe(serialize).pipe(fs.createWriteStream(tmpSerial))
            .on('finish', function() {
                var final = tilelive.createWriteStream(outMbtiles)
                    .on('error', function(err) { t.ifError(err); });

                fs.createReadStream(tmpSerial)
                    .pipe(deserialize)
                    .pipe(final)
                    .on('stop', makeAssertions);
            });
    });

    function makeAssertions() {
        var originalStats = fs.statSync(filepath);
        var serializedStats = fs.statSync(tmpSerial);
        var finalStats = fs.statSync(tmpDst);

        var sizeDiff = Math.abs(originalStats.size - finalStats.size) / originalStats.size;

        t.ok(sizeDiff < 0.01, 'round-tripped mbtiles are approx. the same size');
        t.end();
    }

});

test('deserialize: incomplete', function(t) {
    t.plan(1);
    fs.createReadStream(path.join(__dirname, 'fixtures', 'cereal-incomplete.gz'))
        .pipe(unzip)
        .pipe(tilelive.deserialize())
        .on('error', function(err) {
            t.ok(err instanceof DeserializationError, 'incomplete file throws expected exception');
        })
        .on('end', t.end);
});

test('deserialize: split into jobs', function(t) {
    var results = [];
    var tilesPerJob = [];
    var tilelist = path.join(__dirname, 'fixtures', 'plain_1.tilelist');
    var expectedTiles = fs.readFileSync(tilelist, 'utf8').split('\n').slice(0, -1);

    runJob(1, 0, function() {       // one job
    runJob(4, 0, function() {       // a few jobs
    runJob(15, 0, function() {      // a moderate number of jobs
    runJob(285, 0, function() {     // as many jobs as there are tiles
    runJob(400, 0, t.end.bind(t));  // more jobs than there are tiles
    });});});});

    function runJob(total, num, done) {
        var tileCount = 0;
        var gotInfo = false;
        fs.createReadStream(path.join(__dirname, 'fixtures', 'plain_1.serialtiles'))
            .pipe(tilelive.deserialize({ job: { total: total, num: num } }))
            .on('error', function(err) {
                t.ifError(err, 'Error during deserialization');
            })
            .on('tile', function(tile) {
                if (tile.hasOwnProperty('x')) { // filters out info objects
                    results.push([tile.z, tile.x, tile.y].join('/'));
                    tileCount++;
                }
            })
            .on('info', function(info) {
                gotInfo = true;
            })
            .on('finish', function() {
                tilesPerJob.push(tileCount);
                if (num === total - 1) {
                    t.ok(gotInfo, 'got info object');
                    t.equal(results.length, 285, 'correct number of tiles across ' + total + ' jobs');
                    var tiles = results.reduce(function(memo, tile) {
                        if (memo[tile]) memo[tile]++;
                        else memo[tile] = 1;
                        return memo;
                    }, {});

                    for (var k in tiles) {
                        if (tiles[k] > 1) t.fail('tile repeated ' + tiles[k] + ' times with ' + total + ' jobs: ' + k);
                    }

                    var gotAllTiles = expectedTiles.reduce(function(memo, tile) {
                        if (results.indexOf(tile) < 0) memo = false;
                        return memo;
                    }, true);
                    t.ok(gotAllTiles, 'rendered all expected tiles');

                    results = [];
                    tilesPerJob = [];
                    done();
                } else {
                    num++;
                    runJob(total, num, done);
                }
            });
    }
});
<commit_msg>print error from filepath unlink<commit_after>var test = require('tape');
var MBTiles = require('mbtiles');
var tilelive = require('..');
var Tile = require('../lib/stream-util').Tile;
var Info = require('../lib/stream-util').Info;
var DeserializationError = require('../lib/stream-util').DeserializationError;
var fs = require('fs');
var path = require('path');
var tmp = require('os').tmpdir();
var assert = require('assert');
var unzip = require('zlib').createGunzip();

var filepath = path.join(tmp, 'list.mbtiles');
var tmpSerial = path.join(tmp, 'tilelive.serialized');
var tmpDst = path.join(tmp, 'tilelive.dstMbtiles');

var src, dst;

test('deserialize: src', function(t) {
    new MBTiles(__dirname + '/fixtures/plain_1.mbtiles', function(err, s) {
        t.ifError(err);
        src = s;
        t.end();
    });
});

test('deserialize: dst', function(t) {
    try { fs.unlinkSync(filepath); } catch(e) {
        console.log(e);
    }
    new MBTiles(filepath, function(err, d) {
        t.ifError(err);
        dst = d;
        dst._batchSize = 1;
        t.end();
    });
});

test('deserialize: list', function(t) {
    var file = fs.createReadStream(path.join(__dirname,'fixtures','filescheme.flat'));
    var get = tilelive.createReadStream(src, {type:'list'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];

    file.pipe(get).pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 77, 'correct number of tiles deserialized');
            t.end();
        });
});

test('deserialize: scanline', function(t) {
    var get = tilelive.createReadStream(src, {type:'scanline'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];
    t.plan(6);
    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .once('tile', function(tile) {
            t.ok(tile instanceof Tile, 'emitted Tile event');
            t.ok(Object.keys(tile).length > 0, 'emitted tile object is deserialized');
        })
        .on('info', function(info) {
            t.ok(info instanceof Info, 'emitted Info event');
            t.ok(Object.keys(info).length > 0, 'emitted info object is deserialized');
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 286, 'correct number of tiles deserialized');
        });
});

test('deserialize: pyramid', function(t) {
    var get = tilelive.createReadStream(src, {type:'pyramid'});
    get.on('error', function(err) { t.ifError(err); });

    var out = [];
    t.plan(6);
    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize())
        .on('data', function(d) {
            out.push(d);
        })
        .once('tile', function(tile) {
            t.ok(tile instanceof Tile, 'emitted Tile event');
            t.ok(Object.keys(tile).length > 0, 'emitted tile object is deserialized');
        })
        .on('info', function(info) {
            t.ok(info instanceof Info, 'emitted Info event');
            t.ok(Object.keys(info).length > 0, 'emitted info object is deserialized');
        })
        .on('finish', function() {
            function checkType() {
                out.forEach(function(obj) {
                    if (obj instanceof Tile || obj instanceof Info) return;
                    throw new Error('Not a valid type of object');
                });
            }
            t.doesNotThrow(checkType, 'deserialized objects are valid Tile or Info objects');
            t.equal(out.length, 286, 'correct number of tiles deserialized');
        });
});

test('deserialize: put', function(t) {
    var get = tilelive.createReadStream(src, {type:'scanline'});
    var put = tilelive.createWriteStream(dst);
    get.on('error', function(err) { t.ifError(err); });
    put.on('error', function(err) { t.ifError(err); });
    put.on('stop', function() {
        t.deepEqual(get.stats, { ops:285, total: 285, skipped: 0, done: 285 });
        t.end();
    });

    get.pipe(tilelive.serialize()).pipe(tilelive.deserialize()).pipe(put);
});

test('deserialize: verify put', function(t) {
    var errors = [];
    tilelive.createReadStream(dst, {type: 'scanline'})
        .on('error', function(err) { t.ifError(err); })
        .on('data', function(data) {
            if (data instanceof Tile || data instanceof Info) return;
            errors.push(new Error('Not a valid type of object'));
        })
        .on('end', function() {
            t.equal(errors.length, 0, 'put objects are valid Tile or Info objects');
            t.end();
        });
});

test('deserialize: garbage', function(t) {
    t.plan(1);
    fs.createReadStream(path.join(__dirname,'fixtures','filescheme.flat'))
        .pipe(tilelive.deserialize())
        .on('data', function(d) { t.notOk(d, 'no data should be received'); })
        .on('end', function() { t.notOk(true, 'error should\'ve occurred'); })
        .on('error', function(err) {
            t.ok(err instanceof DeserializationError, 'deserialization error should be thrown');
        });
});

test('de/serialize: round-trip', function(t) {
    try { fs.unlinkSync(tmpSerial); } catch(e) {
        console.log(e);
    }
    try { fs.unlinkSync(tmpDst); } catch(e) {
        console.log(e);
    }

    var original = tilelive.createReadStream(src, {type: 'scanline'})
        .on('error', function(err) { t.ifError(err); });
    var serialize = tilelive.serialize()
        .on('error', function(err) { t.ifError(err); });
    var deserialize = tilelive.deserialize()
        .on('error', function(err) { t.ifError(err); });

    new MBTiles(tmpDst, function(err, outMbtiles) {
        t.ifError(err);
        original.pipe(serialize).pipe(fs.createWriteStream(tmpSerial))
            .on('finish', function() {
                var final = tilelive.createWriteStream(outMbtiles)
                    .on('error', function(err) { t.ifError(err); });

                fs.createReadStream(tmpSerial)
                    .pipe(deserialize)
                    .pipe(final)
                    .on('stop', makeAssertions);
            });
    });

    function makeAssertions() {
        var originalStats = fs.statSync(filepath);
        var serializedStats = fs.statSync(tmpSerial);
        var finalStats = fs.statSync(tmpDst);

        var sizeDiff = Math.abs(originalStats.size - finalStats.size) / originalStats.size;

        t.ok(sizeDiff < 0.01, 'round-tripped mbtiles are approx. the same size');
        t.end();
    }

});

test('deserialize: incomplete', function(t) {
    t.plan(1);
    fs.createReadStream(path.join(__dirname, 'fixtures', 'cereal-incomplete.gz'))
        .pipe(unzip)
        .pipe(tilelive.deserialize())
        .on('error', function(err) {
            t.ok(err instanceof DeserializationError, 'incomplete file throws expected exception');
        })
        .on('end', t.end);
});

test('deserialize: split into jobs', function(t) {
    var results = [];
    var tilesPerJob = [];
    var tilelist = path.join(__dirname, 'fixtures', 'plain_1.tilelist');
    var expectedTiles = fs.readFileSync(tilelist, 'utf8').split('\n').slice(0, -1);

    runJob(1, 0, function() {       // one job
    runJob(4, 0, function() {       // a few jobs
    runJob(15, 0, function() {      // a moderate number of jobs
    runJob(285, 0, function() {     // as many jobs as there are tiles
    runJob(400, 0, t.end.bind(t));  // more jobs than there are tiles
    });});});});

    function runJob(total, num, done) {
        var tileCount = 0;
        var gotInfo = false;
        fs.createReadStream(path.join(__dirname, 'fixtures', 'plain_1.serialtiles'))
            .pipe(tilelive.deserialize({ job: { total: total, num: num } }))
            .on('error', function(err) {
                t.ifError(err, 'Error during deserialization');
            })
            .on('tile', function(tile) {
                if (tile.hasOwnProperty('x')) { // filters out info objects
                    results.push([tile.z, tile.x, tile.y].join('/'));
                    tileCount++;
                }
            })
            .on('info', function(info) {
                gotInfo = true;
            })
            .on('finish', function() {
                tilesPerJob.push(tileCount);
                if (num === total - 1) {
                    t.ok(gotInfo, 'got info object');
                    t.equal(results.length, 285, 'correct number of tiles across ' + total + ' jobs');
                    var tiles = results.reduce(function(memo, tile) {
                        if (memo[tile]) memo[tile]++;
                        else memo[tile] = 1;
                        return memo;
                    }, {});

                    for (var k in tiles) {
                        if (tiles[k] > 1) t.fail('tile repeated ' + tiles[k] + ' times with ' + total + ' jobs: ' + k);
                    }

                    var gotAllTiles = expectedTiles.reduce(function(memo, tile) {
                        if (results.indexOf(tile) < 0) memo = false;
                        return memo;
                    }, true);
                    t.ok(gotAllTiles, 'rendered all expected tiles');

                    results = [];
                    tilesPerJob = [];
                    done();
                } else {
                    num++;
                    runJob(total, num, done);
                }
            });
    }
});
<|endoftext|>


<commit_before>'use strict';

// Dependencies
var CustomErrors	= require('./CustomErrors'),
	ncp				= require('ncp').ncp;

var Utils = {
	parameterCheck : function(type, arg) {
		if (!arg && arg !== 0 && arg !== false && arg !== '')
			throw new CustomErrors.ParameterNotFound();
		else if (!(arg instanceof type))
			throw new TypeError("Expected an " + type.toString() + " as parametes for this constructor.");
	},

	recursiveCopy : function (src, dest) {
		return new Promise(function(resolve, reject) {
			ncp(src, dest, function(error) {
				if( error ){
					reject(error);
					return false;
				}

				resolve(dest);
			});
		});
	}
};

module.exports = Utils;
<commit_msg>Arrow functions for Utils<commit_after>'use strict';

// Dependencies
var CustomErrors	= require('./CustomErrors'),
	ncp				= require('ncp').ncp;

var Utils = {
	parameterCheck : function (type, arg){
		if (!arg && arg !== 0 && arg !== false && arg !== '')
			throw new CustomErrors.ParameterNotFound();
		else if (!(arg instanceof type))
			throw new TypeError("Expected an " + type.toString() + " as parametes for this constructor.");
	},

	recursiveCopy : function (src, dest){
		return new Promise((resolve, reject) => {
			ncp(src, dest, (error) => {
				if (error) {
					reject(error);
					return false;
				}

				resolve(dest);
			});
		});
	}
};

module.exports = Utils;
<|endoftext|>


<commit_before>'use strict';

var Api = require('../');
var util = require('util');

var nameOnly, fnameOnly;
if (process.argv.length > 2) {
  nameOnly = process.argv[2];
}
if (process.argv.length > 3) {
  fnameOnly = process.argv[3];
}

var models = {};

console.log('API');
console.log('===');
console.log('Auto-generated Api documentation.');

function printToc() {
  console.log('**Table of contents:**');

  Object.keys(Api).forEach(function(name) {
    if (nameOnly && name !== nameOnly) return;

    var Intfc = Api[name];

    console.log('-', '[' + name + '](#' + name.toLowerCase() + ')');

    // var inst = new Intfc();
    var protos = Object.keys(Intfc.prototype);

    protos.forEach(function(fname) {
      if (fname !== 'log' && fname.indexOf('_') !== 0) {
        console.log('  -', '[`' + fname + '`](#' + name.toLowerCase() + '-' + fname.toLowerCase() + ')');
      }
    });
  });
}

function addModel(model, interfaceName, operationName) {
  if (!interfaceName) return;

  var title = 'Model ' + model;

  if (!models[title]) {
    models[title] = {
      name: model,
      interfaceName: interfaceName,
      operations: {}
    };
  }

  if (operationName) {
    models[title].operations[interfaceName + '.' + operationName] = {
      interface: interfaceName,
      operation: operationName
    };
  }
}

function printSchema(schema, interfaceName, operation, padding, ignoreChildren) {
  var descr = schema.describe ? schema.describe() : schema;
  var children = descr.children;
  if (!padding) padding = '';

  Object.keys(children).forEach(function(name) {
    var child = children[name];
    var meta = child.meta && child.meta[0];

    if (meta && meta.header) {
      return;
    }

    var required = (child.flags && child.flags.presence === 'required') || (meta && meta.path);
    var originalType = child.label;

    if (originalType) addModel(originalType, interfaceName, operation);

    process.stdout.write(util.format('\n%s- `%s`:', padding, name));

    if (required) process.stdout.write(util.format(' required'));

    process.stdout.write(util.format(' %s', child.type));

    if (originalType) {
      if (interfaceName) {
        process.stdout.write(util.format(' [%s](#model-%s)', originalType, originalType.toLowerCase()));
      } else {
        process.stdout.write(util.format(' ref %s', originalType));
      }
    }
    if (child.description) process.stdout.write(util.format(' - %s', child.description));

    if (!ignoreChildren && child.children && Object.keys(child.children).length > 0) {
      printSchema(child, interfaceName, operation, padding + '  ');
    }
  });
}

function printFunctions() {
  Object.keys(Api).forEach(function(name) {
    if (nameOnly && name !== nameOnly) return;

    console.log('\n#', name);

    var Intfc = Api[name];

    console.log('**Constructor:**');
    console.log('- `options`');
    printSchema(Intfc.ctorSchema, null, null, '  ');

    // var inst = new Intfc();
    var protos = Object.keys(Intfc.prototype);

    protos.forEach(function(fname) {
      if (fnameOnly && fname !== fnameOnly) return;

      var op = Intfc.prototype[fname];

      if (fname !== 'log' && fname.indexOf('_') !== 0) {
        console.log('\n##', name, fname);
        if (op.path) {
          console.log('Represents call to:');
          console.log('`%s %s`', op.method, op.path);
        }

        if (op.requiresAuthorization) {
          console.log('');
          console.log('> Requires authorization');
        }
        if (op.schemas) {
          console.log('\n**Arguments:**');
          printSchema(op.schemas.input, name, fname, '', true);
          console.log('');

          if (op.schemas.output) {
            console.log('\n**Callback:**');
            console.log('- `error`: Error or null');
            console.log('- `response`: Http response');

            var output = op.schemas.output.describe();
            var originalType = output.label;

            if (originalType) {
              addModel(originalType, name, fname);
            }

            console.log('- `body`:', '[' + output.label + '](' + '#model-' + output.label.toLowerCase() + ')');

            // printSchema(op.schemas.output, name);
            // console.log('');
          }
        }
      }
    });
  });
};

function printModels() {
  console.log('\n# Models');

  Object.keys(models).forEach(function(title) {
    var model = models[title];

    var Intfc = Api[model.interfaceName];
    var modelSchema = Intfc.models[model.name];

    console.log('\n##', title);

    if (modelSchema) {
      printSchema(modelSchema);
      console.log('');
    }

    var operations = Object.keys(model.operations);
    if (operations.length > 0) {
      console.log('\n**Used by:**');
      operations.forEach(function(opTitle) {
        var op = model.operations[opTitle];
        console.log(util.format('[`%s`](#%s-%s)', opTitle, op.interface.toLowerCase(), op.operation.toLowerCase()));
      });
    }
  });
}

printToc();
printFunctions();
printModels();
<commit_msg>And the API.md<commit_after>'use strict';

var Api = require('../');
var util = require('util');

var nameOnly, fnameOnly;
if (process.argv.length > 2) {
  nameOnly = process.argv[2];
}
if (process.argv.length > 3) {
  fnameOnly = process.argv[3];
}

var models = {};

console.log('API');
console.log('===');
console.log('Auto-generated Api documentation.');

function printToc() {
  console.log('**Table of contents:**');

  Object.keys(Api).forEach(function(name) {
    if (nameOnly && name !== nameOnly) return;

    var Intfc = Api[name];

    console.log('-', '[' + name + '](#' + name.toLowerCase() + ')');

    // var inst = new Intfc();
    var protos = Object.keys(Intfc.prototype);

    protos.forEach(function(fname) {
      if (fname !== 'log' && fname.indexOf('_') !== 0) {
        console.log('  -', '[`' + fname + '`](#' + name.toLowerCase() + '-' + fname.toLowerCase() + ')');
      }
    });
  });
}

function addModel(model, interfaceName, operationName) {
  if (!interfaceName) return;

  var title = 'Model ' + model;

  if (!models[title]) {
    models[title] = {
      name: model,
      interfaceName: interfaceName,
      operations: {}
    };
  }

  if (operationName) {
    models[title].operations[interfaceName + '.' + operationName] = {
      interface: interfaceName,
      operation: operationName
    };
  }
}

function printSchema(schema, interfaceName, operation, padding, ignoreChildren) {
  var descr = schema.describe ? schema.describe() : schema;
  var children = descr.children;
  if (!padding) padding = '';

  Object.keys(children).forEach(function(name) {
    var child = children[name];
    var meta = child.meta && child.meta[0];

    if (meta && meta.header) {
      return;
    }

    var required = (child.flags && child.flags.presence === 'required') || (meta && meta.path);
    var originalType = child.label;

    if (originalType) addModel(originalType, interfaceName, operation);

    process.stdout.write(util.format('\n%s- `%s`:', padding, name));

    if (required) process.stdout.write(util.format(' required'));

    process.stdout.write(util.format(' %s', child.type));

    if (originalType) {
      if (interfaceName) {
        process.stdout.write(util.format(' [%s](#model-%s)', originalType, originalType.toLowerCase()));
      } else {
        process.stdout.write(util.format(' ref %s', originalType));
      }
    }
    if (child.description) process.stdout.write(util.format(' - %s', child.description));

    if (!ignoreChildren && child.children && Object.keys(child.children).length > 0) {
      printSchema(child, interfaceName, operation, padding + '  ');
    }
  });
}

function printFunctions() {
  Object.keys(Api).forEach(function(name) {
    if (nameOnly && name !== nameOnly) return;

    console.log('\n#', name);

    var Intfc = Api[name];

    console.log('**Constructor:**');
    console.log('- `options`');
    printSchema(Intfc.ctorSchema, null, null, '  ');
    console.log('');

    // var inst = new Intfc();
    var protos = Object.keys(Intfc.prototype);

    protos.forEach(function(fname) {
      if (fnameOnly && fname !== fnameOnly) return;

      var op = Intfc.prototype[fname];

      if (fname !== 'log' && fname.indexOf('_') !== 0) {
        console.log('\n##', name, fname);
        if (op.path) {
          console.log('Represents call to:');
          console.log('`%s %s`', op.method, op.path);
        }

        if (op.requiresAuthorization) {
          console.log('');
          console.log('> Requires authorization');
        }
        if (op.schemas) {
          console.log('\n**Arguments:**');
          printSchema(op.schemas.input, name, fname, '', true);
          console.log('');

          if (op.schemas.output) {
            console.log('\n**Callback:**');
            console.log('- `error`: Error or null');
            console.log('- `response`: Http response');

            var output = op.schemas.output.describe();
            var originalType = output.label;

            if (originalType) {
              addModel(originalType, name, fname);
            }

            console.log('- `body`:', '[' + output.label + '](' + '#model-' + output.label.toLowerCase() + ')');

            // printSchema(op.schemas.output, name);
            // console.log('');
          }
        }
      }
    });
  });
};

function printModels() {
  console.log('\n# Models');

  Object.keys(models).forEach(function(title) {
    var model = models[title];

    var Intfc = Api[model.interfaceName];
    var modelSchema = Intfc.models[model.name];

    console.log('\n##', title);

    if (modelSchema) {
      printSchema(modelSchema);
      console.log('');
    }

    var operations = Object.keys(model.operations);
    if (operations.length > 0) {
      console.log('\n**Used by:**');
      operations.forEach(function(opTitle) {
        var op = model.operations[opTitle];
        console.log(util.format('[`%s`](#%s-%s)', opTitle, op.interface.toLowerCase(), op.operation.toLowerCase()));
      });
    }
  });
}

printToc();
printFunctions();
printModels();
<|endoftext|>


<commit_before>/*global QUnit:true, module:true, test:true, asyncTest:true, expect:true*/
/*global start:true, stop:true, ok:true, equal:true, notEqual:true, deepEqual:true*/

define([
  'jquery',
  'underscore',
  'backbone',
  'sinon',
  'test/environment',
  'test/EventLog',
  'test/DummyEvent',
  'view/ModelView',
  'view/ContainerView',
  'model/Model',
  'example/model/Post',
  'example/collection/PostCollection',
  'example/ExampleData'
], function($, _, Backbone, sinon, Environment, EventLog, DummyEvent, ModelView, ContainerView, Model, Post, PostCollection, ExampleData) {

  //use Environment to mock ajax
  module('ModelView', _.extend(new Environment(), {
    setup: function() {
      Environment.prototype.setup.apply(this, arguments);
    }
  }));

  var PostRemote = Post.extend({relationships: _.clone(Post.prototype.relationships)});
  PostRemote.prototype.relationships.comments = _.extend({}, PostRemote.prototype.relationships.comments, {
    remote: true,
    deferLoad: true
  });

  test('test model binding', function(){
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var view = new ModelView();
    sinon.spy(view, 'onModelChange');
    sinon.spy(view, 'onModelLoad');
//    sinon.spy(view, 'onModelDestroy');
//    sinon.spy(view, 'onModelError');

    view.setModel(model);
    ok(!view.onModelLoad.called, 'should not fire load on set unlead');

    model.trigger('change');
    ok(view.onModelChange.calledOnce, 'should handle change event');

    model.trigger('load');
    ok(view.onModelLoad.calledOnce, 'should handle load event');

    var newModel = new Post(ExampleData.POST_DATA_WITH_RELATED);
    newModel.onLoad();

    view.setModel(newModel);
    ok(view.onModelLoad.calledOnce, 'do not fire load when setting model with same id');

    model.trigger('load');
    ok(view.onModelLoad.calledOnce, 'stop listening to old model');

    newModel.trigger('load');
    ok(view.onModelLoad.calledTwice, 'start listening to new model');

    view.remove();
    newModel.trigger('load');
    ok(view.onModelLoad.calledTwice, 'stop listening after being removed');

    view.setModel(undefined);
    equal(view.model, undefined, 'should successfully set model to null');

  });

  test('Ignore setting wrong model class', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);

    var view = new ModelView({relationship: 'author'});
    view.setModel(model);

    equal(view.model, model.getRelated('author'), 'should set related model correctly');

    view.model = undefined;
    view.modelCls = 'example/model/Post';
    view.setModel(model);
    equal(view.model, undefined, 'should ingnore inconrrect modelCls');

    view.modelCls = 'example/model/User';
    view.setModel(model);
    equal(view.model, model.getRelated('author'), 'should set related model correctly');
  });

  test('Ignore setting wrong root model class', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var view = new ModelView({rootModelCls: 'example/model/User', relationship: 'author'});

    view.setModel(model);
    equal(view.model, undefined, 'should ingnore inconrrect rootModelCls');

    view.rootModelCls = 'example/model/Post';
    view.setModel(model);
    equal(view.model, model.getRelated('author'), 'should successfully set correct rootModelCls');
  });

  test('Set with relationship', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var author = model.getRelated('author');
    var company = author.getRelated('company');
    var view = new ModelView({
      modelCls: 'example/model/User',
      relationship: 'author',
      subViews: [
        {relationship: 'author.company'},
        {relationship: '.company'}
      ]
    });

    view.setModel(model);

    equal(view.model.id, author.id, 'should correctly set related as model');
    equal(view.subViews[0].model, company, 'should correctly set nested related as model');
    equal(view.subViews[0].model, company, 'should correctly set relative related as model');
  });

  test('Do not render on load until rendered', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var onRenderSpy = sinon.spy();
    var view = new ModelView({onRender: onRenderSpy});

    view.setModel(model);

    model.trigger('load');
    ok(!onRenderSpy.called, 'not rendered, so no render on load');

    view.render();
    model.trigger('load');
    ok(onRenderSpy.calledTwice, 'after render, so render on load');
  });

  test('onShow triggers deferred load', function() {
    var model = new PostRemote(_.extend({}, ExampleData.POST_DATA, {comments: []}));
    var comments = model.getRelated('comments');
    sinon.spy(comments, 'fetchIfAvailable');

    var view = new ModelView({relationship: 'comments', onRender: function(){}});
    view.setModel(model);

    equal(model.fetched, false);
    view.onShow();
    ok(comments.fetchIfAvailable.calledOnce, 'should fetch on show');

    view.onHide();
    view.onShow();
    ok(comments.fetchIfAvailable.calledOnce, 'should not fetch twice');

    view.model = null;
    comments.fetched = false;
    view.setModel(model);
    ok(comments.fetchIfAvailable.calledTwice, 'If view is already shown, fetch on setModel');
  });

  test('nested deferLoad', function() {
    var ParentModel = Model.extend({
      relationships: {
        post: {
          modelCls: PostRemote,
          remote: true,
          deferLoad: true
        }
      }
    });

    var model = new ParentModel({},_.extend({}, ExampleData.POST_DATA, {comments: []}));
  });

  test('setModel sets model on subviews', function() {
    var post = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var author = post.getRelated('author');
    var view = new ModelView({
      subViews: [
        new ContainerView({
          subViews: [
            new ModelView({
              relationship: 'author'
            })
          ]
        })
      ]
    });

    view.setModel(post);
    equal(view.subViews[0].subViews[0].model, author);
  });

  test('test build subivews', function(){
    var view = new ModelView({
      template: '

',
      subViews: [{
        selector: '.section1',
        template: '
foo
'
      }]
    });

    ok(view.subViews[0] instanceof ModelView, 'should instantiate subviews');
  });

  test('onLinkClick short circuits view state only navigation', function() {
    var model = new PostCollection();
    var view = new ModelView();
    view.setModel(model);

    var eventLog = new EventLog(model);

    sinon.stub(model, 'updateUrl');
    view.onLinkClick(new DummyEvent('click', $('foo')));
    equal(model.get('sortField'), 'foo', 'should set view state');
    ok(eventLog.counts.load === undefined, 'should not trigger load');
    equal(eventLog.counts.change, 1, 'should trigger change');

  });

});<commit_msg>removed empty test<commit_after>/*global QUnit:true, module:true, test:true, asyncTest:true, expect:true*/
/*global start:true, stop:true, ok:true, equal:true, notEqual:true, deepEqual:true*/

define([
  'jquery',
  'underscore',
  'backbone',
  'sinon',
  'test/environment',
  'test/EventLog',
  'test/DummyEvent',
  'view/ModelView',
  'view/ContainerView',
  'model/Model',
  'example/model/Post',
  'example/collection/PostCollection',
  'example/ExampleData'
], function($, _, Backbone, sinon, Environment, EventLog, DummyEvent, ModelView, ContainerView, Model, Post, PostCollection, ExampleData) {

  //use Environment to mock ajax
  module('ModelView', _.extend(new Environment(), {
    setup: function() {
      Environment.prototype.setup.apply(this, arguments);
    }
  }));

  var PostRemote = Post.extend({relationships: _.clone(Post.prototype.relationships)});
  PostRemote.prototype.relationships.comments = _.extend({}, PostRemote.prototype.relationships.comments, {
    remote: true,
    deferLoad: true
  });

  test('test model binding', function(){
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var view = new ModelView();
    sinon.spy(view, 'onModelChange');
    sinon.spy(view, 'onModelLoad');
//    sinon.spy(view, 'onModelDestroy');
//    sinon.spy(view, 'onModelError');

    view.setModel(model);
    ok(!view.onModelLoad.called, 'should not fire load on set unlead');

    model.trigger('change');
    ok(view.onModelChange.calledOnce, 'should handle change event');

    model.trigger('load');
    ok(view.onModelLoad.calledOnce, 'should handle load event');

    var newModel = new Post(ExampleData.POST_DATA_WITH_RELATED);
    newModel.onLoad();

    view.setModel(newModel);
    ok(view.onModelLoad.calledOnce, 'do not fire load when setting model with same id');

    model.trigger('load');
    ok(view.onModelLoad.calledOnce, 'stop listening to old model');

    newModel.trigger('load');
    ok(view.onModelLoad.calledTwice, 'start listening to new model');

    view.remove();
    newModel.trigger('load');
    ok(view.onModelLoad.calledTwice, 'stop listening after being removed');

    view.setModel(undefined);
    equal(view.model, undefined, 'should successfully set model to null');

  });

  test('Ignore setting wrong model class', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);

    var view = new ModelView({relationship: 'author'});
    view.setModel(model);

    equal(view.model, model.getRelated('author'), 'should set related model correctly');

    view.model = undefined;
    view.modelCls = 'example/model/Post';
    view.setModel(model);
    equal(view.model, undefined, 'should ingnore inconrrect modelCls');

    view.modelCls = 'example/model/User';
    view.setModel(model);
    equal(view.model, model.getRelated('author'), 'should set related model correctly');
  });

  test('Ignore setting wrong root model class', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var view = new ModelView({rootModelCls: 'example/model/User', relationship: 'author'});

    view.setModel(model);
    equal(view.model, undefined, 'should ingnore inconrrect rootModelCls');

    view.rootModelCls = 'example/model/Post';
    view.setModel(model);
    equal(view.model, model.getRelated('author'), 'should successfully set correct rootModelCls');
  });

  test('Set with relationship', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var author = model.getRelated('author');
    var company = author.getRelated('company');
    var view = new ModelView({
      modelCls: 'example/model/User',
      relationship: 'author',
      subViews: [
        {relationship: 'author.company'},
        {relationship: '.company'}
      ]
    });

    view.setModel(model);

    equal(view.model.id, author.id, 'should correctly set related as model');
    equal(view.subViews[0].model, company, 'should correctly set nested related as model');
    equal(view.subViews[0].model, company, 'should correctly set relative related as model');
  });

  test('Do not render on load until rendered', function() {
    var model = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var onRenderSpy = sinon.spy();
    var view = new ModelView({onRender: onRenderSpy});

    view.setModel(model);

    model.trigger('load');
    ok(!onRenderSpy.called, 'not rendered, so no render on load');

    view.render();
    model.trigger('load');
    ok(onRenderSpy.calledTwice, 'after render, so render on load');
  });

  test('onShow triggers deferred load', function() {
    var model = new PostRemote(_.extend({}, ExampleData.POST_DATA, {comments: []}));
    var comments = model.getRelated('comments');
    sinon.spy(comments, 'fetchIfAvailable');

    var view = new ModelView({relationship: 'comments', onRender: function(){}});
    view.setModel(model);

    equal(model.fetched, false);
    view.onShow();
    ok(comments.fetchIfAvailable.calledOnce, 'should fetch on show');

    view.onHide();
    view.onShow();
    ok(comments.fetchIfAvailable.calledOnce, 'should not fetch twice');

    view.model = null;
    comments.fetched = false;
    view.setModel(model);
    ok(comments.fetchIfAvailable.calledTwice, 'If view is already shown, fetch on setModel');
  });

  test('setModel sets model on subviews', function() {
    var post = new Post(ExampleData.POST_DATA_WITH_RELATED);
    var author = post.getRelated('author');
    var view = new ModelView({
      subViews: [
        new ContainerView({
          subViews: [
            new ModelView({
              relationship: 'author'
            })
          ]
        })
      ]
    });

    view.setModel(post);
    equal(view.subViews[0].subViews[0].model, author);
  });

  test('test build subivews', function(){
    var view = new ModelView({
      template: '

',
      subViews: [{
        selector: '.section1',
        template: '
foo
'
      }]
    });

    ok(view.subViews[0] instanceof ModelView, 'should instantiate subviews');
  });

  test('onLinkClick short circuits view state only navigation', function() {
    var model = new PostCollection();
    var view = new ModelView();
    view.setModel(model);

    var eventLog = new EventLog(model);

    sinon.stub(model, 'updateUrl');
    view.onLinkClick(new DummyEvent('click', $('foo')));
    equal(model.get('sortField'), 'foo', 'should set view state');
    ok(eventLog.counts.load === undefined, 'should not trigger load');
    equal(eventLog.counts.change, 1, 'should trigger change');

  });

});<|endoftext|>


<commit_before>'use strict';
/* global require */
/* global process */

var fs = require ('fs');
var path = require ('path');

var mkdirSync = function (path) {
  try {
    fs.mkdirSync (path);
  } catch (e) {
    if (e.code !== 'EEXIST') {
      throw e;
    }
  }
};

var cwd = process.cwd ().replace (/\\/g, '/');
var suffix = '/node_modules/mai-chai';

if (cwd.endsWith (suffix)) {
  var root = cwd.substr (0, cwd.length - suffix.length);
  var test = path.join (root, 'test');
  var files = fs.readdirSync (path.join (cwd, 'templates'));

  mkdirSync (test);
  mkdirSync (path.join (root, 'src'));
  mkdirSync (path.join (root, 'src.test'));

  files.forEach (function (file) {
    var data = fs.readFileSync (path.join (cwd, 'templates', file));
    if (file.substr (0, 2) === '--') {
      var name = file.substr (2, file.length - 2);
      fs.writeFileSync (path.join (root, name), data);
    } else {
      fs.writeFileSync (path.join (test, file), data);
    }
  });
}
<commit_msg>Use the right line terminator accordingly to the platform<commit_after>'use strict';
/* global require */
/* global process */

var fs = require ('fs');
var path = require ('path');

var mkdirSync = function (path) {
  try {
    fs.mkdirSync (path);
  } catch (e) {
    if (e.code !== 'EEXIST') {
      throw e;
    }
  }
};

var cwd = process.cwd ().replace (/\\/g, '/');
var suffix = '/node_modules/mai-chai';

if (cwd.endsWith (suffix)) {
  var root = cwd.substr (0, cwd.length - suffix.length);
  var test = path.join (root, 'test');
  var files = fs.readdirSync (path.join (cwd, 'templates'));

  mkdirSync (test);
  mkdirSync (path.join (root, 'src'));
  mkdirSync (path.join (root, 'src.test'));

  var regex = /\r\n/g;
  var newline = '\n';
  if (process.platform === 'win32') {
    regex = /[^\r]\n/g;
    newline = '\r\n';
  }

  files.forEach (function (file) {
    var data = fs.readFileSync (path.join (cwd, 'templates', file));
    if (file.substr (0, 2) === '--') {
      var name = file.substr (2, file.length - 2);
      fs.writeFileSync (path.join (root, name), data.replace (regex, newline));
    } else {
      fs.writeFileSync (path.join (test, file), data.replace (regex, newline));
    }
  });
}
<|endoftext|>