<commit_before>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */

var head = doc.getElementsByTagName("head")[0] || doc.documentElement
var baseElement = head.getElementsByTagName("base")[0]

var IS_CSS_RE = /\.css(?:\?|$)/i
var READY_STATE_RE = /^(?:loaded|complete|undefined)$/

var currentlyAddingScript
var interactiveScript

// `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = +navigator.userAgent
    .replace(/.*AppleWebKit\/(\d+)\..*/, "$1") < 536

function request(url, callback, charset) {
  var isCSS = IS_CSS_RE.test(url)
  var node = doc.createElement(isCSS ? "link" : "script")

  if (charset) {
    var cs = isFunction(charset) ? charset(url) : charset
    if (cs) {
      node.charset = cs
    }
  }

  addOnload(node, callback, isCSS)

  if (isCSS) {
    node.rel = "stylesheet"
    node.href = url
  }
  else {
    node.async = true
    node.src = url
  }

  // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
  // the end of the insert execution, so use `currentlyAddingScript` to
  // hold current node, for deriving url in `define` call
  currentlyAddingScript = node

  // ref: #185 & http://dev.jquery.com/ticket/2709
  baseElement ?
      head.insertBefore(node, baseElement) :
      head.appendChild(node)

  currentlyAddingScript = null
}

function addOnload(node, callback, isCSS) {
  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

  // for Old WebKit and Old Firefox
  if (missingOnload) {
    setTimeout(function() {
      pollCss(node, callback)
    }, 1) // Begin after node insertion
    return
  }

  node.onload = node.onerror = node.onreadystatechange = function() {
    if (READY_STATE_RE.test(node.readyState)) {

      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!isCSS && !data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback()
    }
  }
}

function pollCss(node, callback) {
  var sheet = node.sheet
  var isLoaded

  // for WebKit < 536
  if (isOldWebKit) {
    if (sheet) {
      isLoaded = true
    }
  }
  // for Firefox < 9.0
  else if (sheet) {
    try {
      if (sheet.cssRules) {
        isLoaded = true
      }
    } catch (ex) {
      // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
      // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
      // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
      if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
        isLoaded = true
      }
    }
  }

  setTimeout(function() {
    if (isLoaded) {
      // Place callback here to give time for style rendering
      callback()
    }
    else {
      pollCss(node, callback)
    }
  }, 20)
}

function getCurrentScript() {
  if (currentlyAddingScript) {
    return currentlyAddingScript
  }

  // For IE6-9 browsers, the script onload event may not fire right
  // after the script is evaluated. Kris Zyp found that it
  // could query the script nodes and the one that is in "interactive"
  // mode indicates the current script
  // ref: http://goo.gl/JHfFW
  if (interactiveScript && interactiveScript.readyState === "interactive") {
    return interactiveScript
  }

  var scripts = head.getElementsByTagName("script")

  for (var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i]
    if (script.readyState === "interactive") {
      interactiveScript = script
      return interactiveScript
    }
  }
}

<commit_msg>Enhance onload event for some weird behavior in iframe of IE10+<commit_after>/**
 * util-request.js - The utilities for requesting script and style files
 * ref: tests/research/load-js-css/test.html
 */

var head = doc.getElementsByTagName("head")[0] || doc.documentElement
var baseElement = head.getElementsByTagName("base")[0]

var IS_CSS_RE = /\.css(?:\?|$)/i
var READY_STATE_RE = /^(?:loaded|complete|undefined)$/

var currentlyAddingScript
var interactiveScript

// `onload` event is not supported in WebKit < 535.23 and Firefox < 9.0
// ref:
//  - https://bugs.webkit.org/show_activity.cgi?id=38995
//  - https://bugzilla.mozilla.org/show_bug.cgi?id=185236
//  - https://developer.mozilla.org/en/HTML/Element/link#Stylesheet_load_events
var isOldWebKit = +navigator.userAgent
    .replace(/.*AppleWebKit\/(\d+)\..*/, "$1") < 536

function request(url, callback, charset) {
  var isCSS = IS_CSS_RE.test(url)
  var node = doc.createElement(isCSS ? "link" : "script")

  if (charset) {
    var cs = isFunction(charset) ? charset(url) : charset
    if (cs) {
      node.charset = cs
    }
  }

  addOnload(node, callback, isCSS)

  if (isCSS) {
    node.rel = "stylesheet"
    node.href = url
  }
  else {
    node.async = true
    node.src = url
  }

  // For some cache cases in IE 6-8, the script executes IMMEDIATELY after
  // the end of the insert execution, so use `currentlyAddingScript` to
  // hold current node, for deriving url in `define` call
  currentlyAddingScript = node

  // ref: #185 & http://dev.jquery.com/ticket/2709
  baseElement ?
      head.insertBefore(node, baseElement) :
      head.appendChild(node)

  currentlyAddingScript = null
}

function addOnload(node, callback, isCSS) {
  var missingOnload = isCSS && (isOldWebKit || !("onload" in node))

  // for Old WebKit and Old Firefox
  if (missingOnload) {
    setTimeout(function() {
      pollCss(node, callback)
    }, 1) // Begin after node insertion
    return
  }

  ("onload" in node) ?
      node.onload = node.onerror = onload :
      node.onreadystatechange = onload

  function onload() {
    if (READY_STATE_RE.test(node.readyState)) {

      // Ensure only run once and handle memory leak in IE
      node.onload = node.onerror = node.onreadystatechange = null

      // Remove the script to reduce memory leak
      if (!isCSS && !data.debug) {
        head.removeChild(node)
      }

      // Dereference the node
      node = null

      callback()
    }
  }
}

function pollCss(node, callback) {
  var sheet = node.sheet
  var isLoaded

  // for WebKit < 536
  if (isOldWebKit) {
    if (sheet) {
      isLoaded = true
    }
  }
  // for Firefox < 9.0
  else if (sheet) {
    try {
      if (sheet.cssRules) {
        isLoaded = true
      }
    } catch (ex) {
      // The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
      // to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
      // in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
      if (ex.name === "NS_ERROR_DOM_SECURITY_ERR") {
        isLoaded = true
      }
    }
  }

  setTimeout(function() {
    if (isLoaded) {
      // Place callback here to give time for style rendering
      callback()
    }
    else {
      pollCss(node, callback)
    }
  }, 20)
}

function getCurrentScript() {
  if (currentlyAddingScript) {
    return currentlyAddingScript
  }

  // For IE6-9 browsers, the script onload event may not fire right
  // after the script is evaluated. Kris Zyp found that it
  // could query the script nodes and the one that is in "interactive"
  // mode indicates the current script
  // ref: http://goo.gl/JHfFW
  if (interactiveScript && interactiveScript.readyState === "interactive") {
    return interactiveScript
  }

  var scripts = head.getElementsByTagName("script")

  for (var i = scripts.length - 1; i >= 0; i--) {
    var script = scripts[i]
    if (script.readyState === "interactive") {
      interactiveScript = script
      return interactiveScript
    }
  }
}

<|endoftext|>


<commit_before>/* eslint no-constant-condition: "off" */

const d3 = require('d3')
const Tabletop = require('tabletop')
const _ = {
  map: require('lodash/map'),
  uniqBy: require('lodash/uniqBy'),
  capitalize: require('lodash/capitalize'),
  each: require('lodash/each')
}

const InputSanitizer = require('./inputSanitizer')
const Radar = require('../models/radar')
const Quadrant = require('../models/quadrant')
const Ring = require('../models/ring')
const Blip = require('../models/blip')
const GraphingRadar = require('../graphing/radar')
const QueryParams = require('./queryParamProcessor')
const MalformedDataError = require('../exceptions/malformedDataError')
const SheetNotFoundError = require('../exceptions/sheetNotFoundError')
const ContentValidator = require('./contentValidator')
const Sheet = require('./sheet')
const ExceptionMessages = require('./exceptionMessages')
const GoogleAuth = require('./googleAuth')

const plotRadar = function (title, blips, currentRadarName, alternativeRadars) {
  document.title = title
  d3.selectAll('.loading').remove()

  var rings = _.map(_.uniqBy(blips, 'ring'), 'ring')
  var ringMap = {}
  var maxRings = 4

  _.each(rings, function (ringName, i) {
    if (i === maxRings) {
      throw new MalformedDataError(ExceptionMessages.TOO_MANY_RINGS)
    }
    ringMap[ringName] = new Ring(ringName, i)
  })

  var quadrants = {}
  _.each(blips, function (blip) {
    if (!quadrants[blip.quadrant]) {
      quadrants[blip.quadrant] = new Quadrant(_.capitalize(blip.quadrant))
    }
    quadrants[blip.quadrant].add(new Blip(blip.name, ringMap[blip.ring], blip.isNew.toLowerCase() === 'true', blip.topic, blip.description))
  })

  var radar = new Radar()
  _.each(quadrants, function (quadrant) {
    radar.addQuadrant(quadrant)
  })

  if (alternativeRadars !== undefined || true) {
    alternativeRadars.forEach(function (sheetName) {
      radar.addAlternative(sheetName)
    })
  }

  if (currentRadarName !== undefined || true) {
    radar.setCurrentSheet(currentRadarName)
  }

  var size = (window.innerHeight - 133) < 620 ? 620 : window.innerHeight - 133

  new GraphingRadar(size, radar).init().plot()
}

const GoogleSheet = function (sheetReference, sheetName) {
  var self = {}

  self.build = function () {
    var sheet = new Sheet(sheetReference)
    sheet.validate(function (error) {
      if (!error) {
        Tabletop.init({
          key: sheet.id,
          callback: createBlips
        })
        return
      }

      if (error instanceof SheetNotFoundError) {
        plotErrorMessage(error)
        return
      }
      self.authenticate(false)
    })

    function createBlips (__, tabletop) {
      try {
        if (!sheetName) {
          sheetName = tabletop.foundSheetNames[0]
        }
        var columnNames = tabletop.sheets(sheetName).columnNames

        var contentValidator = new ContentValidator(columnNames)
        contentValidator.verifyContent()
        contentValidator.verifyHeaders()

        var all = tabletop.sheets(sheetName).all()
        var blips = _.map(all, new InputSanitizer().sanitize)

        plotRadar(tabletop.googleSheetName + ' - ' + sheetName, blips, sheetName, tabletop.foundSheetNames)
      } catch (exception) {
        plotErrorMessage(exception)
      }
    }
  }

  function createBlipsForProtectedSheet (documentTitle, values, sheetNames) {
    if (!sheetName) {
      sheetName = sheetNames[0]
    }
    values.forEach(function (value) {
      var contentValidator = new ContentValidator(values[0])
      contentValidator.verifyContent()
      contentValidator.verifyHeaders()
    })

    const all = values
    const header = all.shift()
    var blips = _.map(all, blip => new InputSanitizer().sanitizeForProtectedSheet(blip, header))
    plotRadar(documentTitle + ' - ' + sheetName, blips, sheetName, sheetNames)
  }

  self.authenticate = function (force = false, callback) {
    GoogleAuth.loadGoogle(function (e) {
      GoogleAuth.login(_ => {
        var sheet = new Sheet(sheetReference)
        sheet.processSheetResponse(sheetName, createBlipsForProtectedSheet, error => {
          if (error.status === 403) {
            plotUnauthorizedErrorMessage()
          } else {
            plotErrorMessage(error)
          }
        })
        if (callback) { callback() }
      }, force)
    })
  }

  self.init = function () {
    plotLoading()
    return self
  }

  return self
}

const CSVDocument = function (url) {
  var self = {}

  self.build = function () {
    d3.csv(url, createBlips)
  }

  var createBlips = function (data) {
    try {
      var columnNames = data['columns']
      delete data['columns']
      var contentValidator = new ContentValidator(columnNames)
      contentValidator.verifyContent()
      contentValidator.verifyHeaders()
      var blips = _.map(data, new InputSanitizer().sanitize)
      plotRadar(FileName(url), blips, 'CSV File', [])
    } catch (exception) {
      plotErrorMessage(exception)
    }
  }

  self.init = function () {
    plotLoading()
    return self
  }

  return self
}

const DomainName = function (url) {
  var search = /.+:\/\/([^\\/]+)/
  var match = search.exec(decodeURIComponent(url.replace(/\+/g, ' ')))
  return match == null ? null : match[1]
}

const FileName = function (url) {
  var search = /([^\\/]+)$/
  var match = search.exec(decodeURIComponent(url.replace(/\+/g, ' ')))
  if (match != null) {
    var str = match[1]
    return str
  }
  return url
}

const GoogleSheetInput = function () {
  var self = {}
  var sheet

  self.build = function () {
    var domainName = DomainName(window.location.search.substring(1))
    var queryString = window.location.href.match(/sheetId(.*)/)
    var queryParams = queryString ? QueryParams(queryString[0]) : {}

    if (domainName && queryParams.sheetId.endsWith('csv')) {
      sheet = CSVDocument(queryParams.sheetId)
      sheet.init().build()
    } else if (domainName && domainName.endsWith('google.com') && queryParams.sheetId) {
      sheet = GoogleSheet(queryParams.sheetId, queryParams.sheetName)
      console.log(queryParams.sheetName)

      sheet.init().build()
    } else {
      var content = d3.select('body')
        .append('div')
        .attr('class', 'input-sheet')
      setDocumentTitle()

      plotLogo(content)

      var bannerText = '

Build your own radar

Once you\'ve created your Radar, you can use this service' +
        ' to generate an
interactive version of your Technology Radar. Not sure how? Read this first.

'

      plotBanner(content, bannerText)

      plotForm(content)

      plotFooter(content)
    }
  }

  return self
}

function setDocumentTitle () {
  document.title = 'Build your own Radar'
}

function plotLoading (content) {
  content = d3.select('body')
    .append('div')
    .attr('class', 'loading')
    .append('div')
    .attr('class', 'input-sheet')

  setDocumentTitle()

  plotLogo(content)

  var bannerText = '
Building your radar...

Your Technology Radar will be available in just a few seconds
'
  plotBanner(content, bannerText)
  plotFooter(content)
}

function plotLogo (content) {
  content.append('div')
    .attr('class', 'input-sheet__logo')
    .html('')
}

function plotFooter (content) {
  content
    .append('div')
    .attr('id', 'footer')
    .append('div')
    .attr('class', 'footer-content')
    .append('p')
    .html('Powered by  ThoughtWorks. ' +
      'By using this service you agree to ThoughtWorks\' terms of use. ' +
      'You also agree to our privacy policy, which describes how we will gather, use and protect any personal data contained in your public Google Sheet. ' +
      'This software is open source and available for download and self-hosting.')
}

function plotBanner (content, text) {
  content.append('div')
    .attr('class', 'input-sheet__banner')
    .html(text)
}

function plotForm (content) {
  content.append('div')
    .attr('class', 'input-sheet__form')
    .append('p')
    .html('Enter the URL of your Google Sheet or CSV file below…')

  var form = content.select('.input-sheet__form').append('form')
    .attr('method', 'get')

  form.append('input')
    .attr('type', 'text')
    .attr('name', 'sheetId')
    .attr('placeholder', 'e.g. https://docs.google.com/spreadsheets/d/<sheetid> or hosted CSV file')
    .attr('required', '')

  form.append('button')
    .attr('type', 'submit')
    .append('a')
    .attr('class', 'button')
    .text('Build my radar')

  form.append('p').html("Need help?")
}

function plotErrorMessage (exception) {
  var message = 'Oops! It seems like there are some problems with loading your data. '

  var content = d3.select('body')
    .append('div')
    .attr('class', 'input-sheet')
  setDocumentTitle()

  plotLogo(content)

  var bannerText = '

Build your own radar

Once you\'ve created your Radar, you can use this service' +
    ' to generate an
interactive version of your Technology Radar. Not sure how? Read this first.

'

  plotBanner(content, bannerText)

  d3.selectAll('.loading').remove()
  message = "Oops! We can't find the Google Sheet you've entered"
  var faqMessage = 'Please check FAQs for possible solutions.'
  if (exception instanceof MalformedDataError) {
    message = message.concat(exception.message)
  } else if (exception instanceof SheetNotFoundError) {
    message = exception.message
  } else {
    console.error(exception)
  }

  const container = content.append('div').attr('class', 'error-container')
  var errorContainer = container.append('div')
    .attr('class', 'error-container__message')
  errorContainer.append('div').append('p')
    .html(message)
  errorContainer.append('div').append('p')
    .html(faqMessage)

  var homePageURL = window.location.protocol + '//' + window.location.hostname
  homePageURL += (window.location.port === '' ? '' : ':' + window.location.port)
  var homePage = 'GO BACK'

  errorContainer.append('div').append('p')
    .html(homePage)

  plotFooter(content)
}

function plotUnauthorizedErrorMessage () {
  var content = d3.select('body')
    .append('div')
    .attr('class', 'input-sheet')
  setDocumentTitle()

  plotLogo(content)

  var bannerText = '

Build your own radar

'

  plotBanner(content, bannerText)

  d3.selectAll('.loading').remove()
  const currentUser = GoogleAuth.geEmail()
  let homePageURL = window.location.protocol + '//' + window.location.hostname
  homePageURL += (window.location.port === '' ? '' : ':' + window.location.port)
  const goBack = 'GO BACK'
  const message = `<strong>Oops!</strong> Looks like you are accessing this sheet using <b>${currentUser}</b>, which does not have permission.Try switching to another account.`

  const container = content.append('div').attr('class', 'error-container')

  const errorContainer = container.append('div')
    .attr('class', 'error-container__message')

  errorContainer.append('div').append('p')
    .attr('class', 'error-title')
    .html(message)

  const button = errorContainer.append('button')
    .attr('class', 'button switch-account-button')
    .text('SWITCH ACCOUNT')

  errorContainer.append('div').append('p')
    .attr('class', 'error-subtitle')
    .html(`or ${goBack} to try a different sheet.`)

  button.on('click', _ => {
    var queryString = window.location.href.match(/sheetId(.*)/)
    var queryParams = queryString ? QueryParams(queryString[0]) : {}
    const sheet = GoogleSheet(queryParams.sheetId, queryParams.sheetName)
    sheet.authenticate(true, _ => {
      content.remove()
    })
  })
}

module.exports = GoogleSheetInput
<commit_msg>Revert "Revert "[Arun/Vivek] reverted google auth feature""<commit_after>/* eslint no-constant-condition: "off" */

const d3 = require('d3')
const Tabletop = require('tabletop')
const _ = {
  map: require('lodash/map'),
  uniqBy: require('lodash/uniqBy'),
  capitalize: require('lodash/capitalize'),
  each: require('lodash/each')
}

const InputSanitizer = require('./inputSanitizer')
const Radar = require('../models/radar')
const Quadrant = require('../models/quadrant')
const Ring = require('../models/ring')
const Blip = require('../models/blip')
const GraphingRadar = require('../graphing/radar')
const QueryParams = require('./queryParamProcessor')
const MalformedDataError = require('../exceptions/malformedDataError')
const SheetNotFoundError = require('../exceptions/sheetNotFoundError')
const ContentValidator = require('./contentValidator')
const Sheet = require('./sheet')
const ExceptionMessages = require('./exceptionMessages')
const GoogleAuth = require('./googleAuth')

const plotRadar = function (title, blips, currentRadarName, alternativeRadars) {
  document.title = title
  d3.selectAll('.loading').remove()

  var rings = _.map(_.uniqBy(blips, 'ring'), 'ring')
  var ringMap = {}
  var maxRings = 4

  _.each(rings, function (ringName, i) {
    if (i === maxRings) {
      throw new MalformedDataError(ExceptionMessages.TOO_MANY_RINGS)
    }
    ringMap[ringName] = new Ring(ringName, i)
  })

  var quadrants = {}
  _.each(blips, function (blip) {
    if (!quadrants[blip.quadrant]) {
      quadrants[blip.quadrant] = new Quadrant(_.capitalize(blip.quadrant))
    }
    quadrants[blip.quadrant].add(new Blip(blip.name, ringMap[blip.ring], blip.isNew.toLowerCase() === 'true', blip.topic, blip.description))
  })

  var radar = new Radar()
  _.each(quadrants, function (quadrant) {
    radar.addQuadrant(quadrant)
  })

  if (alternativeRadars !== undefined || true) {
    alternativeRadars.forEach(function (sheetName) {
      radar.addAlternative(sheetName)
    })
  }

  if (currentRadarName !== undefined || true) {
    radar.setCurrentSheet(currentRadarName)
  }

  var size = (window.innerHeight - 133) < 620 ? 620 : window.innerHeight - 133

  new GraphingRadar(size, radar).init().plot()
}

const GoogleSheet = function (sheetReference, sheetName) {
  var self = {}

  self.build = function () {
    var sheet = new Sheet(sheetReference)
    sheet.validate(function (notFound) {
      if (notFound) {
        plotErrorMessage(notFound)
        return
      }

      Tabletop.init({
        key: sheet.id,
        callback: createBlips
      })
    })

    function createBlips (__, tabletop) {
      try {
        if (!sheetName) {
          sheetName = tabletop.foundSheetNames[0]
        }
        var columnNames = tabletop.sheets(sheetName).columnNames

        var contentValidator = new ContentValidator(columnNames)
        contentValidator.verifyContent()
        contentValidator.verifyHeaders()

        var all = tabletop.sheets(sheetName).all()
        var blips = _.map(all, new InputSanitizer().sanitize)

        plotRadar(tabletop.googleSheetName + ' - ' + sheetName, blips, sheetName, tabletop.foundSheetNames)
      } catch (exception) {
        plotErrorMessage(exception)
      }
    }
  }

  function createBlipsForProtectedSheet (documentTitle, values, sheetNames) {
    if (!sheetName) {
      sheetName = sheetNames[0]
    }
    values.forEach(function (value) {
      var contentValidator = new ContentValidator(values[0])
      contentValidator.verifyContent()
      contentValidator.verifyHeaders()
    })

    const all = values
    const header = all.shift()
    var blips = _.map(all, blip => new InputSanitizer().sanitizeForProtectedSheet(blip, header))
    plotRadar(documentTitle + ' - ' + sheetName, blips, sheetName, sheetNames)
  }

  self.authenticate = function (force = false, callback) {
    GoogleAuth.loadGoogle(function (e) {
      GoogleAuth.login(_ => {
        var sheet = new Sheet(sheetReference)
        sheet.processSheetResponse(sheetName, createBlipsForProtectedSheet, error => {
          if (error.status === 403) {
            plotUnauthorizedErrorMessage()
          } else {
            plotErrorMessage(error)
          }
        })
        if (callback) { callback() }
      }, force)
    })
  }

  self.init = function () {
    plotLoading()
    return self
  }

  return self
}

const CSVDocument = function (url) {
  var self = {}

  self.build = function () {
    d3.csv(url, createBlips)
  }

  var createBlips = function (data) {
    try {
      var columnNames = data['columns']
      delete data['columns']
      var contentValidator = new ContentValidator(columnNames)
      contentValidator.verifyContent()
      contentValidator.verifyHeaders()
      var blips = _.map(data, new InputSanitizer().sanitize)
      plotRadar(FileName(url), blips, 'CSV File', [])
    } catch (exception) {
      plotErrorMessage(exception)
    }
  }

  self.init = function () {
    plotLoading()
    return self
  }

  return self
}

const DomainName = function (url) {
  var search = /.+:\/\/([^\\/]+)/
  var match = search.exec(decodeURIComponent(url.replace(/\+/g, ' ')))
  return match == null ? null : match[1]
}

const FileName = function (url) {
  var search = /([^\\/]+)$/
  var match = search.exec(decodeURIComponent(url.replace(/\+/g, ' ')))
  if (match != null) {
    var str = match[1]
    return str
  }
  return url
}

const GoogleSheetInput = function () {
  var self = {}
  var sheet

  self.build = function () {
    var domainName = DomainName(window.location.search.substring(1))
    var queryString = window.location.href.match(/sheetId(.*)/)
    var queryParams = queryString ? QueryParams(queryString[0]) : {}

    if (domainName && queryParams.sheetId.endsWith('csv')) {
      sheet = CSVDocument(queryParams.sheetId)
      sheet.init().build()
    } else if (domainName && domainName.endsWith('google.com') && queryParams.sheetId) {
      sheet = GoogleSheet(queryParams.sheetId, queryParams.sheetName)
      console.log(queryParams.sheetName)

      sheet.init().build()
    } else {
      var content = d3.select('body')
        .append('div')
        .attr('class', 'input-sheet')
      setDocumentTitle()

      plotLogo(content)

      var bannerText = '

Build your own radar

Once you\'ve created your Radar, you can use this service' +
        ' to generate an
interactive version of your Technology Radar. Not sure how? Read this first.

'

      plotBanner(content, bannerText)

      plotForm(content)

      plotFooter(content)
    }
  }

  return self
}

function setDocumentTitle () {
  document.title = 'Build your own Radar'
}

function plotLoading (content) {
  content = d3.select('body')
    .append('div')
    .attr('class', 'loading')
    .append('div')
    .attr('class', 'input-sheet')

  setDocumentTitle()

  plotLogo(content)

  var bannerText = '
Building your radar...

Your Technology Radar will be available in just a few seconds
'
  plotBanner(content, bannerText)
  plotFooter(content)
}

function plotLogo (content) {
  content.append('div')
    .attr('class', 'input-sheet__logo')
    .html('')
}

function plotFooter (content) {
  content
    .append('div')
    .attr('id', 'footer')
    .append('div')
    .attr('class', 'footer-content')
    .append('p')
    .html('Powered by  ThoughtWorks. ' +
      'By using this service you agree to ThoughtWorks\' terms of use. ' +
      'You also agree to our privacy policy, which describes how we will gather, use and protect any personal data contained in your public Google Sheet. ' +
      'This software is open source and available for download and self-hosting.')
}

function plotBanner (content, text) {
  content.append('div')
    .attr('class', 'input-sheet__banner')
    .html(text)
}

function plotForm (content) {
  content.append('div')
    .attr('class', 'input-sheet__form')
    .append('p')
    .html('Enter the URL of your Google Sheet or CSV file below…')

  var form = content.select('.input-sheet__form').append('form')
    .attr('method', 'get')

  form.append('input')
    .attr('type', 'text')
    .attr('name', 'sheetId')
    .attr('placeholder', 'e.g. https://docs.google.com/spreadsheets/d/<sheetid> or hosted CSV file')
    .attr('required', '')

  form.append('button')
    .attr('type', 'submit')
    .append('a')
    .attr('class', 'button')
    .text('Build my radar')

  form.append('p').html("Need help?")
}

function plotErrorMessage (exception) {
  var message = 'Oops! It seems like there are some problems with loading your data. '

  var content = d3.select('body')
    .append('div')
    .attr('class', 'input-sheet')
  setDocumentTitle()

  plotLogo(content)

  var bannerText = '

Build your own radar

Once you\'ve created your Radar, you can use this service' +
    ' to generate an
interactive version of your Technology Radar. Not sure how? Read this first.

'

  plotBanner(content, bannerText)

  d3.selectAll('.loading').remove()
  message = "Oops! We can't find the Google Sheet you've entered"
  var faqMessage = 'Please check FAQs for possible solutions.'
  if (exception instanceof MalformedDataError) {
    message = message.concat(exception.message)
  } else if (exception instanceof SheetNotFoundError) {
    message = exception.message
  } else {
    console.error(exception)
  }

  const container = content.append('div').attr('class', 'error-container')
  var errorContainer = container.append('div')
    .attr('class', 'error-container__message')
  errorContainer.append('div').append('p')
    .html(message)
  errorContainer.append('div').append('p')
    .html(faqMessage)

  var homePageURL = window.location.protocol + '//' + window.location.hostname
  homePageURL += (window.location.port === '' ? '' : ':' + window.location.port)
  var homePage = 'GO BACK'

  errorContainer.append('div').append('p')
    .html(homePage)

  plotFooter(content)
}

function plotUnauthorizedErrorMessage () {
  var content = d3.select('body')
    .append('div')
    .attr('class', 'input-sheet')
  setDocumentTitle()

  plotLogo(content)

  var bannerText = '

Build your own radar

'

  plotBanner(content, bannerText)

  d3.selectAll('.loading').remove()
  const currentUser = GoogleAuth.geEmail()
  let homePageURL = window.location.protocol + '//' + window.location.hostname
  homePageURL += (window.location.port === '' ? '' : ':' + window.location.port)
  const goBack = 'GO BACK'
  const message = `<strong>Oops!</strong> Looks like you are accessing this sheet using <b>${currentUser}</b>, which does not have permission.Try switching to another account.`

  const container = content.append('div').attr('class', 'error-container')

  const errorContainer = container.append('div')
    .attr('class', 'error-container__message')

  errorContainer.append('div').append('p')
    .attr('class', 'error-title')
    .html(message)

  const button = errorContainer.append('button')
    .attr('class', 'button switch-account-button')
    .text('SWITCH ACCOUNT')

  errorContainer.append('div').append('p')
    .attr('class', 'error-subtitle')
    .html(`or ${goBack} to try a different sheet.`)

  button.on('click', _ => {
    var queryString = window.location.href.match(/sheetId(.*)/)
    var queryParams = queryString ? QueryParams(queryString[0]) : {}
    const sheet = GoogleSheet(queryParams.sheetId, queryParams.sheetName)
    sheet.authenticate(true, _ => {
      content.remove()
    })
  })
}

module.exports = GoogleSheetInput
<|endoftext|>


<commit_before>var Twitter = require('twitter');

var client = new Twitter({
    consumer_key: process.env.CONSUMER_KEY,
    consumer_secret: process.env.CONSUMER_SECRET,
    access_token_key: process.env.ACCESS_TOKEN_KEY,
    access_token_secret: process.env.ACCESS_TOKEN_SECRET
});

var tagsToTrack = ['cat', 'dog'];
var params = { track: tagsToTrack.join() };

client.stream('statuses/filter', params, function(stream){
    stream.on('data', function(tweet){
        var hashtags = tweet.entities.hashtags;
        if(hashtags.length > 0) {
            parseTags(hashtags);
        }
    });

    stream.on('error', console.error);
    stream.on('end', function(){ console.log('----------Closing stream-----------'); });
});

function parseTags(hashtags){
   hashtags.forEach(function(hashtag){
        tagsToTrack.forEach(function(tag){
            if(hashtag.text.toLowerCase().indexOf(tag) > -1)
                console.log(tag + ' +1', hashtag.text);
        });
    });
};
<commit_msg>No update<commit_after>var Twitter = require('twitter');

var client = new Twitter({
    consumer_key: process.env.CONSUMER_KEY,
    consumer_secret: process.env.CONSUMER_SECRET,
    access_token_key: process.env.ACCESS_TOKEN_KEY,
    access_token_secret: process.env.ACCESS_TOKEN_SECRET
});

var tagsToTrack = ['cat', 'dog'];
var params = { track: tagsToTrack.join() };

client.stream('statuses/filter', params, function(stream){
    stream.on('data', function(tweet){
        var hashtags = tweet.entities.hashtags;
        if(hashtags.length > 0) {
            parseTags(hashtags);
        }
    });

    stream.on('error', console.error);
    stream.on('end', function(){ console.log('----------Closing stream-----------'); });
});

function parseTags(hashtags){
   hashtags.forEach(function(hashtag){
        tagsToTrack.forEach(function(tag){
            if(hashtag.text.toLowerCase().indexOf(tag) > -1)
                console.log(tag + ' +1', hashtag.text);
        });
    });
};
<|endoftext|>


<commit_before>var _ = require('lodash'),
    async = require('async'),
    checkForFiling = require('./check'),
    request = require('request'),
    models = require('../../models'),
    parser = require('rss-parser');

var interval = 60000;

function queueFilingsToCheck() {
    console.log('checking RSS');

    parser.parseURL('http://efilingapps.fec.gov/rss/generate?preDefinedFilingType=ALL', function(err, parsed) {
        if (!err && parsed && parsed.feed && parsed.feed.entries) {
            var newFilings = parsed.feed.entries.map(function (filing) {
                return parseInt(filing.link.replace('http://docquery.fec.gov/dcdev/posted/','').replace('.fec',''));
            });

            models.fec_filing.findAll({
                    attributes: ['filing_id'],
                    where: {
                        filing_id: {
                            gte: _.min(newFilings)
                        }
                    }
                })
                .then(function(filings) {
                    filings = filings.map(function(filing) {
                        return filing.filing_id;
                    });

                    async.mapSeries(_.difference(newFilings,filings), checkForFiling, function () {
                        console.log('waiting');
                        setTimeout(queueFilingsToCheck,interval);
                    });

                });

        }
        else {
            console.error(error);

            console.log('waiting');
            setTimeout(queueFilingsToCheck,interval);
        }
    });
}

queueFilingsToCheck();
<commit_msg>Remove Node RSS restart interval in favor of kubernetes cron job<commit_after>var _ = require('lodash'),
    async = require('async'),
    checkForFiling = require('./check'),
    request = require('request'),
    models = require('../../models'),
    parser = require('rss-parser');

// var interval = 60000;

function queueFilingsToCheck() {
    console.log('checking RSS');

    parser.parseURL('http://efilingapps.fec.gov/rss/generate?preDefinedFilingType=ALL', function(err, parsed) {
        if (!err && parsed && parsed.feed && parsed.feed.entries) {
            var newFilings = parsed.feed.entries.map(function (filing) {
                return parseInt(filing.link.replace('http://docquery.fec.gov/dcdev/posted/','').replace('.fec',''));
            });

            models.fec_filing.findAll({
                    attributes: ['filing_id'],
                    where: {
                        filing_id: {
                            gte: _.min(newFilings)
                        }
                    }
                })
                .then(function(filings) {
                    filings = filings.map(function(filing) {
                        return filing.filing_id;
                    });

                    async.mapSeries(_.difference(newFilings,filings), checkForFiling, function () {
                        console.log('done');
                        // setTimeout(queueFilingsToCheck,interval);
                    });

                });

        }
        else {
            console.error(error);

            console.log('done');
            // setTimeout(queueFilingsToCheck,interval);
        }
    });
}

queueFilingsToCheck();
<|endoftext|>


<commit_before>/*
 * @fileOverview Stealth support.
 */

define(['darkwallet','bitcoinjs-lib'], function(DarkWallet, Bitcoin) {

var convert = Bitcoin.convert;

var Stealth = {};

// Elliptic curve N (couldn't find a way to get it from bitcoin api, so just hardcoded it here...)
Stealth.ecN = new Bitcoin.BigInteger("115792089237316195423570985008687907852837564279074904382605163141518161494337");

// Version for address format
Stealth.version = 255;

// Can these be different in the future?
Stealth.nonceVersion = Stealth.version;

/*
 * Create a bitcoin key with just public component.
 * @param {Object} Q public key as bytes
 * @private
 */
Stealth.importPublic = function(Q) {
    var key = new Bitcoin.ECPubKey(Q, true);
    return key;
}

/*
 * Perform curvedh and stealth formatting
 * @param {Bitcoin.BigInteger} e private part
 * @param {Bitcoin.Key} decKey public key
 * @private
 */
Stealth.stealthDH = function(e, decKey) {
    // diffie hellman stage
    var point = decKey.pub.multiply(e);

    // start the second stage
    var S1 = [3].concat(point.getX().toBigInteger().toByteArrayUnsigned());
    var c = convert.wordArrayToBytes(Bitcoin.Crypto.SHA256(convert.bytesToWordArray(S1)));
    return c;
}

/*
 * Format a stealth address in base58
 * @param {Object} scanPubKeyBytes Public key as byte array
 * @param {Object} spendPubKeys Spend public keys as array of byte arrays
 *
 * [version:1] [options:1] [scan_pubkey:33] [N:1] [spend_pubkey_1:33] ...
[spend_pubkey_N:33] [number_sigs:1] [prefix_length:1] [prefix:prefix_length/8, round up]
 * version = 255
 * options bitfield = 0 or 1 (reuse scan_pubkey for spends)
 */
Stealth.formatAddress = function(scanPubKeyBytes, spendPubKeys) {
    if (!spendPubKeys) {
        spendPubKeys = [];
    }
    var reuseScan = spendPubKeys.length ? 0 : 1;

    // Header, version will be added later when encoding
    var stealth = [reuseScan];

    // Add scan public key
    stealth = stealth.concat(scanPubKeyBytes);

    // Add spend public keys
    stealth = stealth.concat([spendPubKeys.length]);
    spendPubKeys.forEach(function(spendPubKey) {
        stealth = stealth.concat(spendPubKey);
    });

    // Number of signatures
    var nSigs = spendPubKeys.length || 1;
    stealth = stealth.concat([nSigs]);

    // TODO: Add prefix
    stealth = stealth.concat([0]);
    // Encode in base58 and add version
    return Bitcoin.base58.checkEncode(stealth, Stealth.version);
}

/*
 * Parse a stealth address into its forming parts
 * @param {String} recipient Address in base58 format
 */
Stealth.parseAddress = function(recipient) {
    // TODO perform consistency checks here
    var stealthBytes = Bitcoin.base58.checkDecode(recipient);
    var options = stealthBytes.splice(0, 1)[0];
    var scanKeyBytes = stealthBytes.splice(0, 33);
    var nSpendKeys = stealthBytes.splice(0, 1)[0];
    var spendKeys = [];
    for(var idx=0; idx<nSpendKeys; idx++) {
        spendKeys.push(stealthBytes.splice(0, 33));
    }
    var nSigs = stealthBytes.splice(0, 1)[0];

    // Prefix should be the remaining bytes
    var prefix = stealthBytes.slice(0);

    // Return packed in an object
    return {options: {reuseScan: options},
            scanKey: scanKeyBytes,
            spendKeys: spendKeys,
            sigs: nSigs,
            prefix: prefix};
}

/*
 * Generate a key and related address to send to for a stealth address
 * @param {Object} scanKeyBytes Scanning key as byte array
 * @param {Object} spendKeyBytes Spending key as byte array
 * @param {Object} ephemKeyBytes (optional) Ephemeral private key as byte array,
 *                                if null will be generated.
 */
Stealth.initiateStealth = function(scanKeyBytes, spendKeyBytes, ephemKeyBytes) {
    // Parse public keys into api objects
    var scanKey = Stealth.importPublic(scanKeyBytes);
    var spendKey = Stealth.importPublic(spendKeyBytes);

    // new ephemeral key
    var encKey = new Bitcoin.Key(ephemKeyBytes);
    var ephemKey = encKey.getPub().pub.getEncoded(true);

    // Generate shared secret
    var c = Stealth.stealthDH(encKey.priv, scanKey)

    // Now generate address
    var address = Stealth.deriveAddress(spendKey, c);
    return [address, ephemKey]
}

/*
 * Generate address for receiving for a spend key with the given ephemkey
 * @param {Object} scanSecretBytes Secret as byte array
 * @param {Object} ephemKeyBytes Ephemeral key data as byte array
 * @param {Object} spendKeyBytes Spend key as bytes
 */
Stealth.uncoverStealth = function(scanSecret, ephemKeyBytes, spendKeyBytes) {
    // Parse public keys into api objects
    var decKey = Stealth.importPublic(ephemKeyBytes);
    var spendKey = Stealth.importPublic(spendKeyBytes);

    // Parse the secret into a BigInteger
    var priv = Bitcoin.BigInteger.fromByteArrayUnsigned(scanSecret.slice(0, 32));

    // Generate shared secret
    var c = Stealth.stealthDH(priv, decKey)

    // Now generate address
    return Stealth.deriveKey(spendKey, c);
}

/*
 * Derive public key from spendKey and shared secret
 * @param {Bitcoin.ECPubKey} spendKey Spend Key
 * @param {BigInteger} c Derivation value
 */
Stealth.deriveKey = function(spendKey, c) {
    // Now generate address
    var bytes = spendKey.pub
                          .add(new Bitcoin.Key(c).getPub().pub)
                          .getEncoded(true);

    return bytes;
}

/*
 * Derive a Bitcoin Address from spendKey and shared secret
 * @param {Bitcoin.ECPubKey} spendKey Spend Key
 * @param {BigInteger} c Derivation value
 */
Stealth.deriveAddress = function(spendKey, c) {
    // Now generate address
    var bytes = this.deriveKey(spendKey, c);

    // Turn to address
    var mpKeyHash = Bitcoin.Util.sha256ripe160(bytes);
    var address = new Bitcoin.Address(mpKeyHash);
    return address;
}

/*
 * Build the stealth nonce output so it can be added to a transaction.
 * returns a Bitcoin.TransactionOut object.
 * @param {Object} ephemKeyBytes Ephemeral key data as byte array
 * @param {Number} nonce Nonce for the output
 */
Stealth.buildNonceOutput = function(ephemKeyBytes, nonce) {
    var ephemScript = new Bitcoin.Script();
    ephemScript.writeOp(Bitcoin.Opcode.map.OP_RETURN);
    var nonceBytes = convert.numToBytes(nonce, 4);
    ephemScript.writeBytes([Stealth.nonceVersion].concat(nonceBytes.concat(ephemKeyBytes)));
    var stealthOut = new Bitcoin.TransactionOut({script: ephemScript, value: 0});
    return stealthOut;
}

/*
 * Check prefix against the given array
 * returns true or false
 * @param {Object} outHash byte array to compare to
 * @param {Number} prefix prefix array including first byte defining mask
 */
Stealth.checkPrefix = function(outHash, stealthPrefix) {
    var prefixN = stealthPrefix[0];
    var prefix = stealthPrefix.slice(1);
    var mask = 1<<7;
    var nbyte = 0;
    while(prefixN) {
        // check current bit
        if ((outHash[nbyte] & mask) != (prefix[nbyte] & mask)) {
            return false;
        }
        if (mask == 1) {
            // restart mask and advance byte
            mask = 1<<7;
            nbyte += 1;
        } else {
            // advance mask one bit to the right
            mask = (mask >> 1);
        }
        prefixN -= 1;
    }
    return true;
}

/*
 * Add stealth output to the given transaction and return destination address.
 * returns the new recipient (standard bitcoin address)
 * @param {String} recipient Stealth address in base58 format.
 * @param {Bitcoin.Transaction} newTx Transaction where we want to add stealth outputs.
 * @param {Object} ephemKeyBytes (optional) Ephemeral private key as byte array,
 *                                if null will be generated.
 */

Stealth.addStealth = function(recipient, newTx, ephemKeyBytes) {
    var stealthData, outHash, ephemKey, nonce;
    var stealthAddress = Stealth.parseAddress(recipient);
    var stealthPrefix = stealthAddress.prefix;
    // iterate since we might not find a nonce for our required prefix then
    // we need to create a new ephemkey
    var scanKeyBytes = stealthAddress.scanKey;

    // TODO: Correctly manage spend keys here
    var spendKeyBytes = stealthAddress.spendKeys[0];
    do {
        stealthData = Stealth.initiateStealth(scanKeyBytes, spendKeyBytes, ephemKeyBytes);
        recipient = stealthData[0].toString();
        ephemKey = stealthData[1];
        nonce = 0;
        // iterate through nonces to find a match for given prefix
        do {
	    var nonceBytes = convert.numToBytes(nonce, 4)
            outHash = Bitcoin.Util.sha256ripe160(nonceBytes.concat(ephemKey));
            nonce += 1;
        } while(nonce < 4294967296 && !Stealth.checkPrefix(outHash, stealthPrefix));

    } while(!Stealth.checkPrefix(outHash, stealthPrefix));

    // we finally mined the ephemKey that makes the hash match
    var stealthOut = Stealth.buildNonceOutput(ephemKey, nonce);
    newTx.addOutput(stealthOut);
    return recipient;
}

return Stealth;
});
<commit_msg>Add a function to derive a private key.<commit_after>/*
 * @fileOverview Stealth support.
 */

define(['darkwallet','bitcoinjs-lib'], function(DarkWallet, Bitcoin) {

var convert = Bitcoin.convert;

var Stealth = {};

// Elliptic curve N (couldn't find a way to get it from bitcoin api, so just hardcoded it here...)
Stealth.ecN = new Bitcoin.BigInteger("115792089237316195423570985008687907852837564279074904382605163141518161494337");

// Version for address format
Stealth.version = 255;

// Can these be different in the future?
Stealth.nonceVersion = Stealth.version;

/*
 * Create a bitcoin key with just public component.
 * @param {Object} Q public key as bytes
 * @private
 */
Stealth.importPublic = function(Q) {
    var key = new Bitcoin.ECPubKey(Q, true);
    return key;
}

/*
 * Perform curvedh and stealth formatting
 * @param {Bitcoin.BigInteger} e private part
 * @param {Bitcoin.Key} decKey public key
 * @private
 */
Stealth.stealthDH = function(e, decKey) {
    // diffie hellman stage
    var point = decKey.pub.multiply(e);

    // start the second stage
    var S1 = [3].concat(point.getX().toBigInteger().toByteArrayUnsigned());
    var c = convert.wordArrayToBytes(Bitcoin.Crypto.SHA256(convert.bytesToWordArray(S1)));
    return c;
}

/*
 * Format a stealth address in base58
 * @param {Object} scanPubKeyBytes Public key as byte array
 * @param {Object} spendPubKeys Spend public keys as array of byte arrays
 *
 * [version:1] [options:1] [scan_pubkey:33] [N:1] [spend_pubkey_1:33] ...
[spend_pubkey_N:33] [number_sigs:1] [prefix_length:1] [prefix:prefix_length/8, round up]
 * version = 255
 * options bitfield = 0 or 1 (reuse scan_pubkey for spends)
 */
Stealth.formatAddress = function(scanPubKeyBytes, spendPubKeys) {
    if (!spendPubKeys) {
        spendPubKeys = [];
    }
    var reuseScan = spendPubKeys.length ? 0 : 1;

    // Header, version will be added later when encoding
    var stealth = [reuseScan];

    // Add scan public key
    stealth = stealth.concat(scanPubKeyBytes);

    // Add spend public keys
    stealth = stealth.concat([spendPubKeys.length]);
    spendPubKeys.forEach(function(spendPubKey) {
        stealth = stealth.concat(spendPubKey);
    });

    // Number of signatures
    var nSigs = spendPubKeys.length || 1;
    stealth = stealth.concat([nSigs]);

    // TODO: Add prefix
    stealth = stealth.concat([0]);
    // Encode in base58 and add version
    return Bitcoin.base58.checkEncode(stealth, Stealth.version);
}

/*
 * Parse a stealth address into its forming parts
 * @param {String} recipient Address in base58 format
 */
Stealth.parseAddress = function(recipient) {
    // TODO perform consistency checks here
    var stealthBytes = Bitcoin.base58.checkDecode(recipient);
    var options = stealthBytes.splice(0, 1)[0];
    var scanKeyBytes = stealthBytes.splice(0, 33);
    var nSpendKeys = stealthBytes.splice(0, 1)[0];
    var spendKeys = [];
    for(var idx=0; idx<nSpendKeys; idx++) {
        spendKeys.push(stealthBytes.splice(0, 33));
    }
    var nSigs = stealthBytes.splice(0, 1)[0];

    // Prefix should be the remaining bytes
    var prefix = stealthBytes.slice(0);

    // Return packed in an object
    return {options: {reuseScan: options},
            scanKey: scanKeyBytes,
            spendKeys: spendKeys,
            sigs: nSigs,
            prefix: prefix};
}

/*
 * Generate a key and related address to send to for a stealth address
 * @param {Object} scanKeyBytes Scanning key as byte array
 * @param {Object} spendKeyBytes Spending key as byte array
 * @param {Object} ephemKeyBytes (optional) Ephemeral private key as byte array,
 *                                if null will be generated.
 */
Stealth.initiateStealth = function(scanKeyBytes, spendKeyBytes, ephemKeyBytes) {
    // Parse public keys into api objects
    var scanKey = Stealth.importPublic(scanKeyBytes);
    var spendKey = Stealth.importPublic(spendKeyBytes);

    // new ephemeral key
    var encKey = new Bitcoin.Key(ephemKeyBytes);
    var ephemKey = encKey.getPub().pub.getEncoded(true);

    // Generate shared secret
    var c = Stealth.stealthDH(encKey.priv, scanKey)

    // Now generate address
    var address = Stealth.deriveAddress(spendKey, c);
    return [address, ephemKey]
}

/*
 * Generate address for receiving for a spend key with the given ephemkey
 * @param {Object} scanSecretBytes Secret as byte array
 * @param {Object} ephemKeyBytes Ephemeral key data as byte array
 * @param {Object} spendKeyBytes Spend key as bytes
 */
Stealth.uncoverStealth = function(scanSecret, ephemKeyBytes, spendKeyBytes) {
    // Parse public keys into api objects
    var decKey = Stealth.importPublic(ephemKeyBytes);
    var spendKey = Stealth.importPublic(spendKeyBytes);

    // Parse the secret into a BigInteger
    var priv = Bitcoin.BigInteger.fromByteArrayUnsigned(scanSecret.slice(0, 32));

    // Generate shared secret
    var c = Stealth.stealthDH(priv, decKey)

    // Now generate address
    return Stealth.deriveKey(spendKey, c);
}

/*
 * Derive a private key from spend key and shared secret
 * @param {Bitcoin.ECKey} spendKey Spend Key
 * @param {BigInteger} c Derivation value
 */
Stealth.derivePrivateKey = function(spendKey, c) {
    // Generate the key with the bitcoin api
    return spendKey.add(c);
}

/*
 * Derive public key from spendKey and shared secret
 * @param {Bitcoin.ECPubKey} spendKey Spend Key
 * @param {BigInteger} c Derivation value
 */
Stealth.deriveKey = function(spendKey, c) {
    // Now generate address
    var bytes = spendKey.pub
                          .add(new Bitcoin.Key(c).getPub().pub)
                          .getEncoded(true);

    return bytes;
}

/*
 * Derive a Bitcoin Address from spendKey and shared secret
 * @param {Bitcoin.ECPubKey} spendKey Spend Key
 * @param {BigInteger} c Derivation value
 */
Stealth.deriveAddress = function(spendKey, c) {
    // Now generate address
    var bytes = this.deriveKey(spendKey, c);

    // Turn to address
    var mpKeyHash = Bitcoin.Util.sha256ripe160(bytes);
    var address = new Bitcoin.Address(mpKeyHash);
    return address;
}

/*
 * Build the stealth nonce output so it can be added to a transaction.
 * returns a Bitcoin.TransactionOut object.
 * @param {Object} ephemKeyBytes Ephemeral key data as byte array
 * @param {Number} nonce Nonce for the output
 */
Stealth.buildNonceOutput = function(ephemKeyBytes, nonce) {
    var ephemScript = new Bitcoin.Script();
    ephemScript.writeOp(Bitcoin.Opcode.map.OP_RETURN);
    var nonceBytes = convert.numToBytes(nonce, 4);
    ephemScript.writeBytes([Stealth.nonceVersion].concat(nonceBytes.concat(ephemKeyBytes)));
    var stealthOut = new Bitcoin.TransactionOut({script: ephemScript, value: 0});
    return stealthOut;
}

/*
 * Check prefix against the given array
 * returns true or false
 * @param {Object} outHash byte array to compare to
 * @param {Number} prefix prefix array including first byte defining mask
 */
Stealth.checkPrefix = function(outHash, stealthPrefix) {
    var prefixN = stealthPrefix[0];
    var prefix = stealthPrefix.slice(1);
    var mask = 1<<7;
    var nbyte = 0;
    while(prefixN) {
        // check current bit
        if ((outHash[nbyte] & mask) != (prefix[nbyte] & mask)) {
            return false;
        }
        if (mask == 1) {
            // restart mask and advance byte
            mask = 1<<7;
            nbyte += 1;
        } else {
            // advance mask one bit to the right
            mask = (mask >> 1);
        }
        prefixN -= 1;
    }
    return true;
}

/*
 * Add stealth output to the given transaction and return destination address.
 * returns the new recipient (standard bitcoin address)
 * @param {String} recipient Stealth address in base58 format.
 * @param {Bitcoin.Transaction} newTx Transaction where we want to add stealth outputs.
 * @param {Object} ephemKeyBytes (optional) Ephemeral private key as byte array,
 *                                if null will be generated.
 */

Stealth.addStealth = function(recipient, newTx, ephemKeyBytes) {
    var stealthData, outHash, ephemKey, nonce;
    var stealthAddress = Stealth.parseAddress(recipient);
    var stealthPrefix = stealthAddress.prefix;
    // iterate since we might not find a nonce for our required prefix then
    // we need to create a new ephemkey
    var scanKeyBytes = stealthAddress.scanKey;

    // TODO: Correctly manage spend keys here
    var spendKeyBytes = stealthAddress.spendKeys[0];
    do {
        stealthData = Stealth.initiateStealth(scanKeyBytes, spendKeyBytes, ephemKeyBytes);
        recipient = stealthData[0].toString();
        ephemKey = stealthData[1];
        nonce = 0;
        // iterate through nonces to find a match for given prefix
        do {
	    var nonceBytes = convert.numToBytes(nonce, 4)
            outHash = Bitcoin.Util.sha256ripe160(nonceBytes.concat(ephemKey));
            nonce += 1;
        } while(nonce < 4294967296 && !Stealth.checkPrefix(outHash, stealthPrefix));

    } while(!Stealth.checkPrefix(outHash, stealthPrefix));

    // we finally mined the ephemKey that makes the hash match
    var stealthOut = Stealth.buildNonceOutput(ephemKey, nonce);
    newTx.addOutput(stealthOut);
    return recipient;
}

return Stealth;
});
<|endoftext|>


<commit_before>var Manual =  Backbone.View.extend({
	el: '#load',

	events:{
		'click #manualBac': 'addBACLevel'
	},

	initialize: function(){
		this.user = new You();
		this.you = this.user.retrieveProfile();
		this.form = '<form role="form">
Alcohol Content Volume<input id="acv" name="acv" class="form-control" placeholder="0" required>

Ounzes<input id="oz" name="oz" class="form-control" placeholder="0" required>

Time<input id="time" type="number" min="0" max="24" class="form-control" required>

<button id="manualBac" class="btn btn-success">Calculate</button></form>
 Your BAC Level is
';

		this.manualBac = new BAC();
		//this.render();
	},

	render: function(){
		this.$el.html(this.form);
	},

	calcBac: function(){
		var acv = $('#acv').val();
		var oz = $('#oz').val();
		var time = $('#time').val();
		var bac = this.manualBac.calcBac(acv, oz, this.you.weight, this.you.genderBac, time);
		$('#BACLevel').empty();
		$('#BACLevel').append('
'+bac+'
');
		return bac;
	},

	getTime: function(){
		var date = new Date();
		var time = {
			hour: 0,
			min: 0
		};
		time.hour = date.getHours();
		time.min = date.getMinutes();

		var curTime = time.hour+':'+time.min;
		return curTime;
	},

	getDate: function(){
		var date2 = new Date();
		var date = {
			year: 0,
			month: 0,
			day:0
		};

		date.year = date2.getFullYear();
		date.month = date2.getMonth()+1;
		date.day = date2.getDate();

		var today = date.year+'-'+date.month+'-'+date.day;

		return today;
	},

	addBACLevel: function(){
		console.log(this.you);
		var bac = this.calcBac();
		var time = this.getTime();
		var date = this.getDate();
		var location = area.getLocation();

		var newBac = new PersonBAC({
			"date": date,
			"bacLevelHigh": bac,
			"bacLevelCur": 0,
			"lat": location.lat,
			"lng": location.lng,
			"timeStart": time,
			"timeFinish": time,
			"manualEnter": true
		});

		this.you.BACLevels.push(newBac);
		people.localStorage.update(this.you);

		console.log(newBac);
	}
})<commit_msg>Renders table from user records<commit_after>var Manual =  Backbone.View.extend({
	el: '#load',

	events:{
		'click #manualBac': 'addBACLevel'
	},

	initialize: function(){
		this.user = new You();
		this.you = this.user.retrieveProfile();
		this.form = '<form role="form">
Alcohol Content Volume<input id="acv" name="acv" class="form-control" placeholder="0" required>

Ounzes<input id="oz" name="oz" class="form-control" placeholder="0" required>

Time<input id="time" type="number" min="0" max="24" class="form-control" required>

<button id="manualBac" class="btn btn-success">Calculate</button></form>
 Your BAC Level is
';

		this.manualBac = new BAC();
		//this.render();
	},

	render: function(){
		this.$el.html(this.form);
	},

	calcBac: function(){
		var acv = $('#acv').val();
		var oz = $('#oz').val();
		var time = $('#time').val();
		var bac = this.manualBac.calcBac(acv, oz, this.you.weight, this.you.genderBac, time);
		$('#BACLevel').empty();
		$('#BACLevel').append('
'+bac+'
');
		return bac;
	},

	getTime: function(){
		var date = new Date();
		var time = {
			hour: 0,
			min: 0
		};
		time.hour = date.getHours();
		time.min = date.getMinutes();

		var curTime = time.hour+':'+time.min;
		return curTime;
	},

	getDate: function(){
		var date2 = new Date();
		var date = {
			year: 0,
			month: 0,
			day:0
		};

		date.year = date2.getFullYear();
		date.month = date2.getMonth()+1;
		date.day = date2.getDate();

		var today = date.year+'-'+date.month+'-'+date.day;

		return today;
	},

	addBACLevel: function(){
		console.log(this.you);
		var bac = this.calcBac();
		var time = this.getTime();
		var date = this.getDate();
		//var location = area.getLocation();

		var newBac = new PersonBAC({
			"date": date,
			"bacLevelHigh": bac,
			"bacLevelCur": 0,
			"lat": 0,
			"lng": 0,
			"timeStart": time,
			"timeFinish": time,
			"manualEnter": true
		});

		this.you.BACLevels.push(newBac);
		people.localStorage.update(this.you);

		console.log(newBac);
	}
})<|endoftext|>


<commit_before>(function ($widget, $util) {
    'use strict';
    var CrysyanShapeWidget = $widget.clone();
    var Shape = CrysyanShapeWidget;

    // 0:Triangle, 1:Rect ,2:Circular,
    Shape.shapeType = 0;
    // 01:stroke, 10:fill
    Shape.depictType = 1;

    Shape.lineWidth = 1;
    Shape.strokeStyle ='#000000';
    Shape.fillStyle ='#000000';
    Shape.depictType = 1;

    Shape.menu = false;

    Shape.setConfig = function () {
        var ctx = Shape.crysyanCanvas.playContext;
        //  ctx.lineJoin = pencil.config.lineJoin;
        ctx.lineWidth = Shape.lineWidth;
        ctx.strokeStyle = Shape.strokeStyle;
        ctx.fillStyle = Shape.fillStyle;
    };

    Shape.iconClick = function (ele, e) {
        var rect = ele.getBoundingClientRect();
        if (!Shape.menu) {
            Shape.menu = new Menu(Shape);
        }
        Shape.menu.display(rect);
    };
    //
    Shape.iconLeave = function (ele, e) {
        Shape.menu.hide();
    };

    Shape.mouseDown = function(e, loc) {
        Shape.menu.hide();
    };
    Shape.mouseMove = function (e, loc) {
        if (!Shape.isDown) return;
        Shape.drawShape(e, loc);
    };
    Shape.mouseUp = function (e, loc) {
        Shape.drawShape(e, loc)
    };

    Shape.drawShape = function (e, loc) {
        var ctx = Shape.crysyanCanvas.playContext;
        Shape.crysyanCanvas.restoreDrawingSurface();
        var prePoint = Shape.prePoint.loc;
        ctx.save();
        Shape.setConfig();
        ctx.beginPath();
        switch (Shape.shapeType) {
            case 0:
            case "0":
                var x = loc.x - prePoint.x;
                ctx.moveTo(prePoint.x, prePoint.y);
                ctx.lineTo(loc.x, loc.y);
                ctx.lineTo(loc.x - 2 * x, loc.y);
                break;
            case 1:
            case "1":
                ctx.rect(prePoint.x, prePoint.y, loc.x - prePoint.x, loc.y - prePoint.y);
                break;
            case 2 :
            case "2":
                var x = loc.x - prePoint.x;
                var y = loc.y - prePoint.y;
                ctx.arc(prePoint.x, prePoint.y,
                    Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)),
                    0, Math.PI * 2, false);
                break;
        }
        ctx.closePath();
        if ((Shape.depictType & 1) !== 0)
            ctx.stroke();
        if ((Shape.depictType & 2) !== 0)
            ctx.fill();
        ctx.restore();
    };

    var Menu = function (shape) {
        this.shape = shape;
        // this.isDisplay = false;
        this.menuEle = null;
    };

    Menu.prototype = {
        init: function () {
            this.menuEle = document.createElement("section");
            this.menuEle.setAttribute("id", "crysyan-pencil-menu");
            this.menuEle.setAttribute("class", "crysyan-second-toolbar");
            this.menuEle.style.display = "none";
            var innerHtml = "
";

            innerHtml = innerHtml.concat("
shape:<select id='crysyan-shape-type'>");
            var shapeOps = ["Triangle", "Rect", "Circular"];
            for (var key in shapeOps) {
                innerHtml = innerHtml.concat("<option value=\"", key, "\">", shapeOps[key], "</option>");
            }
            innerHtml = innerHtml.concat("</select>
");

            innerHtml = innerHtml.concat("
draw:<select id='crysyan-depict-type'>");
            var depictOps = {1:"stroke", 2:"fill", 3:"stroke&fill"};
            for (var key in depictOps) {
                innerHtml = innerHtml.concat("<option value=\"", key,"\">", depictOps[key], "</option>");
            }
            innerHtml = innerHtml.concat("</select>
");

            innerHtml = innerHtml.concat("
fill color:<input id='crysyan-shape-fill-color' type='color' value='", this.shape.fillStyle, "'>
");

            innerHtml = innerHtml.concat("
Done
");
            innerHtml = innerHtml.concat("
");
            this.menuEle.innerHTML = innerHtml;
            (document.getElementsByTagName("body").item(0) || document.documentElement).appendChild(this.menuEle);

            var menu = this;
            var shape = this.shape;
            var shapeOpsEle = findElement("crysyan-shape-type");
            var depictOpsEle = findElement("crysyan-depict-type");
            var fillColorEle = findElement("crysyan-shape-fill-color");
            $util.addEvent(findElement("crysan-shape-done"), "click", function () {
                shape.shapeType = shapeOpsEle.value;
                shape.fillStyle = fillColorEle.value;
                shape.depictType=parseInt(depictOpsEle.value);
                menu.hide();
            })
        },
        display: function (rect) {
            if (!this.menuEle) {
                this.init();
            }
            var style = this.menuEle.style;
            this.top = rect.top - 80;
            this.left = rect.left - 10;
            style.display = "inline";
            style.top = this.top + "px";
            style.left = this.left + "px";
        },
        hide: function () {
            var style = this.menuEle.style;
            style.display = "none";
        }
    };

    function findElement(id) {
        return document.getElementById(id);
    }

    Shape.CrysyanWidgetType = "CrysyanShapeWidget";
    // export to window
    window.CrysyanShapeWidget = CrysyanShapeWidget;
})(CrysyanWidget, CrysyanUtil);<commit_msg>set  the color for shape‘s stroke<commit_after>(function ($widget, $util) {
    'use strict';
    var CrysyanShapeWidget = $widget.clone();
    var Shape = CrysyanShapeWidget;

    // 0:Triangle, 1:Rect ,2:Circular,
    Shape.shapeType = 0;
    // 01:stroke, 10:fill
    Shape.depictType = 1;

    Shape.lineWidth = 1;
    Shape.strokeStyle ='#000000';
    Shape.fillStyle ='#000000';
    Shape.depictType = 1;

    Shape.menu = false;

    Shape.setConfig = function () {
        var ctx = Shape.crysyanCanvas.playContext;
        //  ctx.lineJoin = pencil.config.lineJoin;
        ctx.lineWidth = Shape.lineWidth;
        ctx.strokeStyle = Shape.strokeStyle;
        ctx.fillStyle = Shape.fillStyle;
    };

    Shape.iconClick = function (ele, e) {
        var rect = ele.getBoundingClientRect();
        if (!Shape.menu) {
            Shape.menu = new Menu(Shape);
        }
        Shape.menu.display(rect);
    };
    //
    Shape.iconLeave = function (ele, e) {
        Shape.menu.hide();
    };

    Shape.mouseDown = function(e, loc) {
        Shape.menu.hide();
    };
    Shape.mouseMove = function (e, loc) {
        if (!Shape.isDown) return;
        Shape.drawShape(e, loc);
    };
    Shape.mouseUp = function (e, loc) {
        Shape.drawShape(e, loc)
    };

    Shape.drawShape = function (e, loc) {
        var ctx = Shape.crysyanCanvas.playContext;
        Shape.crysyanCanvas.restoreDrawingSurface();
        var prePoint = Shape.prePoint.loc;
        ctx.save();
        Shape.setConfig();
        ctx.beginPath();
        switch (Shape.shapeType) {
            case 0:
            case "0":
                var x = loc.x - prePoint.x;
                ctx.moveTo(prePoint.x, prePoint.y);
                ctx.lineTo(loc.x, loc.y);
                ctx.lineTo(loc.x - 2 * x, loc.y);
                break;
            case 1:
            case "1":
                ctx.rect(prePoint.x, prePoint.y, loc.x - prePoint.x, loc.y - prePoint.y);
                break;
            case 2 :
            case "2":
                var x = loc.x - prePoint.x;
                var y = loc.y - prePoint.y;
                ctx.arc(prePoint.x, prePoint.y,
                    Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)),
                    0, Math.PI * 2, false);
                break;
        }
        ctx.closePath();
        if ((Shape.depictType & 1) !== 0)
            ctx.stroke();
        if ((Shape.depictType & 2) !== 0)
            ctx.fill();
        ctx.restore();
    };

    var Menu = function (shape) {
        this.shape = shape;
        // this.isDisplay = false;
        this.menuEle = null;
    };

    Menu.prototype = {
        init: function () {
            this.menuEle = document.createElement("section");
            this.menuEle.setAttribute("id", "crysyan-pencil-menu");
            this.menuEle.setAttribute("class", "crysyan-second-toolbar");
            this.menuEle.style.display = "none";
            var innerHtml = "
";

            innerHtml = innerHtml.concat("
shape:<select id='crysyan-shape-type'>");
            var shapeOps = ["Triangle", "Rect", "Circular"];
            for (var key in shapeOps) {
                innerHtml = innerHtml.concat("<option value=\"", key, "\">", shapeOps[key], "</option>");
            }
            innerHtml = innerHtml.concat("</select>
");

            innerHtml = innerHtml.concat("
draw:<select id='crysyan-depict-type'>");
            var depictOps = {1:"stroke", 2:"fill", 3:"stroke&fill"};
            for (var key in depictOps) {
                innerHtml = innerHtml.concat("<option value=\"", key,"\">", depictOps[key], "</option>");
            }
            innerHtml = innerHtml.concat("</select>
");

            innerHtml = innerHtml.concat("
stroke color:<input id='crysyan-shape-stroke-color' type='color' value='", this.shape.strokeStyle, "'>
");

            innerHtml = innerHtml.concat("
fill color:<input id='crysyan-shape-fill-color' type='color' value='", this.shape.fillStyle, "'>
");

            innerHtml = innerHtml.concat("
Done
");
            innerHtml = innerHtml.concat("
");
            this.menuEle.innerHTML = innerHtml;
            (document.getElementsByTagName("body").item(0) || document.documentElement).appendChild(this.menuEle);

            var menu = this;
            var shape = this.shape;
            var shapeOpsEle = findElement("crysyan-shape-type");
            var depictOpsEle = findElement("crysyan-depict-type");
            var fillColorEle = findElement("crysyan-shape-fill-color");
            var strokeColorEle = findElement("crysyan-shape-stroke-color");
            $util.addEvent(findElement("crysan-shape-done"), "click", function () {
                shape.shapeType = shapeOpsEle.value;
                shape.fillStyle = fillColorEle.value;
                shape.strokeStyle = strokeColorEle.value;
                shape.depictType=parseInt(depictOpsEle.value);
                menu.hide();
            })
        },
        display: function (rect) {
            if (!this.menuEle) {
                this.init();
            }
            var style = this.menuEle.style;
            this.top = rect.top - 80;
            this.left = rect.left - 10;
            style.display = "inline";
            style.top = this.top + "px";
            style.left = this.left + "px";
        },
        hide: function () {
            var style = this.menuEle.style;
            style.display = "none";
        }
    };

    function findElement(id) {
        return document.getElementById(id);
    }

    Shape.CrysyanWidgetType = "CrysyanShapeWidget";
    // export to window
    window.CrysyanShapeWidget = CrysyanShapeWidget;
})(CrysyanWidget, CrysyanUtil);<|endoftext|>


<commit_before>// Wizard Helper JavaScript class to communicate with the WizardHelper PHP class
function WizardHelper(sClass, sFormPrefix)
{
	this.m_oData = { 'm_sClass' : '',
					 'm_oFieldsMap': {},
					 'm_oCurrentValues': {},
					 'm_aDefaultValueRequested': [],
					 'm_aAllowedValuesRequested': [],
					 'm_oDefaultValue': {},
					 'm_oAllowedValues': {},
					 'm_iFieldsCount' : 0,
					 'm_sFormPrefix' : sFormPrefix
					};
	this.m_oData.m_sClass = sClass;

	// Methods
	this.SetFieldsMap = function (oFieldsMap)
	{
		this.m_oData.m_oFieldsMap = oFieldsMap;
	}

	this.SetFieldsCount = function (count)
	{
		this.m_oData.m_iFieldsCount = count;

	}

	this.GetFieldId = function(sFieldName)
	{
		id = this.m_oData.m_oFieldsMap[sFieldName];
		return id;
	}

	this.RequestDefaultValue = function (sFieldName)
	{
		currentValue = this.UpdateCurrentValue(sFieldName);
		if (currentValue == null)
		{
			this.m_oData.m_aDefaultValueRequested.push(sFieldName);
		}
	}
	this.RequestAllowedValues = function (sFieldName)
	{
		this.m_oData.m_aAllowedValuesRequested.push(sFieldName);
	}
	this.SetCurrentValue = function (sFieldName, currentValue)
	{
		this.m_oData.m_oCurrentValues[sFieldName] = currentValue;
	}

	this.ToJSON = function ()
	{
		return JSON.stringify(this.m_oData);
	}

	this.FromJSON = function (sJSON)
	{
		//console.log('Parsing JSON:'+sJSON);
		this.m_oData = JSON.parse(sJSON);
	}

	this.ResetQuery = function ()
	{
		this.m_oData.m_aDefaultValueRequested = [];
		this.m_oData.m_oDefaultValue = {};
		this.m_oData.m_aAllowedValuesRequested = [];
		this.m_oData.m_oAllowedValues = {};
	}

	this.UpdateFields = function ()
	{
		//console.log('** UpdateFields **');
		// Set the full HTML for the input field
		for(i=0; i<this.m_oData.m_aAllowedValuesRequested.length; i++)
		{
			sAttCode = this.m_oData.m_aAllowedValuesRequested[i];
			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
			//console.log('Setting #field_'+sFieldId+' to: '+this.m_oData.m_oAllowedValues[sAttCode]);
			$('#field_'+sFieldId).html(this.m_oData.m_oAllowedValues[sAttCode]);
		}
		// Set the actual value of the input
		for(i=0; i<this.m_oData.m_aDefaultValueRequested.length; i++)
		{
			sAttCode = this.m_oData.m_aDefaultValueRequested[i];
			defaultValue = this.m_oData.m_oDefaultValue[sAttCode];
			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
			$('#'+sFieldId).val(defaultValue);
		}
	}

	this.UpdateWizard = function ()
	{
		//console.log('** UpdateWizard **')
		for(sFieldCode in this.m_oData.m_oFieldsMap)
		{
			sCleanFieldCode = sFieldCode.replace('"', '');
			//console.log(sFieldCode);
			this.UpdateCurrentValue(sCleanFieldCode);
		}
	}

	this.AjaxQueryServer = function ()
	{
		//console.log('data sent:', this.ToJSON());
		//console.log('oWizard:', this);
		$.post('ajax.render.php',
		   { operation: 'wizard_helper', json_obj: this.ToJSON() },
			function(html){
				$('#ajax_content').html(html);
				//console.log('data received:', oWizardHelper);
				//oWizardHelper.FromJSON(json_data);
				//oWizardHelper.UpdateFields(); // Is done directly in the html provided by ajax.render.php
				//console.log(oWizardHelper);
				//$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
			});
	}

	this.Preview = function (divId)
	{
		//console.log('data sent:', this.ToJSON());
		//console.log('oWizard:', this);
		$('#'+divId).load('ajax.render.php?operation=wizard_helper_preview',
		   	{'json_obj': this.ToJSON()},
			function(responseText, textStatus, XMLHttpRequest){
				$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
			});
	}

	this.UpdateCurrentValue = function (sFieldCode)
	{
		value = $('#'+this.m_oData.m_oFieldsMap[sFieldCode]).val();
		if (value == '')
		{
			value = null;
		}
		this.m_oData.m_oCurrentValues[sFieldCode] = value;
		return value;
	}

	this.UpdateDependentFields = function(aFieldNames)
	{
		index = 0;
		this.ResetQuery();
		this.UpdateWizard();
		while(index < aFieldNames.length )
		{
			sAttCode = aFieldNames[index];
			sFieldId = this.GetFieldId(sAttCode);
			$('#v_'+sFieldId).html('');
			this.RequestAllowedValues(sAttCode);
			index++;
		}
		this.AjaxQueryServer();
	}
}
<commit_msg>Fixed Trac #338: when updating a 'dependent' field, don't forget to cascade the refresh in case this field has its own dependent fields.<commit_after>// Wizard Helper JavaScript class to communicate with the WizardHelper PHP class

if (!Array.prototype.indexOf) // Emulation of the indexOf function for IE and old browsers
{
	Array.prototype.indexOf = function(elt /*, from*/)
	{
		var len = this.length;
		var from = Number(arguments[1]) || 0;
		from = (from < 0) ? Math.ceil(from) : Math.floor(from);

		if (from < 0) from += len;
		for (; from < len; from++)
		{
			if (from in this && this[from] === elt) return from;
		}
		return -1;
	};
}

function WizardHelper(sClass, sFormPrefix)
{
	this.m_oData = { 'm_sClass' : '',
					 'm_oFieldsMap': {},
					 'm_oCurrentValues': {},
					 'm_aDefaultValueRequested': [],
					 'm_aAllowedValuesRequested': [],
					 'm_oDefaultValue': {},
					 'm_oAllowedValues': {},
					 'm_iFieldsCount' : 0,
					 'm_sFormPrefix' : sFormPrefix
					};
	this.m_oData.m_sClass = sClass;

	// Methods
	this.SetFieldsMap = function (oFieldsMap)
	{
		this.m_oData.m_oFieldsMap = oFieldsMap;
	}

	this.SetFieldsCount = function (count)
	{
		this.m_oData.m_iFieldsCount = count;

	}

	this.GetFieldId = function(sFieldName)
	{
		id = this.m_oData.m_oFieldsMap[sFieldName];
		return id;
	}

	this.RequestDefaultValue = function (sFieldName)
	{
		currentValue = this.UpdateCurrentValue(sFieldName);
		if (currentValue == null)
		{
			this.m_oData.m_aDefaultValueRequested.push(sFieldName);
		}
	}
	this.RequestAllowedValues = function (sFieldName)
	{
		this.m_oData.m_aAllowedValuesRequested.push(sFieldName);
	}
	this.SetCurrentValue = function (sFieldName, currentValue)
	{
		this.m_oData.m_oCurrentValues[sFieldName] = currentValue;
	}

	this.ToJSON = function ()
	{
		return JSON.stringify(this.m_oData);
	}

	this.FromJSON = function (sJSON)
	{
		//console.log('Parsing JSON:'+sJSON);
		this.m_oData = JSON.parse(sJSON);
	}

	this.ResetQuery = function ()
	{
		this.m_oData.m_aDefaultValueRequested = [];
		this.m_oData.m_oDefaultValue = {};
		this.m_oData.m_aAllowedValuesRequested = [];
		this.m_oData.m_oAllowedValues = {};
	}

	this.UpdateFields = function ()
	{
		var aRefreshed = [];
		//console.log('** UpdateFields **');
		// Set the full HTML for the input field
		for(i=0; i<this.m_oData.m_aAllowedValuesRequested.length; i++)
		{
			sAttCode = this.m_oData.m_aAllowedValuesRequested[i];
			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
			//console.log('Setting #field_'+sFieldId+' to: '+this.m_oData.m_oAllowedValues[sAttCode]);
			$('#field_'+sFieldId).html(this.m_oData.m_oAllowedValues[sAttCode]);
			aRefreshed.push(sFieldId);
		}
		// Set the actual value of the input
		for(i=0; i<this.m_oData.m_aDefaultValueRequested.length; i++)
		{
			sAttCode = this.m_oData.m_aDefaultValueRequested[i];
			defaultValue = this.m_oData.m_oDefaultValue[sAttCode];
			sFieldId = this.m_oData.m_oFieldsMap[sAttCode];
			$('#'+sFieldId).val(defaultValue);
			if (!aRefreshed.indexOf(sFieldId))
			{
				aRefreshed.push(sFieldId);
			}
		}
		// For each "refreshed" field, asynchronously trigger a change in case there are dependent fields to update
		for(i=0; i<aRefreshed.length; i++)
		{
			var sString = "$('#"+aRefreshed[i]+"').trigger('change');"
			window.setTimeout(sString, 1); // Synchronous 'trigger' does nothing, call it asynchronously
		}
	}

	this.UpdateWizard = function ()
	{
		//console.log('** UpdateWizard **')
		for(sFieldCode in this.m_oData.m_oFieldsMap)
		{
			sCleanFieldCode = sFieldCode.replace('"', '');
			//console.log(sFieldCode);
			this.UpdateCurrentValue(sCleanFieldCode);
		}
	}

	this.AjaxQueryServer = function ()
	{
		//console.log('data sent:', this.ToJSON());
		//console.log('oWizard:', this);
		$.post('ajax.render.php',
		   { operation: 'wizard_helper', json_obj: this.ToJSON() },
			function(html){
				$('#ajax_content').html(html);
				//console.log('data received:', oWizardHelper);
				//oWizardHelper.FromJSON(json_data);
				//oWizardHelper.UpdateFields(); // Is done directly in the html provided by ajax.render.php
				//console.log(oWizardHelper);
				//$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
			});
	}

	this.Preview = function (divId)
	{
		//console.log('data sent:', this.ToJSON());
		//console.log('oWizard:', this);
		$('#'+divId).load('ajax.render.php?operation=wizard_helper_preview',
		   	{'json_obj': this.ToJSON()},
			function(responseText, textStatus, XMLHttpRequest){
				$('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} );
			});
	}

	this.UpdateCurrentValue = function (sFieldCode)
	{
		value = $('#'+this.m_oData.m_oFieldsMap[sFieldCode]).val();
		if (value == '')
		{
			value = null;
		}
		this.m_oData.m_oCurrentValues[sFieldCode] = value;
		return value;
	}

	this.UpdateDependentFields = function(aFieldNames)
	{
		index = 0;
		this.ResetQuery();
		this.UpdateWizard();
		while(index < aFieldNames.length )
		{
			sAttCode = aFieldNames[index];
			sFieldId = this.GetFieldId(sAttCode);
			$('#v_'+sFieldId).html('');
			this.RequestAllowedValues(sAttCode);
			index++;
		}
		this.AjaxQueryServer();
	}
}
<|endoftext|>