<commit_before>var commentPages = []

/*************************************
 ** UI functions
 ************************************/
$(document).ready(function () {
  var videoID = $('#videoID').val()
  if (!videoID) return alert('No valid Video ID found.')
  scrapeComments(videoID)
})

function addDetailItem (id, text) {
  var li =
    '
{{text}}
'
  $(li.replace('{{text}}', text).replace('{{id}}', id))
    .hide()
    .appendTo('#detail-list')
    .show('slow')
}

function setItemCompleted (id) {
  $('#' + id).attr('class', 'fa-li fa fa-check-circle-o')
}

function setItemFailed (id) {
  $('#' + id).attr('class', 'fa-li fa fa-times-circle')
}

function updateProgressBar (progress, total, percentage) {
  percentage = percentage || Math.ceil(100 / total * progress)
  percentage = percentage <= 100 ? percentage : 100

  if (percentage < 99) {
    $('.progress-bar')
      .attr('aria-valuenow', percentage)
      .attr('style', 'width: ' + percentage + '%')
    $('.progress-bar').text(progress + ' / ' + total)
  } else {
    $('.progress-bar').attr('aria-valuenow', 100).attr('style', 'width: 100%')
    $('.progress-bar').text('Done!')
    $('.progress').attr('class', 'progress')
  }
}

function showProcessSpinner () {
  $('.well-progress').fadeIn('fast')
  $('#options-row').css('opacity', '0.4')
  $('#options-row *').attr('disabled', 'disabled')
  $('.tab-pane').css('visibility', 'hidden')
}

function hideProcessSpinner () {
  $('#options-row *').removeAttr('disabled')
  $('#options-row').css('opacity', '1')
  $('.well-progress').fadeOut('fast')
  $('.tab-pane').css('visibility', 'visible')
}

function displayVideoDetails (title, commentCount, videoId) {
  $('#video-detail .video-title').text(title)
  $('#video-detail .video-title').attr(
    'href',
    'https://www.youtube.com/watch?v=' + videoId
  )
  $('#video-detail .comment-count').text(commentCount + ' comments')
  setTimeout(function () {
    $('#video-detail .video-embed iframe').attr(
      'src',
      '//www.youtube.com/embed/' + videoId + '?rel=0'
    )
    $('#video-detail .video-embed').slideDown('fast')
  }, 800)

  $('.video-detail').slideDown('slow')
}

/*************************************
 ** Scraping functions
 ************************************/

function scrapeComments (videoID) {
  addDetailItem('i-fetch-details', 'Fetching video details')
  var fetchCount = 0
  fetch()

  function fetch (pageToken) {
    fetchCommentPage(videoID, pageToken, function (err, commentPage) {
      if (err) {
        setItemFailed('i-fetch-details')
        updateProgressBar(0, 0, 0)

        $('#video-error-modal #error-message').html(buildErrorMessage(err))

        $('#video-error-modal').modal({
          backdrop: 'static',
          keyboard: false
        })
        $('#video-error-modal').show()
        return
      }

      if (!fetchCount) {
        setItemCompleted('i-fetch-details')
        addDetailItem(
          'i-scrape-comments',
          'Scraping ' + commentPage.videoCommentCount + ' comments'
        )
        setTimeout(function () {
          displayVideoDetails(
            commentPage.videoTitle,
            commentPage.videoCommentCount,
            videoID
          )
        }, 800)
      }

      fetchCount += commentPage.comments.reduce(function (total, comment) {
        var replies = comment.replies ? comment.replies.length : 0
        return total + 1 + replies
      }, 0)

      commentPages.push(commentPage)
      updateProgressBar(fetchCount, commentPage.videoCommentCount)

      if (commentPage.nextPageToken) {
        fetch(commentPage.nextPageToken)
      } else {
        setItemCompleted('i-scrape-comments')
        addDetailItem('i-process-results', 'Processing results')
        updateProgressBar(fetchCount, commentPage.videoCommentCount, 100)
        displayResults(videoID)
      }
    })
  }
}

function fetchCommentPage (videoID, pageToken, callback) {
  var data = {
    videoID: videoID
  }
  if (pageToken) {
    data['pageToken'] = pageToken
  }

  $.ajax({
    type: 'POST',
    accepts: 'json',
    url: '/api',
    data: data,
    success: successHandler,
    error: errorHandler
  })

  function successHandler (data, status, xhr) {
    callback(null, data)
  }

  function errorHandler (xhr, status, error) {
    callback(xhr.responseJSON || {})
  }
}

function buildErrorMessage (error) {
  switch (error.type) {
    case 'video-error':
      return 'The comments on this video cannot be scraped.'
    case 'video-error/country-restriction':
      return (
        'This video is not available in the location of the server. ' +
        'Please ensure the video is available in Canada.'
      )
    case 'video-error/unavailable':
      return error.message || 'The comments on this video cannot be scraped.'
    case 'video-error/private':
      return 'This video is private. Comments on a private video cannot be scraped.'
    case 'video-error/no-comments':
      return "It appears this video doesn't have any comments. There's nothing to scrape."
    case 'scraper-error':
      return 'Uh oh, something went wrong. ' + (error.message || '')
    default:
      return 'Uh oh, something went wrong. ' + (error.message || '')
  }
}

/*************************************
 ** Result Processing functions
 ************************************/

function displayResults (videoID) {
  $('#result-container').attr('class', '')
  $('#result-container').show('slow')

  loadPages(function () {
    setItemCompleted('i-process-results')
    addDetailItem('i-done', 'Done!')
    setItemCompleted('i-done')
  })

  $('#save-json').click(function () {
    downloadJson(videoID)
  })

  $('#save-csv').click(function () {
    downloadCsv(videoID)
  })

  $('#result-container .well :checkbox').on('ifToggled', function () {
    return loadPages()
  })
}

var lastPageIndex = 0

function loadPages (callback) {
  showProcessSpinner()

  setTimeout(function () {
    var pagesToDisplay = []
    for (var i = 0; i <= lastPageIndex; i++) {
      pagesToDisplay.push(commentPages[i])
    }

    var buttonHtml =
      '<button type="button" class="btn btn-default btn-block" onclick="lastPageIndex++; loadPages()">Show more</button>'
    var resultArray = buildResultArray(pagesToDisplay, getFieldOptions())
    $('#json-result').html(generateJsonOutput(resultArray))
    $('#csv-result').html(generateCsvOutput(resultArray))

    if (commentPages[lastPageIndex + 1]) {
      $('#json-result').append('
' + buttonHtml + '
')
      $('#csv-result').append('
' + buttonHtml + '
')
    }
    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function downloadJson (videoID, callback) {
  showProcessSpinner()

  setTimeout(function () {
    var requiredFields = getFieldOptions()
    var resultArray = buildResultArray(commentPages, requiredFields)
    download(
      generateJsonFile(resultArray),
      'comments-' + videoID + '.json',
      'text/plain'
    )

    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function downloadCsv (videoID, callback) {
  showProcessSpinner()

  setTimeout(function () {
    var requiredFields = getFieldOptions()
    var resultArray = buildResultArray(commentPages, requiredFields)
    download(
      generateCsvFile(resultArray),
      'comments-' + videoID + '.csv',
      'text/plain'
    )

    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function getFieldOptions () {
  return jQuery
    .makeArray($('#result-container .well input:checked'))
    .map(function (elem) {
      return $(elem).val()
    })
}

function generateJsonFile (resultArray) {
  return JSON.stringify(resultArray, null, 2)
}

function generateCsvFile (resultArray) {
  var flattened = flattenResultArray(resultArray)
  return Papa.unparse(JSON.stringify(flattened))
}

function generateJsonOutput (resultArray) {
  return JsonHuman.format(resultArray)
}

function generateCsvOutput (resultArray) {
  var flattened = flattenResultArray(resultArray)
  return generateHtmlTable(flattened)
}

function generateHtmlTable (flattenedResultArray) {
  if (!flattenedResultArray || !flattenedResultArray.length) return ''
  var props = Object.keys(flattenedResultArray[0])

  return [
    '
',
    '
',
    '
',
    props.reduce(function (str, prop) {
      return (str += '' + prop + '	')
    }, ''),
    '
',
    '
',
    '
',
    flattenedResultArray.reduce(function (str, row) {
      return (str +=
        '
' +
        Object.keys(row).reduce(function (rowStr, key) {
          return (rowStr +=
            '' + row[key] + '	')
        }, '') +
        '
\n')
    }, ''),
    '
',
    '
'
  ].join('\n')
}

function buildResultArray (commentPages, fields) {
  if (!fields || !fields.length) return []

  return commentPages.reduce(function (comments, page) {
    if (comments.length) {
      // look for any overlap and remove it
      for (var i = 0; i < 20; i++) {
        if (comments[comments.length - 1 - i].id === page.comments[0].id) {
          page.comments.splice(0, 1)
        } else {
          break
        }
      }
    }
    return comments.concat(
      page.comments.map(function (comment) {
        return filterCommentFields(comment, fields)
      })
    )
  }, [])
}

function filterCommentFields (comment, fields) {
  var c = {}
  fields.forEach(function (prop) {
    if (prop === 'replies') {
      c['hasReplies'] = comment['hasReplies']
      c['numberOfReplies'] = comment['numberOfReplies'] || 0
      if (comment.replies) {
        c.replies = []
        comment.replies.forEach(function (reply) {
          var r = {}
          fields.forEach(function (replyProp) {
            if (replyProp !== 'replies') {
              r[replyProp] = reply[replyProp]
            }
          })
          c.replies.push(r)
        })
      }
    } else if (typeof comment[prop] !== 'undefined' && comment[prop] !== null) {
      c[prop] = comment[prop] !== 0 ? comment[prop] || '' : 0
    }
  })
  return c
}

function flattenResultArray (resultArray) {
  var result = []
  var props = findProps(resultArray)

  resultArray.forEach(function (elem) {
    var comment = {}
    props.forEach(function (prop) {
      comment[prop] = ''
    })

    Object.keys(elem).forEach(function (key) {
      if (key !== 'replies') {
        comment[key] = elem[key]
      }
    })
    result.push(comment)

    if (elem.replies) {
      elem.replies.forEach(function (replyElem) {
        var reply = {}
        props.forEach(function (prop) {
          reply[prop] = ''
        })

        Object.keys(replyElem).forEach(function (key) {
          reply['replies.' + key] = replyElem[key]
        })
        result.push(reply)
      })
    }
  })

  return result

  function findProps (arr) {
    var props = {}
    arr.forEach(function (obj) {
      Object.keys(obj).forEach(function (key) {
        if (Array.isArray(obj[key])) {
          findProps(obj[key]).forEach(function (subKey) {
            props[key + '.' + subKey] = true
          })
        } else {
          props[key] = true
        }
      })
    })
    return Object.keys(props)
  }
}
<commit_msg>continue scraping for unavailable videos if possible<commit_after>var commentPages = []

/*************************************
 ** UI functions
 ************************************/
$(document).ready(function () {
  var videoID = $('#videoID').val()
  if (!videoID) return alert('No valid Video ID found.')
  scrapeComments(videoID)
})

function addDetailItem (id, text) {
  var li =
    '
{{text}}
'
  $(li.replace('{{text}}', text).replace('{{id}}', id))
    .hide()
    .appendTo('#detail-list')
    .show('slow')
}

function setItemCompleted (id) {
  $('#' + id).attr('class', 'fa-li fa fa-check-circle-o')
}

function setItemFailed (id) {
  $('#' + id).attr('class', 'fa-li fa fa-times-circle')
}

function updateProgressBar (progress, total, percentage) {
  percentage = percentage || Math.ceil(100 / total * progress)
  percentage = percentage <= 100 ? percentage : 100

  if (percentage < 99 && total > 0) {
    $('.progress-bar')
      .attr('aria-valuenow', percentage)
      .attr('style', 'width: ' + percentage + '%')
    $('.progress-bar').text(progress + ' / ' + total)
  } else {
    $('.progress-bar').attr('aria-valuenow', 100).attr('style', 'width: 100%')
    $('.progress-bar').text('Done!')
    $('.progress').attr('class', 'progress')
  }
}

function showProcessSpinner () {
  $('.well-progress').fadeIn('fast')
  $('#options-row').css('opacity', '0.4')
  $('#options-row *').attr('disabled', 'disabled')
  $('.tab-pane').css('visibility', 'hidden')
}

function hideProcessSpinner () {
  $('#options-row *').removeAttr('disabled')
  $('#options-row').css('opacity', '1')
  $('.well-progress').fadeOut('fast')
  $('.tab-pane').css('visibility', 'visible')
}

function displayVideoDetails (title, commentCount, videoId) {
  $('#video-detail .video-title').text(title)
  $('#video-detail .video-title').attr(
    'href',
    'https://www.youtube.com/watch?v=' + videoId
  )

  if (commentCount) {
    $('#video-detail .comment-count').text(commentCount + ' comments')
  }

  setTimeout(function () {
    $('#video-detail .video-embed iframe').attr(
      'src',
      '//www.youtube.com/embed/' + videoId + '?rel=0'
    )
    $('#video-detail .video-embed').slideDown('fast')
  }, 800)

  $('.video-detail').slideDown('slow')
}

/*************************************
 ** Scraping functions
 ************************************/

function scrapeComments (videoID) {
  addDetailItem('i-fetch-details', 'Fetching video details')
  var fetchCount = 0
  fetch()

  function fetch (pageToken) {
    fetchCommentPage(videoID, pageToken, function (err, commentPage) {
      if (err) {
        setItemFailed('i-fetch-details')
        updateProgressBar(0, 0, 0)

        $('#video-error-modal #error-message').html(buildErrorMessage(err))

        $('#video-error-modal').modal({
          backdrop: 'static',
          keyboard: false
        })
        $('#video-error-modal').show()
        return
      }

      if (!fetchCount) {
        setItemCompleted('i-fetch-details')
        addDetailItem(
          'i-scrape-comments',
          ['Scraping', commentPage.videoCommentCount, 'comments']
            .filter(Boolean)
            .join(' ')
        )
        setTimeout(function () {
          displayVideoDetails(
            commentPage.videoTitle,
            commentPage.videoCommentCount,
            videoID
          )
        }, 800)
      }

      fetchCount += commentPage.comments.reduce(function (total, comment) {
        var replies = comment.replies ? comment.replies.length : 0
        return total + 1 + replies
      }, 0)

      commentPages.push(commentPage)

      if (commentPage.videoCommentCount) {
        updateProgressBar(fetchCount, commentPage.videoCommentCount)
      }

      if (commentPage.nextPageToken) {
        fetch(commentPage.nextPageToken)
      } else {
        setItemCompleted('i-scrape-comments')
        addDetailItem('i-process-results', 'Processing results')
        updateProgressBar(fetchCount, commentPage.videoCommentCount, 100)
        displayResults(videoID)
      }
    })
  }
}

function fetchCommentPage (videoID, pageToken, callback) {
  var data = {
    videoID: videoID
  }
  if (pageToken) {
    data['pageToken'] = pageToken
  }

  $.ajax({
    type: 'POST',
    accepts: 'json',
    url: '/api',
    data: data,
    success: successHandler,
    error: errorHandler
  })

  function successHandler (data, status, xhr) {
    callback(null, data)
  }

  function errorHandler (xhr, status, error) {
    callback(xhr.responseJSON || {})
  }
}

function buildErrorMessage (error) {
  switch (error.type) {
    case 'video-error':
      return 'The comments on this video cannot be scraped.'
    case 'video-error/country-restriction':
      return (
        'This video is not available in the location of the server. ' +
        'Please ensure the video is available in Canada.'
      )
    case 'video-error/unavailable':
      return error.message || 'The comments on this video cannot be scraped.'
    case 'video-error/private':
      return 'This video is private. Comments on a private video cannot be scraped.'
    case 'video-error/no-comments':
      return "It appears this video doesn't have any comments. There's nothing to scrape."
    case 'scraper-error':
      return 'Uh oh, something went wrong. ' + (error.message || '')
    default:
      return 'Uh oh, something went wrong. ' + (error.message || '')
  }
}

/*************************************
 ** Result Processing functions
 ************************************/

function displayResults (videoID) {
  $('#result-container').attr('class', '')
  $('#result-container').show('slow')

  loadPages(function () {
    setItemCompleted('i-process-results')
    addDetailItem('i-done', 'Done!')
    setItemCompleted('i-done')
  })

  $('#save-json').click(function () {
    downloadJson(videoID)
  })

  $('#save-csv').click(function () {
    downloadCsv(videoID)
  })

  $('#result-container .well :checkbox').on('ifToggled', function () {
    return loadPages()
  })
}

var lastPageIndex = 0

function loadPages (callback) {
  showProcessSpinner()

  setTimeout(function () {
    var pagesToDisplay = []
    for (var i = 0; i <= lastPageIndex; i++) {
      pagesToDisplay.push(commentPages[i])
    }

    var buttonHtml =
      '<button type="button" class="btn btn-default btn-block" onclick="lastPageIndex++; loadPages()">Show more</button>'
    var resultArray = buildResultArray(pagesToDisplay, getFieldOptions())
    $('#json-result').html(generateJsonOutput(resultArray))
    $('#csv-result').html(generateCsvOutput(resultArray))

    if (commentPages[lastPageIndex + 1]) {
      $('#json-result').append('
' + buttonHtml + '
')
      $('#csv-result').append('
' + buttonHtml + '
')
    }
    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function downloadJson (videoID, callback) {
  showProcessSpinner()

  setTimeout(function () {
    var requiredFields = getFieldOptions()
    var resultArray = buildResultArray(commentPages, requiredFields)
    download(
      generateJsonFile(resultArray),
      'comments-' + videoID + '.json',
      'text/plain'
    )

    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function downloadCsv (videoID, callback) {
  showProcessSpinner()

  setTimeout(function () {
    var requiredFields = getFieldOptions()
    var resultArray = buildResultArray(commentPages, requiredFields)
    download(
      generateCsvFile(resultArray),
      'comments-' + videoID + '.csv',
      'text/plain'
    )

    hideProcessSpinner()
    if (callback) callback()
  }, 0)
}

function getFieldOptions () {
  return jQuery
    .makeArray($('#result-container .well input:checked'))
    .map(function (elem) {
      return $(elem).val()
    })
}

function generateJsonFile (resultArray) {
  return JSON.stringify(resultArray, null, 2)
}

function generateCsvFile (resultArray) {
  var flattened = flattenResultArray(resultArray)
  return Papa.unparse(JSON.stringify(flattened))
}

function generateJsonOutput (resultArray) {
  return JsonHuman.format(resultArray)
}

function generateCsvOutput (resultArray) {
  var flattened = flattenResultArray(resultArray)
  return generateHtmlTable(flattened)
}

function generateHtmlTable (flattenedResultArray) {
  if (!flattenedResultArray || !flattenedResultArray.length) return ''
  var props = Object.keys(flattenedResultArray[0])

  return [
    '
',
    '
',
    '
',
    props.reduce(function (str, prop) {
      return (str += '' + prop + '	')
    }, ''),
    '
',
    '
',
    '
',
    flattenedResultArray.reduce(function (str, row) {
      return (str +=
        '
' +
        Object.keys(row).reduce(function (rowStr, key) {
          return (rowStr +=
            '' + row[key] + '	')
        }, '') +
        '
\n')
    }, ''),
    '
',
    '
'
  ].join('\n')
}

function buildResultArray (commentPages, fields) {
  if (!fields || !fields.length) return []

  return commentPages.reduce(function (comments, page) {
    if (comments.length) {
      // look for any overlap and remove it
      for (var i = 0; i < 20; i++) {
        if (comments[comments.length - 1 - i].id === page.comments[0].id) {
          page.comments.splice(0, 1)
        } else {
          break
        }
      }
    }
    return comments.concat(
      page.comments.map(function (comment) {
        return filterCommentFields(comment, fields)
      })
    )
  }, [])
}

function filterCommentFields (comment, fields) {
  var c = {}
  fields.forEach(function (prop) {
    if (prop === 'replies') {
      c['hasReplies'] = comment['hasReplies']
      c['numberOfReplies'] = comment['numberOfReplies'] || 0
      if (comment.replies) {
        c.replies = []
        comment.replies.forEach(function (reply) {
          var r = {}
          fields.forEach(function (replyProp) {
            if (replyProp !== 'replies') {
              r[replyProp] = reply[replyProp]
            }
          })
          c.replies.push(r)
        })
      }
    } else if (typeof comment[prop] !== 'undefined' && comment[prop] !== null) {
      c[prop] = comment[prop] !== 0 ? comment[prop] || '' : 0
    }
  })
  return c
}

function flattenResultArray (resultArray) {
  var result = []
  var props = findProps(resultArray)

  resultArray.forEach(function (elem) {
    var comment = {}
    props.forEach(function (prop) {
      comment[prop] = ''
    })

    Object.keys(elem).forEach(function (key) {
      if (key !== 'replies') {
        comment[key] = elem[key]
      }
    })
    result.push(comment)

    if (elem.replies) {
      elem.replies.forEach(function (replyElem) {
        var reply = {}
        props.forEach(function (prop) {
          reply[prop] = ''
        })

        Object.keys(replyElem).forEach(function (key) {
          reply['replies.' + key] = replyElem[key]
        })
        result.push(reply)
      })
    }
  })

  return result

  function findProps (arr) {
    var props = {}
    arr.forEach(function (obj) {
      Object.keys(obj).forEach(function (key) {
        if (Array.isArray(obj[key])) {
          findProps(obj[key]).forEach(function (subKey) {
            props[key + '.' + subKey] = true
          })
        } else {
          props[key] = true
        }
      })
    })
    return Object.keys(props)
  }
}
<|endoftext|>


<commit_before>var ensure   = require('ensure')
  , cfg      = require('../../cfg/tests.js')
  , nano     = require('../../nano')(cfg)
  , db_name  = require('../utils').db_name("doc_up")
  , tests    = exports;

function db(i) { return nano.use(db_name(i)); }

tests.update_doc = function (callback) {
  nano.db.create(db_name('a'), function () {
    db('a').insert({foo: "bar"}, "foo", function (_,b) {
      db('a').insert({"_rev": b.rev, foo: "baz"}, "foo", callback);
    });
  });
};

tests.update_doc_ok = function (e,b) {
  nano.db.destroy(db_name('a'));
  this.t.notOk(e);
  this.t.equal(b.id, "foo");
  this.t.ok(b.ok);
  this.t.ok(b.rev);
};

ensure(__filename,tests,module,process.argv[2]);<commit_msg>[nock] doc/update<commit_after>var ensure   = require('ensure')
  , nock     = require('nock')
  , cfg      = require('../../cfg/tests.js')
  , nano     = require('../../nano')(cfg)
  , db_name  = require('../utils').db_name("doc_up")
  , tests    = exports
  , couch
  ;

  couch = nock(cfg.url)
    .put('/' + db_name('a'))
    .reply(201, "{\"ok\":true}\n",
      { server: 'CouchDB/1.1.1 (Erlang OTP/R14B04)',
        location: cfg.url + '/' + db_name('a'),
        date: 'Fri, 02 Dec 2011 19:38:03 GMT',
        'content-type': 'application/json',
        'content-length': '12',
        'cache-control': 'must-revalidate' })
    .put('/' + db_name('a') + '/foo', {"foo": "bar"})
    .reply(201, "{\"ok\": true,\"id\": \"foo\",\"rev\": \"1-4c6114c65e295552ab1019e2b046b10e\"}\n",
      { server: 'CouchDB/1.1.1 (Erlang OTP/R14B04)',
        location: cfg.url + '/' + db_name('a') + '/foo',
        etag: '"1-4c6114c65e295552ab1019e2b046b10e"',
        date: 'Fri, 02 Dec 2011 19:38:04 GMT',
        'content-type': 'application/json',
        'content-length': '66',
        'cache-control': 'must-revalidate' })
    .put('/' + db_name('a') + '/foo',
      { "_rev": "1-4c6114c65e295552ab1019e2b046b10e"
      , "foo":  "baz"
      })
    .reply(201, "{\"ok\": true,\"id\": \"foo\",\"rev\": \"2-cfcd6781f13994bde69a1c3320bfdadb\"}\n",
      { server: 'CouchDB/1.1.1 (Erlang OTP/R14B04)',
        location: cfg.url + '/' + db_name('a') + '/foo',
        etag: '"2-cfcd6781f13994bde69a1c3320bfdadb"',
        date: 'Fri, 02 Dec 2011 19:38:04 GMT',
        'content-type': 'application/json',
        'content-length': '66',
        'cache-control': 'must-revalidate' });

function db(i) { return nano.use(db_name(i)); }

tests.update_doc = function (callback) {
  nano.db.create(db_name('a'), function () {
    db('a').insert({foo: "bar"}, "foo", function (_,b) {
      db('a').insert({"_rev": b.rev, foo: "baz"}, "foo", callback);
    });
  });
};

tests.update_doc_ok = function (e,b) {
  this.t.notOk(e, 'I got err free status');
  this.t.equal(b.id, "foo", 'My filename is foo');
  this.t.ok(b.ok, 'I am now ok');
  this.t.ok(b.rev, 'I got rev');
  this.t.ok(couch.isDone(), 'Nock is done');
};

ensure(__filename,tests,module,process.argv[2]);<|endoftext|>


<commit_before>describe('Enumerable', function () {
    describe('creation', function () {
        it('should allow creation from an existing array', function () {
            var array = [1, 2, 3];

            var enumerable = array.asEnumerable();

            expect(enumerable).to.exist;
        });

        it('should allow creation from a constructor', function () {
            var enumerable = new Enumerable();

            expect(enumerable).to.be.instanceOf(Enumerable);
        });

        it('should allow creation without a constructor', function () {
            var enumerable = Enumerable();

            expect(enumerable).to.be.instanceOf(Enumerable);
        });
    });

    describe('range', function () {
        it('should allow the creation of a range enumerable', function () {
            var range = Enumerable.range(1, 2);

            expect(range).to.exist;
        });

        it('should return values within the range', function () {
            var range = Enumerable.range(1, 2);

            var index = 1;
            for (var x in range) {
                expect(x).to.equal(index);
                index++;
            }
        });
    });

    describe('toArray', function () {
        it('should allow conversion back to an array', function () {
            var items = [1,2,3].asEnumerable().toArray();

            expect(items).isArray;
        });
    });
});<commit_msg>Expanding out the range tests to check chaining<commit_after>describe('Enumerable', function () {
    describe('creation', function () {
        it('should allow creation from an existing array', function () {
            var array = [1, 2, 3];

            var enumerable = array.asEnumerable();

            expect(enumerable).to.exist;
        });

        it('should allow creation from a constructor', function () {
            var enumerable = new Enumerable();

            expect(enumerable).to.be.instanceOf(Enumerable);
        });

        it('should allow creation without a constructor', function () {
            var enumerable = Enumerable();

            expect(enumerable).to.be.instanceOf(Enumerable);
        });
    });

    describe('range', function () {
        it('should allow the creation of a range enumerable', function () {
            var range = Enumerable.range(1, 2);

            expect(range).to.exist;
        });

        it('should return values within the range', function () {
            var range = Enumerable.range(1, 2);

            var index = 1;
            for (var x in range) {
                expect(x).to.equal(index);
                index++;
            }
        });

        it('should create a range of the expected length', function () {
            var range = Enumerable.range(1, 2);

            expect(range.toArray().length).to.equal(2);
        });

        it('should allow a select from a range', function () {
            var range = Enumerable.range(0, 10);

            var selected = range.select(x => x + 1);

            expect(selected.toArray()).to.deep.equal([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
        });

        it('should allow a take from a range', function () {
            var range = Enumerable.range(0, 10);

            var taken = range.take(1);

            expect(taken.toArray()).to.deep.equal([0]);
        });
    });

    describe('toArray', function () {
        it('should allow conversion back to an array', function () {
            var items = [1,2,3].asEnumerable().toArray();

            expect(items).isArray;
        });
    });
});<|endoftext|>


<commit_before>// 话题（帖子）相关js功能
(function(){
	emojify.setConfig({
		// emojify_tag_type : 'span',
		only_crawl_id    : null,
		img_dir          : SG.EMOJI_DOMAIN,
		ignored_tags     : { //忽略以下几种标签内的emoji识别
			'SCRIPT'  : 1,
			'TEXTAREA': 1,
			'A'       : 1,
			'PRE'     : 1,
			'CODE'    : 1
		}
	});

	SG.Topics = function(){}
	SG.Topics.prototype = new SG.Publisher();
	SG.Topics.prototype.parseContent = function(selector) {
		// 配置 marked 语法高亮
		marked = SG.markSettingNoHightlight();

		selector.each(function() {
			var markdownString = $(this).text();

			var contentHtml = marked(markdownString);
			contentHtml = SG.replaceCodeChar(contentHtml);

			$(this).html(contentHtml);

			// emoji 表情解析
			emojify.run(this);
		});
	}

	jQuery(document).ready(function($) {
		// 发布主题
		$('#submit').on('click', function(evt){
			evt.preventDefault();
			var validator = $('.validate-form').validate();
			if (!validator.form()) {
				return false;
			}

			if ($('.usernames').length != 0) {
				var usernames = SG.analyzeAt($('#content').val());
				$('.usernames').val(usernames);
			}

			var topics = new SG.Topics();
			topics.publish(this, function(data) {
				purgeComposeDraft(uid, 'topic');

				setTimeout(function(){
					if (data.tid) {
						window.location.href = '/topics/'+data.tid;
					} else {
						window.location.href = '/topics';
					}
				}, 1000);
			});
		});

		$(document).keypress(function(evt){
			if (evt.ctrlKey && (evt.which == 10 || evt.which == 13)) {
				$('#submit').click();
			}
		});

		SG.registerAtEvent();
	});
}).call(this);
<commit_msg>临时处理 xss<commit_after>// 话题（帖子）相关js功能
(function(){
	emojify.setConfig({
		// emojify_tag_type : 'span',
		only_crawl_id    : null,
		img_dir          : SG.EMOJI_DOMAIN,
		ignored_tags     : { //忽略以下几种标签内的emoji识别
			'SCRIPT'  : 1,
			'TEXTAREA': 1,
			'A'       : 1,
			'PRE'     : 1,
			'CODE'    : 1
		}
	});

	SG.Topics = function(){}
	SG.Topics.prototype = new SG.Publisher();
	SG.Topics.prototype.parseContent = function(selector) {
		// 配置 marked 语法高亮
		marked = SG.markSettingNoHightlight();

		selector.each(function() {
			var markdownString = $(this).text();

			var contentHtml = marked(markdownString);

			// JS 处理，避免 XSS。最终还是改为服务端渲染更好
			if (contentHtml.indexOf('<script') != -1) {
				contentHtml = contentHtml.replace(/<script/g, '<script');
			}
			if (contentHtml.indexOf('<form') != -1) {
				contentHtml = contentHtml.replace(/<form/g, '<form');
			}
			if (contentHtml.indexOf('<input') != -1) {
				contentHtml = contentHtml.replace(/<input/g, '<input');
			}
			if (contentHtml.indexOf('<select') != -1) {
				contentHtml = contentHtml.replace(/<select/g, '<select');
			}
			if (contentHtml.indexOf('<textarea') != -1) {
				contentHtml = contentHtml.replace(/<textarea/g, '<textarea');
			}

			contentHtml = SG.replaceCodeChar(contentHtml);

			$(this).html(contentHtml);

			// emoji 表情解析
			emojify.run(this);
		});
	}

	jQuery(document).ready(function($) {
		// 发布主题
		$('#submit').on('click', function(evt){
			evt.preventDefault();
			var validator = $('.validate-form').validate();
			if (!validator.form()) {
				return false;
			}

			if ($('.usernames').length != 0) {
				var usernames = SG.analyzeAt($('#content').val());
				$('.usernames').val(usernames);
			}

			var topics = new SG.Topics();
			topics.publish(this, function(data) {
				purgeComposeDraft(uid, 'topic');

				setTimeout(function(){
					if (data.tid) {
						window.location.href = '/topics/'+data.tid;
					} else {
						window.location.href = '/topics';
					}
				}, 1000);
			});
		});

		$(document).keypress(function(evt){
			if (evt.ctrlKey && (evt.which == 10 || evt.which == 13)) {
				$('#submit').click();
			}
		});

		SG.registerAtEvent();
	});
}).call(this);
<|endoftext|>


<commit_before>
'use strict';

const Test = require('tape');
const ReadFile = require('fs').readFile;

const Punc = require('../');

/* TESTS */
Test('Punc: exists?', t => {
  t.plan(1);
  t.equal(typeof Punc, 'function', 'it exists');
});

Test('Punc: handles null filePath argument?', t => {
  t.plan(1);
  t.throws(_ => Punc(), 'error throws');
});

Test('Punc: returns a Promise?', t => {
  t.plan(2);
  let module = Punc('tests/books/alice.txt');
  t.ok(module.then, 'is then-able');
  t.ok(module.catch, 'is catch-able');
});

Test('Punc: punctuation count', t => {
  t.plan(1);
  let punctuationMap = { ';': 2
  , ':': 0
  , '\'': 1
  , '"': 2
  , ',': 2
  , '!': 0
  , '?': 0
  , '.': 2
  , '(': 1
  , ')': 0
  , '-': 3
  };

  return Punc('tests/books/alice.txt')
    .then(book => {
      t.ok(book.count, 'count property exists on returned object');

      for (let key in book.count) {

        if (!punctuationMap.hasOwnProperty(key)) {
          return t.fail(`Key not found: ${key}`);
        }

        if (punctuationMap[key] !== book.count[key]) {
          return t.fail(`Value mismatch.\n\
            Punctuation: ${key}\n\
            Expected: ${punctuationMap[key]}.\n\
            Actual: ${book.count[key]}`
          );
        }
      }
    })
    .catch(err => t.fail(err));
});

Test('Punc: punctuation body', t => {
  t.plan(2);

  return Punc('tests/books/alice.txt')
    .then(book => {
      t.ok(book.body, 'body property exists on returned object');
      t.equals(book.body, '-;,.\'(-;-.,""');
    })
    .catch(err => t.fail(err));
});

Test('Punc: words per sentence', t => {
  t.plan(2);

  return Punc('tests/books/word_count.txt')
    .then(book => {
      t.ok(book.wordsPerSentence, 'wordCount property exists on returned object');
      t.equals(book.wordsPerSentence, 2.5);
    })
    .catch(err => t.fail(err));
});

Test('Punc: spaced out text', t => {
  t.plan(2);

  return Punc('tests/books/word_count.txt')
    .then(book => {
      t.ok(book.spaced, 'spaced property exists on returned object');
      t.equals(book.spaced, ' .    !      ?        .');
    })
    .catch(err => t.fail(err));
})

Test('Punc.createPDF: method exists?', t => {
  t.equals(typeof Punc.createPDF, 'function', 'createPDF method exists.');
  t.end();
});

Test('Punc.createPDF: handles null filePath argument?', t => {
  t.plan(1);
  t.throws(_ => Punc.createPDF(), 'error throws');
});

Test('Punc.createPDF: creates PDF file', t => {
  t.plan(2);

  let filePath = 'tests/books/dream_speech';

  Punc.createPDF(filePath)
    .then(success => {
      t.pass('Punc returns success message');

      ReadFile(filePath, (err, data) => {
        if (err) return reject(err);
        if (data) return t.pass('pdf file exists');
      });
    })
    .catch(err => t.fail(err));
});
<commit_msg>saving notes<commit_after>
'use strict';

const Test = require('tape');
const ReadFile = require('fs').readFile;

const Punc = require('../');

/* TESTS */
Test('Punc: exists?', t => {
  t.plan(1);
  t.equal(typeof Punc, 'function', 'it exists');
});

Test('Punc: handles null filePath argument?', t => {
  t.plan(1);
  t.throws(_ => Punc(), 'error throws');
});

Test('Punc: returns a Promise?', t => {
  t.plan(2);
  let module = Punc('tests/books/alice.txt');
  t.ok(module.then, 'is then-able');
  t.ok(module.catch, 'is catch-able');
});

Test('Punc: punctuation count', t => {
  t.plan(1);
  let punctuationMap = { ';': 2
  , ':': 0
  , '\'': 1
  , '"': 2
  , ',': 2
  , '!': 0
  , '?': 0
  , '.': 2
  , '(': 1
  , ')': 0
  , '-': 3
  };

  return Punc('tests/books/alice.txt')
    .then(book => {
      t.ok(book.count, 'count property exists on returned object');

      // TODO: refactor into functional?
      for (let key in book.count) {

        if (!punctuationMap.hasOwnProperty(key)) {
          return t.fail(`Key not found: ${key}`);
        }

        if (punctuationMap[key] !== book.count[key]) {
          return t.fail(`Value mismatch.\n\
            Punctuation: ${key}\n\
            Expected: ${punctuationMap[key]}.\n\
            Actual: ${book.count[key]}`
          );
        }
      }
    })
    .catch(err => t.fail(err));
});

Test('Punc: punctuation body', t => {
  t.plan(2);

  return Punc('tests/books/alice.txt')
    .then(book => {
      t.ok(book.body, 'body property exists on returned object');
      t.equals(book.body, '-;,.\'(-;-.,""');
    })
    .catch(err => t.fail(err));
});

Test('Punc: words per sentence', t => {
  t.plan(2);

  return Punc('tests/books/word_count.txt')
    .then(book => {
      t.ok(book.wordsPerSentence, 'wordCount property exists on returned object');
      t.equals(book.wordsPerSentence, 2.5);
    })
    .catch(err => t.fail(err));
});

Test('Punc: spaced out text', t => {
  t.plan(2);

  return Punc('tests/books/word_count.txt')
    .then(book => {
      t.ok(book.spaced, 'spaced property exists on returned object');
      t.equals(book.spaced, ' .    !      ?        .');
    })
    .catch(err => t.fail(err));
})

Test('Punc.createPDF: method exists?', t => {
  t.equals(typeof Punc.createPDF, 'function', 'createPDF method exists.');
  t.end();
});

Test('Punc.createPDF: handles null filePath argument?', t => {
  t.plan(1);
  t.throws(_ => Punc.createPDF(), 'error throws');
});

Test('Punc.createPDF: creates PDF file', t => {
  t.plan(2);

  let filePath = 'tests/books/dream_speech';

  Punc.createPDF(filePath)
    .then(success => {
      t.pass('Punc returns success message');

      ReadFile(filePath, (err, data) => {
        if (err) return reject(err);
        if (data) return t.pass('pdf file exists');
      });
    })
    .catch(err => t.fail(err));
});
<|endoftext|>


<commit_before>var assert = require('assert');
var http = require('http');

var fakeLogger = { error: function() {}, info: function() {} };
var fakeAuth = {
	setup: function() {},
	ensureZombie: function(req, res, next) { next(); },
	ensureCitizen: function(req, res, next) { next(); },
	ensureRuler: function(req, res, next) { next(); },
	ensureRulerOrOwner: function(req, res, next) { next(); } };
var fakePostsController = {
	getRootRouteHandler: function(req, res) {
		res.end("root - GET");
	},
	getReadRouteHandler: function(req, res) {
		res.end("read post - GET");
	},
	deletePostRouteHandler: function(req, res){
		res.end("deleting post");
	},
	getEditRouteHandler: function(req, res){
		res.end("edit post - GET");
	},
	postEditRouteHandler: function(req, res) {
		res.end("edit post - POST");
	},
	getCreateRouteHandler: function(req, res) {
		res.end("create post - GET");
	},
	postCreateRouteHandler: function(req, res) {
		res.end("create post - POST");
	},
	getPostsRouteHandler: function(req, res) {
		res.end("all posts - GET");
	},
	aboutRouteHandler: function(req, res) {
		res.end("about - GET");
	},
	contactRouteHandler: function(req, res) {
		res.end("contact - GET");
	},
	getMyPostsRouteHandler: function(req, res) {
		res.end("my posts - GET");
	}
};
var fakeUsersController = {
	getAllUsersRouteHandler: function(req, res) {
		res.end("User list - GET");
	},
	getUserRouteHandler: function(req, res) {
		res.end("read user - GET");
	},
	postUserRouteHandler: function(req, res) {
		res.end("edit user - POST");
	}
};
var fakeImageUploadController = {
	uploadImageRouteHandler: function(req, res) {
		res.end("upload image - POST");
	}
};

var fakeScheduler = { start: function() {} };

describe('Server initialization tests', function() {
	var server = require('../server')(fakeLogger, fakeAuth, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('Server should be started', function(done) {
		server.start(function(err) {
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});

	it('Server should be started again', function(done) {
		server.start(function(err) {
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});

	it('Server can be stopped multiple times', function(done) {
		server.start(function(err) {
			server.stop();
			server.stop();
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});
});

describe('Route authorization tests [successfull auth]', function() {
	var server = require('../server')(fakeLogger, fakeAuth, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('GET root call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/posts/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET edit post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET create post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/create", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET users list call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET user detail call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users/someUser", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('POST image - 200 is returned', function(done) {
		server.start(function(err, addr) {
			var options = {
				hostname: 'localhost',
				port: addr.port,
				path: '/uploadImage',
				method: 'POST'
			};
			http.request(options, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			}).end();
		});
	});
});

describe('Route authorization tests [failed auth]', function() {
	var fakeAuth2 = {
		setup: function(){},
		ensureZombie: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureCitizen: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureRuler: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureRulerOrOwner: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); }
	};
	var server = require('../server')(fakeLogger, fakeAuth2, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('GET root call [not auth] - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET post call [not auth] - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/posts/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET edit post call [not ruler|owner] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET create post call [not citizen] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET users list call [not ruler] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET user detail call [not ruler] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users/someUser", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('POST image [not ruler|owner] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			var options = {
				hostname: 'localhost',
				port: addr.port,
				path: '/uploadImage',
				method: 'POST'
			};
			http.request(options, function(res) {
				assert.equal(res.statusCode, 401);
				done();
			}).end();
		});
	});
});
<commit_msg>fixed tests<commit_after>var assert = require('assert');
var http = require('http');

var fakeLogger = { error: function() {}, info: function() {} };
var fakeAuth = {
	setup: function() {},
	ensureZombie: function(req, res, next) { next(); },
	ensureCitizen: function(req, res, next) { next(); },
	ensureRuler: function(req, res, next) { next(); },
	ensureRulerOrOwner: function(req, res, next) { next(); },
	ensureRulerOrMyself: function(req, res, next) { next(); } };
var fakePostsController = {
	getRootRouteHandler: function(req, res) {
		res.end("root - GET");
	},
	getReadRouteHandler: function(req, res) {
		res.end("read post - GET");
	},
	deletePostRouteHandler: function(req, res){
		res.end("deleting post");
	},
	getEditRouteHandler: function(req, res){
		res.end("edit post - GET");
	},
	postEditRouteHandler: function(req, res) {
		res.end("edit post - POST");
	},
	getCreateRouteHandler: function(req, res) {
		res.end("create post - GET");
	},
	postCreateRouteHandler: function(req, res) {
		res.end("create post - POST");
	},
	getPostsRouteHandler: function(req, res) {
		res.end("all posts - GET");
	},
	aboutRouteHandler: function(req, res) {
		res.end("about - GET");
	},
	contactRouteHandler: function(req, res) {
		res.end("contact - GET");
	},
	getMyPostsRouteHandler: function(req, res) {
		res.end("my posts - GET");
	}
};
var fakeUsersController = {
	getAllUsersRouteHandler: function(req, res) {
		res.end("User list - GET");
	},
	getUserRouteHandler: function(req, res) {
		res.end("read user - GET");
	},
	postUserRouteHandler: function(req, res) {
		res.end("edit user - POST");
	}
};
var fakeImageUploadController = {
	uploadImageRouteHandler: function(req, res) {
		res.end("upload image - POST");
	}
};

var fakeScheduler = { start: function() {} };

describe('Server initialization tests', function() {
	var server = require('../server')(fakeLogger, fakeAuth, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('Server should be started', function(done) {
		server.start(function(err) {
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});

	it('Server should be started again', function(done) {
		server.start(function(err) {
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});

	it('Server can be stopped multiple times', function(done) {
		server.start(function(err) {
			server.stop();
			server.stop();
			assert.equal(err, null, 'There should be no error');
			done();
		});
	});
});

describe('Route authorization tests [successfull auth]', function() {
	var server = require('../server')(fakeLogger, fakeAuth, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('GET root call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/posts/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET edit post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET create post call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/create", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET users list call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET user detail call - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users/someUser", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('POST image - 200 is returned', function(done) {
		server.start(function(err, addr) {
			var options = {
				hostname: 'localhost',
				port: addr.port,
				path: '/uploadImage',
				method: 'POST'
			};
			http.request(options, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			}).end();
		});
	});
});

describe('Route authorization tests [failed auth]', function() {
	var fakeAuth2 = {
		setup: function(){},
		ensureZombie: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureCitizen: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureRuler: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureRulerOrOwner: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); },
		ensureRulerOrMyself: function(req, res, next){ var e = new Error("err"); e.statusCode = 401; next(e); }
	};
	var server = require('../server')(fakeLogger, fakeAuth2, fakePostsController, fakeUsersController, fakeImageUploadController, fakeScheduler);

	afterEach(function() { server.stop(); });

	it('GET root call [not auth] - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port, function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET post call [not auth] - 200 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/posts/qwerty", function(res) {
				assert.equal(res.statusCode, 200);
				done();
			});
		});
	});

	it('GET edit post call [not ruler|owner] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET create post call [not citizen] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/edit/qwerty", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET users list call [not ruler] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('GET user detail call [not ruler] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			http.get('http://localhost:' + addr.port + "/users/someUser", function(res) {
				assert.equal(res.statusCode, 401);
				done();
			});
		});
	});

	it('POST image [not ruler|owner] - 401 is returned', function(done) {
		server.start(function(err, addr) {
			var options = {
				hostname: 'localhost',
				port: addr.port,
				path: '/uploadImage',
				method: 'POST'
			};
			http.request(options, function(res) {
				assert.equal(res.statusCode, 401);
				done();
			}).end();
		});
	});
});
<|endoftext|>


<commit_before>function epicDetailsController ($scope, $http, $q, $location) {
	var notStartedNames = ["To Do", "Open"];
	var doneNames = ["Closed", "Resolved", "Done"];

    $scope.contextPath = jQuery('meta[name="ajs-context-path"]').attr('content');
    $scope.epicKey = $location.search().epic;
    $scope.epicName = '';
    $scope.stories = [];
    $scope.notStarted = 0;
    $scope.inProgress = 0;
    $scope.done = 0;

    $scope.workType = 1;

    $scope.points = [[]];

    //map from constant name to custom field name
    var fieldMap = {
        'Epic Name': null

    };

    $q.all([
        $http.get($scope.contextPath + '/rest/api/2/issue/' + $scope.epicKey + '?expand=names'),
        $http.get($scope.contextPath + '/rest/api/2/search?jql="Epic Link"=' + $scope.epicKey)
    ]).then(function(results) {
        var epic = results[0].data;
        var stories = results[1].data;
        //todo deal with epic with more than 50 stories

        //setup field map
        angular.forEach(fieldMap, function(value, key) {
            angular.forEach(epic.names, function(dataValue, dataKey) {
                if(key === dataValue) {
                    fieldMap[key] = dataKey;
                }
            });
        });

        $scope.epicName = getField(epic.fields, 'Epic Name');

        $scope.stories = stories.issues;
        countStories($scope.stories);
        var points = getProgressList($scope.stories);

        points.sort(function(a, b) {
            return a.date - b.date;
        });

        console.log(points);

        $scope.points = [[]];
        var runningTotal = 0;
        angular.forEach(points, function(elem, index) {
            runningTotal += elem.number;
            $scope.points[0].push([elem.date, runningTotal]);
        });

        console.log($scope.points);

    });

    function getField(data, field) {
        return data[fieldMap[field]];
    }

    // count the number of not started, in progress, and done stories
    function countStories(stories) {
    	angular.forEach(stories, function(story, index) {
    		if (jQuery.inArray(story.fields.status.name, notStartedNames) != -1) {
    			$scope.notStarted++;
    		} else if (jQuery.inArray(story.fields.status.name, doneNames) != -1) {
    			$scope.done++;
    		} else {
    			$scope.inProgress++;
    		}
    	});
    }

    // creates a list of (date, number) pairs
    function getProgressList(stories) {
        var list = [];

        angular.forEach(stories, function(story, index) {
            var value = getValue(story);
            list.push({
                date: Date.parse(story.fields.created),
                number: value
            });

            if(story.fields.resolutiondate !== null) {
                list.push({
                    date: Date.parse(story.fields.resolutiondate),
                    number: -value
                });
            }
        });

        return list;
    }

    function getValue(story) {
        return 1;
    }
}

function chartDirective() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            var chart = null, opts = {};
            scope.$watch(attrs.ngModel, function(v) {
                if(!chart) {
                    chart = jQuery.plot(elem, v, opts);
                    elem.show();
                }
                else {
                    chart.setData(v);
                    chart.setupGrid();
                    chart.draw();
                }
            });
        }
    };
}

<commit_msg>changed progress bar to get story value<commit_after>function epicDetailsController ($scope, $http, $q, $location) {
	var notStartedNames = ["To Do", "Open"];
	var doneNames = ["Closed", "Resolved", "Done"];

    $scope.contextPath = jQuery('meta[name="ajs-context-path"]').attr('content');
    $scope.epicKey = $location.search().epic;
    $scope.epicName = '';
    $scope.stories = [];
    $scope.notStarted = 0;
    $scope.inProgress = 0;
    $scope.done = 0;

    $scope.workType = 1;

    $scope.points = [[]];

    //map from constant name to custom field name
    var fieldMap = {
        'Epic Name': null

    };

    $q.all([
        $http.get($scope.contextPath + '/rest/api/2/issue/' + $scope.epicKey + '?expand=names'),
        $http.get($scope.contextPath + '/rest/api/2/search?jql="Epic Link"=' + $scope.epicKey)
    ]).then(function(results) {
        var epic = results[0].data;
        var stories = results[1].data;
        //todo deal with epic with more than 50 stories

        //setup field map
        angular.forEach(fieldMap, function(value, key) {
            angular.forEach(epic.names, function(dataValue, dataKey) {
                if(key === dataValue) {
                    fieldMap[key] = dataKey;
                }
            });
        });

        $scope.epicName = getField(epic.fields, 'Epic Name');

        $scope.stories = stories.issues;
        countStories($scope.stories);
        var points = getProgressList($scope.stories);

        points.sort(function(a, b) {
            return a.date - b.date;
        });

        console.log(points);

        $scope.points = [[]];
        var runningTotal = 0;
        angular.forEach(points, function(elem, index) {
            runningTotal += elem.number;
            $scope.points[0].push([elem.date, runningTotal]);
        });

        console.log($scope.points);

    });

    function getField(data, field) {
        return data[fieldMap[field]];
    }

    // count the number of not started, in progress, and done stories
    function countStories(stories) {
    	angular.forEach(stories, function(story, index) {
    		if (jQuery.inArray(story.fields.status.name, notStartedNames) != -1) {
    			$scope.notStarted += getValue(story);
    		} else if (jQuery.inArray(story.fields.status.name, doneNames) != -1) {
    			$scope.done += getValue(story);
    		} else {
    			$scope.inProgress += getValue(story);
    		}
    	});
    }

    // creates a list of (date, number) pairs
    function getProgressList(stories) {
        var list = [];

        angular.forEach(stories, function(story, index) {
            var value = getValue(story);
            list.push({
                date: Date.parse(story.fields.created),
                number: value
            });

            if(story.fields.resolutiondate !== null) {
                list.push({
                    date: Date.parse(story.fields.resolutiondate),
                    number: -value
                });
            }
        });

        return list;
    }

    function getValue(story) {
        return 1;
    }
}

function chartDirective() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            var chart = null, opts = {};
            scope.$watch(attrs.ngModel, function(v) {
                if(!chart) {
                    chart = jQuery.plot(elem, v, opts);
                    elem.show();
                }
                else {
                    chart.setData(v);
                    chart.setupGrid();
                    chart.draw();
                }
            });
        }
    };
}

<|endoftext|>


<commit_before>$(function () {
  $.ajax({
    url: '/api/find',
    type: 'GET',
    dataType: 'json',
    success: function (result) {
      var member = JSON.parse(result);
      for (var i = 0; i < member.length; i++) {
        if (i%2 !== 0) {
          $("div#members").append('
'+
                                ''+
                                '
Username:'+member[i].username+'
'+
                                '
'+'
Native Language: '+member[i].nlanguage+'
'+
                                '
Practise Language: '+member[i].planguage+'
'+'Last Login Date'+'
');
        } else {
          $("div#members").append('
'+
                                ''+
                                '
Username: '+member[i].username+'
'+
                                '
'+'
Native Language: '+member[i].nlanguage+'
'+
                                '
Practise Language: '+member[i].planguage+'
'+'Last Login Date'+'
');
        }
      }

    }});
  });
<commit_msg>Update GET<commit_after>$(function () {
  $.ajax({
    url: "/api/find",
    success: function (result) {
      var member = JSON.parse(result);
      for (var i = 0; i < member.length; i++) {
        if (i%2 !== 0) {
          $("div#members").append('
'+
                                ''+
                                '
Username:'+member[i].username+'
'+
                                '
'+'
Native Language: '+member[i].nlanguage+'
'+
                                '
Practise Language: '+member[i].planguage+'
'+'Last Login Date'+'
');
        } else {
          $("div#members").append('
'+
                                ''+
                                '
Username: '+member[i].username+'
'+
                                '
'+'
Native Language: '+member[i].nlanguage+'
'+
                                '
Practise Language: '+member[i].planguage+'
'+'Last Login Date'+'
');
        }
      }

    }});
  });
<|endoftext|>


<commit_before>$(document).ready(function () {
    // In details and add page: covert assignable questions table to the Datatable
    $('#rentals').DataTable();
    $('#rentals_history').DataTable();
    $('#usrs').DataTable();
    $('#devs').DataTable();

    $(document).ready(function () {
        $('#devices').bind('keyup', 'keydown', function (event) {
            var inputLength = event.target.value.length;
            if (event.keyCode !== 8) {
                if ((inputLength + 1) % 6 === 0) {
                    var thisVal = event.target.value;
                    thisVal += '-';
                    $(event.target).val(thisVal);
                }
            }
            $('#charNum').text(event.target.value.split("-").length - 1);
        })
    });

});<commit_msg>bug fix<commit_after>function setCharAt(str, index, chr) {
    if (index > str.length - 1) return str;
    return str.substr(0, index + 1) + chr + str.substr(index + 1);
}

$(document).ready(function () {
    // In details and add page: covert assignable questions table to the Datatable
    $('#rentals').DataTable();
    $('#rentals_history').DataTable();
    $('#usrs').DataTable();
    $('#devs').DataTable();

    var total_length = 0;
    $('#devices').bind('input propertychange', function (event) {
        var inputLength = event.target.value.length;

        if (inputLength > total_length && (inputLength % 5 === 0)) {
            var thisVal = event.target.value;
            var newValue = setCharAt(thisVal, thisVal.length - 2, '-');
            $(event.target).val(newValue);
            total_length = newValue.length;
        }

        total_length = inputLength;

        $('#charNum').text(event.target.value.split("-").length - 1);
    })
});<|endoftext|>


<commit_before>// ########################## RDF Browser Window ##########################
function showBrowser(centralNode, centralNodeURI) {
	$('#browser').show(ANIMATION_SPEED);
	$('#entrypoint').hide(ANIMATION_SPEED);

	prepareBrowser(centralNode, centralNodeURI);
}

function prepareBrowser(centralNode, centralNodeURI) {
	var xhttp = new XMLHttpRequest();

	showLoader(centralNode);
	updateBrowsingHistory(centralNode, centralNodeURI);

	xhttp.onreadystatechange = function () {
		if (xhttp.readyState == 4 && xhttp.status == 200) {
			updateBrowserHeight();
			displayNodes(centralNode, centralNodeURI, JSON.parse(xhttp.responseText));
		}
	}

	xhttp.open('GET', getNeighborhoodRequest(centralNodeURI), true);
	xhttp.send();
}

function displayNodes(centralNode, centralNodeURI, neighbors) {
	// Clear the container.
	$('#browserBody').html('

');

	// Add section to show a node's details.
	$('#container').html('

<button id="btnGoToNode" onclick="" type="button" class="btn btn-primary" aria-label="Go to this node"></button><button id="btnHideNodeDetails" onclick="hideNodeDetails()" type="button" class="btn btn-default" aria-label="Hide details"></button>
...

...

');

	// Determine how to display the graph.
	enableExport(true);

	switch (getBrowsingType()) {
		case 'circular':
			arrangeNodesCircular(centralNode, centralNodeURI, neighbors);
			break;
		case 'direction':
			arrangeNodesByDirection(centralNode, centralNodeURI, neighbors);
			break;
		case 'random':
			arrangeNodesRandomized(centralNode, centralNodeURI, neighbors);
			break;
		case 'textual':
			enableExport(false);
			displayNodesTextual(centralNode, centralNodeURI, neighbors);
			break;
		default:
			console.error('Undefined browsing type.');
			break;
	}
}

function enableExport(enable) {
	$('#btnExportDropdown').prop('disabled', !enable);
}

function showLoader(centralNode) {
	$('#browserBody').html('
Computing the neighbors for ' + centralNode + ' ...
' + LOADER);
}

function updateBrowserHeight() {
	// Update the height of the body div w.r.t. to the outer divs.
	var headerTop = $('#browserHeader').offset().top;
	var headerHeight = $('#browserHeader').outerHeight();
	var bottomSpace = 40;

	// For a fullscreen browser, we only have to respect the header height.
	var heightDiff = $('#browser').hasClass('fullscreen') ?
		headerHeight :
		headerTop + headerHeight + bottomSpace;

	$('#browserBody').css('height', 'calc(100vh - ' + heightDiff + 'px)');
}

function reloadGraph() {
	// Reload the graph with the stored values for the central node.
	var centralNode = $('#container').attr('data-central-node');
	var centralNodeURI = $('#container').attr('data-central-node-uri');

	removeLastHistoryElement();
	prepareBrowser(centralNode, centralNodeURI);
}

function toggleBrowserFullscreen() {
	$('#browser').toggleClass('fullscreen');
	$('#btnFullscreenBrowser > span').toggleClass('glyphicon-resize-full glyphicon-resize-small');
	updateBrowserHeight();
}

function closeBrowser() {
	$('#browser').hide(ANIMATION_SPEED);
	$('#entrypoint').show(ANIMATION_SPEED);
	showReturnToBrowser();
}

function returnToBrowser() {
	$('#browser').show(ANIMATION_SPEED);
	$('#entrypoint').hide(ANIMATION_SPEED);
}

function showNodeDetails(data) {
	if (data.disableGoTo) {
		$('#btnGoToNode').prop('disabled', true);
	} else {
		$('#btnGoToNode').attr('onclick', 'prepareBrowser("' + data.nodeName + '", "' + data.nodeURI + '")');
		$('#btnGoToNode').prop('disabled', false);
	}

	$('#nodeDetailsHeading').html(data.heading);
	$('#nodeDetailsContent').html(data.content);
	$('#nodeDetailsContainer').show(ANIMATION_SPEED);
}

function hideNodeDetails() {
	console.log("Heyo");
	$('#nodeDetailsContainer').hide(ANIMATION_SPEED);
}

$(document).ready(function () {
	$('#btnReloadGraph').click(function () {
		reloadGraph();
	});
	$('#btnExportGraphPNG').click(function () {
		exportGraphAsPNG();
	});
	$('#btnExportGraphSVG').click(function () {
		exportGraphAsSVG();
	});
	$('#btnFullscreenBrowser').click(function () {
		toggleBrowserFullscreen();
	});
	$('#btnCloseBrowser').click(function () {
		closeBrowser();
	});
});<commit_msg>Removes debug log.<commit_after>// ########################## RDF Browser Window ##########################
function showBrowser(centralNode, centralNodeURI) {
	$('#browser').show(ANIMATION_SPEED);
	$('#entrypoint').hide(ANIMATION_SPEED);

	prepareBrowser(centralNode, centralNodeURI);
}

function prepareBrowser(centralNode, centralNodeURI) {
	var xhttp = new XMLHttpRequest();

	showLoader(centralNode);
	updateBrowsingHistory(centralNode, centralNodeURI);

	xhttp.onreadystatechange = function () {
		if (xhttp.readyState == 4 && xhttp.status == 200) {
			updateBrowserHeight();
			displayNodes(centralNode, centralNodeURI, JSON.parse(xhttp.responseText));
		}
	}

	xhttp.open('GET', getNeighborhoodRequest(centralNodeURI), true);
	xhttp.send();
}

function displayNodes(centralNode, centralNodeURI, neighbors) {
	// Clear the container.
	$('#browserBody').html('

');

	// Add section to show a node's details.
	$('#container').html('

<button id="btnGoToNode" onclick="" type="button" class="btn btn-primary" aria-label="Go to this node"></button><button id="btnHideNodeDetails" onclick="hideNodeDetails()" type="button" class="btn btn-default" aria-label="Hide details"></button>
...

...

');

	// Determine how to display the graph.
	enableExport(true);

	switch (getBrowsingType()) {
		case 'circular':
			arrangeNodesCircular(centralNode, centralNodeURI, neighbors);
			break;
		case 'direction':
			arrangeNodesByDirection(centralNode, centralNodeURI, neighbors);
			break;
		case 'random':
			arrangeNodesRandomized(centralNode, centralNodeURI, neighbors);
			break;
		case 'textual':
			enableExport(false);
			displayNodesTextual(centralNode, centralNodeURI, neighbors);
			break;
		default:
			console.error('Undefined browsing type.');
			break;
	}
}

function enableExport(enable) {
	$('#btnExportDropdown').prop('disabled', !enable);
}

function showLoader(centralNode) {
	$('#browserBody').html('
Computing the neighbors for ' + centralNode + ' ...
' + LOADER);
}

function updateBrowserHeight() {
	// Update the height of the body div w.r.t. to the outer divs.
	var headerTop = $('#browserHeader').offset().top;
	var headerHeight = $('#browserHeader').outerHeight();
	var bottomSpace = 40;

	// For a fullscreen browser, we only have to respect the header height.
	var heightDiff = $('#browser').hasClass('fullscreen') ?
		headerHeight :
		headerTop + headerHeight + bottomSpace;

	$('#browserBody').css('height', 'calc(100vh - ' + heightDiff + 'px)');
}

function reloadGraph() {
	// Reload the graph with the stored values for the central node.
	var centralNode = $('#container').attr('data-central-node');
	var centralNodeURI = $('#container').attr('data-central-node-uri');

	removeLastHistoryElement();
	prepareBrowser(centralNode, centralNodeURI);
}

function toggleBrowserFullscreen() {
	$('#browser').toggleClass('fullscreen');
	$('#btnFullscreenBrowser > span').toggleClass('glyphicon-resize-full glyphicon-resize-small');
	updateBrowserHeight();
}

function closeBrowser() {
	$('#browser').hide(ANIMATION_SPEED);
	$('#entrypoint').show(ANIMATION_SPEED);
	showReturnToBrowser();
}

function returnToBrowser() {
	$('#browser').show(ANIMATION_SPEED);
	$('#entrypoint').hide(ANIMATION_SPEED);
}

function showNodeDetails(data) {
	if (data.disableGoTo) {
		$('#btnGoToNode').prop('disabled', true);
	} else {
		$('#btnGoToNode').attr('onclick', 'prepareBrowser("' + data.nodeName + '", "' + data.nodeURI + '")');
		$('#btnGoToNode').prop('disabled', false);
	}

	$('#nodeDetailsHeading').html(data.heading);
	$('#nodeDetailsContent').html(data.content);
	$('#nodeDetailsContainer').show(ANIMATION_SPEED);
}

function hideNodeDetails() {
	$('#nodeDetailsContainer').hide(ANIMATION_SPEED);
}

$(document).ready(function () {
	$('#btnReloadGraph').click(function () {
		reloadGraph();
	});
	$('#btnExportGraphPNG').click(function () {
		exportGraphAsPNG();
	});
	$('#btnExportGraphSVG').click(function () {
		exportGraphAsSVG();
	});
	$('#btnFullscreenBrowser').click(function () {
		toggleBrowserFullscreen();
	});
	$('#btnCloseBrowser').click(function () {
		closeBrowser();
	});
});<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

/**
 * Query salecard from servlet
 */
async function querySalecard() {
  let vendor;
  await fetch('/get-vendor', {method: 'POST'})
      .then((response) => {
        if (response.redirected) {
          window.location.href = response.url;
          return;
        }
        // Response status 200 = Servlet response OK
        if (response.status === 200) {
          return response.json();
        } else {
          alert(response.statusText);
          return;
        }
      })
      .then(vendorEntity => {
        vendor = vendorEntity;
      });

  if (!vendor) {
    window.location.href = '/';
    return;
  }

  if (!('saleCard' in vendor)) {
    await updateLocation();
    document.getElementById('salecard-btns').appendChild(createButton('create'));
    setUploadedImage();
  } else {
    showVendorData(vendor);
    addSaleCardButtons(vendor.saleCard);
  }

  drawMap(getVendorInfo());
}

/**
 * Function that shows vendor information in the form
 * @param {Object} vendor - Vendor object to show information
 */
function showVendorData(vendor) {
  let salecard = vendor.saleCard;

  document.getElementById('business-picture').src
      = `/serve-blob?blobKey=${vendor.saleCard.picture.blobKey.blobKey}`;
  document.getElementById('business-picture').alt
      = `Business picture: ${vendor.saleCard.picture.altText}`;
  document.getElementById('blobKey').value = salecard.picture.blobKey.blobKey;
  document.getElementById('businessName').value = salecard.businessName;
  document.getElementById('description').value = salecard.description;
  document.getElementById('startTime').value = parseTime(salecard.startTime);
  document.getElementById('endTime').value = parseTime(salecard.endTime);
  document.getElementById('hasDelivery').checked = salecard.hasDelivery;
  document.getElementById('hasDelivery').value = salecard.hasDelivery;
  document.getElementById('radius').value = salecard.location.radius;
  document.getElementById('altText').value = salecard.picture.altText;
  document.getElementById('lat').value = salecard.location.salePoint.latitude;
  document.getElementById('lng').value = salecard.location.salePoint.longitude;
  document.getElementById('isTemporarilyClosed').value = salecard.isTemporarilyClosed;
}

/**
 * Function that adds salecard's buttons to the container in the DOM.
 * Adds Open/Close, Update and Delete buttons.
 * @param {Object} salecard - A vendor's salecard
 */
function addSaleCardButtons(salecard) {
  const buttonsContainer = document.getElementById('salecard-btns');

  const openCloseButton = (salecard.isTemporarilyClosed)
      ? createButton('open')
      : createButton('close');
  const updateButton = createButton('update');
  const deleteButton = createButton('delete');

  buttonsContainer.append(openCloseButton, updateButton, deleteButton);
}

/**
 * Function that returns a button according to the type provided
 * @param {string} buttonType - The type of button to create
 */
function createButton(buttonType) {
  const template = document.getElementById('button-template');
  const button = template.content.querySelector('button').cloneNode(true);

  switch (buttonType.toLowerCase()) {
    case 'open':
      button.classList.add('btn-primary');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#open-business-modal');
      button.textContent = 'Open my business now';
      button.addEventListener('click', displayBusinessHours);
      break;
    case 'close':
      button.classList.add('btn-danger');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#close-business-modal');
      button.textContent = 'Close my business now';
      break;
    case 'create':
      button.setAttribute('type', 'submit');
      button.classList.add('btn-success');
      button.textContent = 'Create business';
      break;
    case 'update':
      button.setAttribute('type', 'submit');
      button.classList.add('btn-warning');
      button.textContent = 'Update business information';
      break;
    case 'delete':
      button.classList.add('btn-danger');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#delete-business-modal');
      button.textContent = 'Delete my business';
      break;
    default:
      throw new Error('No button option');
  }

  return button;
}

/**
 * Function that display the business hours in the open biz modal
 */
function displayBusinessHours() {
  const start = document.getElementById('startTime').value;
  const end = document.getElementById('endTime').value;

  const modalBody = document.getElementById('business-hours-modal');
  modalBody.textContent = `From ${start} to ${end}.`;
}

/**
 * Function that updates the salecard to be open or temporarily close
 * @param {boolean} isClosed - Indicates if the business is closed or not
 */
function updateSaleCard(isClosed) {
  document.getElementById('isTemporarilyClosed').value = isClosed;
  const form = document.getElementById('form-card');
  const formData = new FormData(form);

  fetch(form.action, {method: 'POST', body: formData}).then(response => {
    if (!response.redirected) {
      alert(response.statusText);
    }
    location.reload();
  });
}

/**
 * Function that deletes the vendor's salecard
 */
function deleteSaleCard() {
  fetch('/delete-salecard', {method: 'POST'}).then(response => {
    if (!response.redirected) {
      alert(response.statusText);
    }
    location.reload();
  });
}

/**
 * Update Location and refreshMap
 */
async function refreshLocation() {
  await updateLocation();
  drawMap(getVendorInfo());
}

/**
 * Function that returns vendor information object to draw a map.
 */
function getVendorInfo() {
  // Create object to draw the map.
  const vendorInfo = {
    latitude: parseFloat(document.getElementById('lat').value),
    longitude: parseFloat(document.getElementById('lng').value),
    radius: Number(document.getElementById('radius').value),
    markerName: document.getElementById('businessName').value,
    type: 'vendor',
    drawCircleRadius: document.getElementById('hasDelivery').checked
  };

  return vendorInfo;
}

/**
 * When window.load retrieve the card
 */
window.onload = () => {
  fetch('/blobstore-upload-url?formHandler=/update-salecard')
      .then((response) => {
        return response.text();
      }).then((formUrl) => {
    document.getElementById('form-card').action = formUrl;
  });

  initMap();
};
<commit_msg>Fix uploaded images bug<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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.

/**
 * Query salecard from servlet
 */
async function querySalecard() {
  let vendor;
  await fetch('/get-vendor', {method: 'POST'})
      .then((response) => {
        if (response.redirected) {
          window.location.href = response.url;
          return;
        }
        // Response status 200 = Servlet response OK
        if (response.status === 200) {
          return response.json();
        } else {
          alert(response.statusText);
          return;
        }
      })
      .then(vendorEntity => {
        vendor = vendorEntity;
      });

  if (!vendor) {
    window.location.href = '/';
    return;
  }

  setUploadedImage();

  if (!('saleCard' in vendor)) {
    await updateLocation();
    document.getElementById('salecard-btns').appendChild(createButton('create'));
  } else {
    showVendorData(vendor);
    addSaleCardButtons(vendor.saleCard);
  }

  drawMap(getVendorInfo());
}

/**
 * Function that shows vendor information in the form
 * @param {Object} vendor - Vendor object to show information
 */
function showVendorData(vendor) {
  let salecard = vendor.saleCard;

  document.getElementById('business-picture').src
      = `/serve-blob?blobKey=${vendor.saleCard.picture.blobKey.blobKey}`;
  document.getElementById('business-picture').alt
      = `Business picture: ${vendor.saleCard.picture.altText}`;
  document.getElementById('blobKey').value = salecard.picture.blobKey.blobKey;
  document.getElementById('businessName').value = salecard.businessName;
  document.getElementById('description').value = salecard.description;
  document.getElementById('startTime').value = parseTime(salecard.startTime);
  document.getElementById('endTime').value = parseTime(salecard.endTime);
  document.getElementById('hasDelivery').checked = salecard.hasDelivery;
  document.getElementById('hasDelivery').value = salecard.hasDelivery;
  document.getElementById('radius').value = salecard.location.radius;
  document.getElementById('altText').value = salecard.picture.altText;
  document.getElementById('lat').value = salecard.location.salePoint.latitude;
  document.getElementById('lng').value = salecard.location.salePoint.longitude;
  document.getElementById('isTemporarilyClosed').value = salecard.isTemporarilyClosed;
}

/**
 * Function that adds salecard's buttons to the container in the DOM.
 * Adds Open/Close, Update and Delete buttons.
 * @param {Object} salecard - A vendor's salecard
 */
function addSaleCardButtons(salecard) {
  const buttonsContainer = document.getElementById('salecard-btns');

  const openCloseButton = (salecard.isTemporarilyClosed)
      ? createButton('open')
      : createButton('close');
  const updateButton = createButton('update');
  const deleteButton = createButton('delete');

  buttonsContainer.append(openCloseButton, updateButton, deleteButton);
}

/**
 * Function that returns a button according to the type provided
 * @param {string} buttonType - The type of button to create
 */
function createButton(buttonType) {
  const template = document.getElementById('button-template');
  const button = template.content.querySelector('button').cloneNode(true);

  switch (buttonType.toLowerCase()) {
    case 'open':
      button.classList.add('btn-primary');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#open-business-modal');
      button.textContent = 'Open my business now';
      button.addEventListener('click', displayBusinessHours);
      break;
    case 'close':
      button.classList.add('btn-danger');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#close-business-modal');
      button.textContent = 'Close my business now';
      break;
    case 'create':
      button.setAttribute('type', 'submit');
      button.classList.add('btn-success');
      button.textContent = 'Create business';
      break;
    case 'update':
      button.setAttribute('type', 'submit');
      button.classList.add('btn-warning');
      button.textContent = 'Update business information';
      break;
    case 'delete':
      button.classList.add('btn-danger');
      button.setAttribute('data-toggle', 'modal');
      button.setAttribute('data-target', '#delete-business-modal');
      button.textContent = 'Delete my business';
      break;
    default:
      throw new Error('No button option');
  }

  return button;
}

/**
 * Function that display the business hours in the open biz modal
 */
function displayBusinessHours() {
  const start = document.getElementById('startTime').value;
  const end = document.getElementById('endTime').value;

  const modalBody = document.getElementById('business-hours-modal');
  modalBody.textContent = `From ${start} to ${end}.`;
}

/**
 * Function that updates the salecard to be open or temporarily close
 * @param {boolean} isClosed - Indicates if the business is closed or not
 */
function updateSaleCard(isClosed) {
  document.getElementById('isTemporarilyClosed').value = isClosed;
  const form = document.getElementById('form-card');
  const formData = new FormData(form);

  fetch(form.action, {method: 'POST', body: formData}).then(response => {
    if (!response.redirected) {
      alert(response.statusText);
    }
    location.reload();
  });
}

/**
 * Function that deletes the vendor's salecard
 */
function deleteSaleCard() {
  fetch('/delete-salecard', {method: 'POST'}).then(response => {
    if (!response.redirected) {
      alert(response.statusText);
    }
    location.reload();
  });
}

/**
 * Update Location and refreshMap
 */
async function refreshLocation() {
  await updateLocation();
  drawMap(getVendorInfo());
}

/**
 * Function that returns vendor information object to draw a map.
 */
function getVendorInfo() {
  // Create object to draw the map.
  const vendorInfo = {
    latitude: parseFloat(document.getElementById('lat').value),
    longitude: parseFloat(document.getElementById('lng').value),
    radius: Number(document.getElementById('radius').value),
    markerName: document.getElementById('businessName').value,
    type: 'vendor',
    drawCircleRadius: document.getElementById('hasDelivery').checked
  };

  return vendorInfo;
}

/**
 * When window.load retrieve the card
 */
window.onload = () => {
  fetch('/blobstore-upload-url?formHandler=/update-salecard')
      .then((response) => {
        return response.text();
      }).then((formUrl) => {
    document.getElementById('form-card').action = formUrl;
  });

  initMap();
};
<|endoftext|>


<commit_before>// Test function that fetches sample JSON and modifies page to display information for one species
function fetchSpeciesData(name) {
    const URL = '/speciesData?species=' + name;
    fetch(URL).then(response => response.json()).then(speciesData => {
        var commonNameContainer     = document.getElementById('common-name-container');
        var scientificNameContainer = document.getElementById('scientific-name-container');
        var statusContainer         = document.getElementById('status-container');
        var descriptionContainer    = document.getElementById('description-container');
        var citationsContainer      = document.getElementById('citations-container');
        var img                     = document.getElementById('species-image');
        var pixelSlider             = document.getElementById('pixel_factor');
        var kingdomContainer        = document.getElementById('kingdom-container');
        var phylumContainer         = document.getElementById('phylum-container');
        var classContainer          = document.getElementById('class-container');
        var orderContainer          = document.getElementById('order-container');
        var familyContainer         = document.getElementById('family-container');
        var genusContainer          = document.getElementById('genus-container');

        // Update names for species
        commonNameContainer.innerText       = speciesData.commonName;
        scientificNameContainer.innerText   = speciesData.binomialName;

        // Map conservation status code to term and update entry
        var statusCode  = speciesData.status;
        statusCode      = (statusCode == null) ? null : statusCode.substr(0, 2);
        var statusMap   = {
                            "EX" : "Extinct",
                            "EW" : "Extinct in the Wild",
                            "CR" : "Critically Endangered",
                            "EN" : "Endangered",
                            "VU" : "Vulnerable",
                            "NT" : "Near Threatened",
                            "LC" : "Least Concern",
                            "DD" : "Data Deficient",
                            "DO" : "Domesticated",
                            "NE" : "Not Evaluated"
                          };
        statusContainer.innerText = (statusMap[statusCode] === undefined) ? "unknown" : statusCode + ": " + statusMap[statusCode];

        // Update description entry
        var notes = speciesData.wikipediaNotes;
        switch(notes) {
            case null:
                descriptionContainer.innerText = "N/A";
                break;
            case "":
                descriptionContainer.innerText = "N/A";
                break;
            default:
                if (notes.indexOf('[') > 0) {
                    descriptionContainer.innerText = notes.substr(0, notes.indexOf('['));
                }
                else {
                    descriptionContainer.innerText = notes;
                }
        }
        citationsContainer.innerText = speciesData.citationLink;

        // Update image source
        img.src = speciesData.imageLink;

        // Manipulate pixelation value based on species population
        pixelSlider.max = img.width * img.height;
        var pop = speciesData.population;
        pixelSlider.value = (pop == undefined) ? pixelSlider.max : pop;

        // Update species taxonomic path
        if (speciesData.taxonomicPath != null) {
            kingdomContainer.innerText      = speciesData.taxonomicPath.kingdom_t;
            phylumContainer.innerText       = speciesData.taxonomicPath.phylum_t;
            classContainer.innerText        = speciesData.taxonomicPath.class_t;
            orderContainer.innerText        = speciesData.taxonomicPath.order_t;
            familyContainer.innerText       = speciesData.taxonomicPath.family_t;
            genusContainer.innerText        = speciesData.taxonomicPath.genus_t;
        }
    });
}
<commit_msg>Minor fix<commit_after>// Test function that fetches sample JSON and modifies page to display information for one species
function fetchSpeciesData(name) {
    const URL = '/speciesData?species=' + name;
    fetch(URL).then(response => response.json()).then(speciesData => {
        var commonNameContainer     = document.getElementById('common-name-container');
        var scientificNameContainer = document.getElementById('scientific-name-container');
        var statusContainer         = document.getElementById('status-container');
        var descriptionContainer    = document.getElementById('description-container');
        var citationsContainer      = document.getElementById('citations-container');
        var img                     = document.getElementById('species-image');
        var pixelSlider             = document.getElementById('pixel_factor');
        var kingdomContainer        = document.getElementById('kingdom-container');
        var phylumContainer         = document.getElementById('phylum-container');
        var classContainer          = document.getElementById('class-container');
        var orderContainer          = document.getElementById('order-container');
        var familyContainer         = document.getElementById('family-container');
        var genusContainer          = document.getElementById('genus-container');

        // Update names for species
        commonNameContainer.innerText       = speciesData.commonName;
        scientificNameContainer.innerText   = speciesData.binomialName;

        // Map conservation status code to term and update entry
        var statusCode  = speciesData.status;
        statusCode      = (statusCode == null) ? null : statusCode.substr(0, 2);
        var statusMap   = {
                            "EX" : "Extinct",
                            "EW" : "Extinct in the Wild",
                            "CR" : "Critically Endangered",
                            "EN" : "Endangered",
                            "VU" : "Vulnerable",
                            "NT" : "Near Threatened",
                            "LC" : "Least Concern",
                            "DD" : "Data Deficient",
                            "DO" : "Domesticated",
                            "NE" : "Not Evaluated"
                          };
        statusContainer.innerText = (statusMap[statusCode] === undefined) ? "unknown" : statusCode + ": " + statusMap[statusCode];

        // Update description entry
        var notes = speciesData.wikipediaNotes;
        switch(notes) {
            case null:
                descriptionContainer.innerText = "N/A";
                break;
            case "":
                descriptionContainer.innerText = "N/A";
                break;
            default:
                if (notes.indexOf('[') > 0) {
                    descriptionContainer.innerText = notes.substr(0, notes.indexOf('['));
                }
                else {
                    descriptionContainer.innerText = notes;
                }
        }
        citationsContainer.innerText = speciesData.citationLink;

        // Update image source
        img.src = speciesData.imageLink;

        // Manipulate pixelation value based on species population
        pixelSlider.max = img.width * img.height;
        var pop = speciesData.population;
        pixelSlider.value = (pop === undefined) ? pixelSlider.max : pop;

        // Update species taxonomic path
        if (speciesData.taxonomicPath != null) {
            kingdomContainer.innerText      = speciesData.taxonomicPath.kingdom_t;
            phylumContainer.innerText       = speciesData.taxonomicPath.phylum_t;
            classContainer.innerText        = speciesData.taxonomicPath.class_t;
            orderContainer.innerText        = speciesData.taxonomicPath.order_t;
            familyContainer.innerText       = speciesData.taxonomicPath.family_t;
            genusContainer.innerText        = speciesData.taxonomicPath.genus_t;
        }
    });
}
<|endoftext|>