<commit_before>const groupFn = function(fn, label) {
  console.group(label || 'Group-function')
  const ret = fn()
  console.groupEnd()
  return ret
}

class Program {
  constructor() {
    this.functions = {}
    this.labels = {}
    this.defaultAttributes = {
      type: 'c', auto: true, conditional: false
    }
  }

  parseAttributes(str) {
    const timesAppearing = {}
    const validAttributes = [
      'c', 'i', 'r',
      '0', '1',
      '!', '?'
    ]
    const attributes = {}
    let contradictory
    let overallContradictory = 0
    const result = {}

    for (let char of str) {
      timesAppearing[char] = (timesAppearing[char] || 0) + 1
      if (timesAppearing[char] > 1) throw new Error(
        `In attributes "${str}" ${char} appears more than once`)

      if (!validAttributes.includes(char)) throw new Error(
        `In attributes "${str}" ${char} is an invalid attribute`)

      attributes[char] = true
    }

    contradictory = 0
    if (attributes['c']) {
      contradictory++
      result.type = 'c'
    }
    if (attributes['i']) {
      contradictory++
      result.type = 'i'
    }
    if (attributes['r']) {
      contradictory++
      result.type = 'r'
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['0']) {
      contradictory++
      result.auto = false
    }
    if (attributes['1']) {
      contradictory++
      result.auto = true
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['?']) {
      result.conditional = true
      contradictory++
    }
    if (attributes['!']) {
      result.conditional = false
      contradictory++
    }
    overallContradictory |= contradictory > 1

    if (overallContradictory) throw new Error(
      `Attributes "${str}" is contradictory`)

    const withDefaults = Object.assign({}, this.defaultAttributes, result)

    return withDefaults
  }

  compile(code, environment = {vars: {}}) {
    const lines = code.split('\n')
    const results = []

    LINE_LOOP:
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      let line = lines[lineIndex]

      if (line === '')
        continue

      if (line.startsWith('define')) {
        // Getting name
        let name = ''

        let defCharIndex = 6
        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === '(')
            break

          name += char
        }

        // Getting parameters
        let params = []
        let currentParam = ''

        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === ')') {
            if (currentParam) {
              params.push(currentParam)
            }
            break
          }

          if (char === ',') {
            params.push(currentParam)
            currentParam = ''
            continue
          }

          if (char === ' ' && currentParam === '')
            continue

          currentParam += char
        }

        // Getting code lines
        const codeLines = []

        while (true) {
          lineIndex++
          line = lines[lineIndex]

          if (lineIndex > lines.length)
            break

          if (!line.startsWith('  '))
            break

          codeLines.push(line.slice(2))
        }

        // Storing function
        this.functions[name] = {codeLines, params}

        continue
      }

      if (line.startsWith('!')) {
        const env = Object.assign({}, environment)

        if (line.endsWith(':')) {
          const argCodeLines = []
          while (true) {
            lineIndex++
            const line = lines[lineIndex]
            if (lineIndex >= lines.length) break
            if (!line.startsWith('  ')) {
              lineIndex--
              break
            }
            argCodeLines.push(line.slice(2))
          }
          env.passedCode = argCodeLines
        }

        console.log('line:', line)

        const { funcResults, name } = this.processFunctionCall(
          line.slice(1), env)

        if (funcResults.length === 0) {
          console.warn(`Procedure call "${name}" had no results!`)
        }

        for (let r of funcResults) {
          results.push(r)
        }

        continue
      }

      if (line.startsWith('#')) {
        const blockName = line.slice(1)
        results.push({block: blockName})
        continue
      }

      if (line.startsWith('@')) {
        let labelName
        if (environment.labelSafe) {
          labelName = `__${environment.labelSafe}__${line.slice(1)}`
        } else {
          labelName = line.slice(1)
        }
        results.push({label: labelName})
        continue
      }

      let attributeStr = ''
      let charIndex = -1
      let char = ''
      while (true) {
        charIndex++
        char = line[charIndex]
        if (char === ' ') {
          attributeStr = ''
          break
        }
        if (charIndex >= line.length) {
          attributeStr = ''
          break
        }
        if (char === ':') break
        attributeStr += char
      }

      let commandPart
      if (attributeStr) {
        let char = ''
        do {
          charIndex++
          char = line[charIndex]
        } while (char === ' ')
        commandPart = line.slice(charIndex)
      } else {
        commandPart = line
      }
      const attributes = this.parseAttributes(attributeStr)
      // console.log('attributes proper:', attributes)

      const command = this.expression(commandPart, environment)

      const obj = Object.assign({command}, attributes)

      results.push(obj)
    }

    return results
  }

  expression(code, environment = {vars: {}}) {
    // TODO: put reused stuff in here because having identical code in multiple
    // parts of a program is BAAAD!

    // Parts of lines that should be put in here:
    // * everything past the command name part of code lines (past first space)
    //   (or just the whole command line)
    // * arguments (each in a separate expression call)

    let charIndex = -1
    let result = ''

    console.log('-')
    console.log('EXPRESSION:', code)
    console.log('ENVIRONMENT:', environment)

    while (true) {
      charIndex++
      let char = code[charIndex]
      if (charIndex >= code.length) break

      // Variables
      if (char === '$') {
        let name = ''
        while (true) {
          charIndex++
          char = code[charIndex]
          if (charIndex >= code.length) break

          if (char === ' ') {
            charIndex--
            break
          }

          name += char
        }

        // console.log('Variable:', name)
        // console.log('Environment:', environment)
        // console.log('Value:', environment.vars[name])

        if (!environment.vars.hasOwnProperty(name))
          throw new Error(`Access to undefined variable: "${name}"`)

        result += environment.vars[name]

        continue
      }

      // Function calls
      if (char === '^') {
        // console.group('fn call')

        const { funcResults, newCharIndex, name } = this.processFunctionCall(
          code.slice(charIndex + 1), environment)
        charIndex += newCharIndex

        const lastLine = funcResults.slice(-1)[0]

        if (funcResults.length > 1)
          console.warn(`Function call "${name}" had more than one result!`
                    + ' (Using last result.)')
        if (!lastLine) console.warn(`Function "${name}" returned nothing!`)

        result += lastLine.command

        // console.groupEnd()

        continue
      }

      // Labels - this is to add special codes for each scope kinda.
      // To avoid passed functions overwriting labels etc blah blah
      if (environment.labelSafe) {
        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = code[charIndex]
            if (char === ' ') {
              charIndex--
              break
            }
            if (charIndex >= code.length) break
            labelName += char
          }
          console.log('foo:', labelName)
          result += '@__' + environment.labelSafe + '__' + labelName
          continue
        }
      }

      result += char
    }

    console.log('RESULT:', result)

    return result
  }

  processFunctionCall(code, environment) {
    // PROCESS PROCESS PROCESS

    // Getting name.
    let name = ''

    let char = ''
    let charIndex = -1
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break

      if (char === '(') break

      name += char
    }

    let func
    console.log(environment.vars)
    if (environment.vars.hasOwnProperty(name)) {
      func = environment.vars[name]
      console.log('lel')
    } else {
      func = this.functions[name]
    }
    console.log('hi:', environment)
    console.log('fn:', func)
    const params = func.params
    const codeLines = func.codeLines
    const env = {
      vars: {},
      labelSafe: 'safe-' + Math.random()
    }

    // Getting arguments.
    const argsSliceStart = charIndex + 1
    let parenWeight = 0
    let currentArg = ''
    const args = []
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break
      if (char === ')') {
        if (parenWeight === 0) {
          if (currentArg) {
            args.push(currentArg)
          }
          break
        } else {
          parenWeight--
        }
      }
      if (char === '(') {
        parenWeight++
      }
      if (char === ',' && parenWeight === 0) {
        args.push(currentArg)
        currentArg = ''
        continue
      }
      if (char === ' ' && currentArg == '') continue
      currentArg += char
    }
    const argCode = code.slice(argsSliceStart, charIndex)
    // console.log('Function name:', name)
    // console.log('Arguments:', args)

    console.log('test', environment.passedCode)
    for (let argIndex = 0; argIndex < params.length; argIndex++) {
      console.log('Param/arg', params[argIndex], '=', args[argIndex])

      if (args[argIndex]) {
        env.vars[params[argIndex]] = this.expression(
          args[argIndex], environment)
      }
    }

    if (environment.passedCode) {
      env.vars[params[params.length - 1]] = {
        codeLines: environment.passedCode,
        params: []
      }
    }

    const funcResults = this.compile(codeLines.join('\n'), env)
    return {newCharIndex: charIndex + 1, funcResults, name}
  }

  handleLabels(stack) {
    // postprocessing is s c a r y

    const results = []

    let labelPositions = {}

    // Set up label positions
    let blockIndex = -1
    let labelCount = 0
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break
      if (block.label) {
        // Labels aren't actual blocks so ignore them in the position.
        labelPositions[block.label] = blockIndex - labelCount
        labelCount++
      }
    }

    // Remove label "blocks"
    stack = stack.filter(t => !t.label)

    // Replace inline-labels with the correct relative positions
    blockIndex = -1
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break

      if (!block.command) {
        results.push(block)
        continue
      }

      let newCommand = ''
      let charIndex = -1
      let char
      while (true) {
        charIndex++
        char = block.command[charIndex]
        if (charIndex >= block.command.length) break

        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = block.command[charIndex]
            if (char === ' ') break
            if (charIndex >= block.command.length) break
            labelName += char
          }

          const y = labelPositions[labelName] - blockIndex
          newCommand += `~ ~${y} ~ `
          continue
        }

        newCommand += char
      }

      results.push(Object.assign({}, block, {command: newCommand}))
    }

    return results
  }
}

const p = new Program()
let stack
stack = p.compile(`

define loop(times, main)
  scoreboard players set LOOP loopCounter $times
  setblock ~ ~2 ~ redstone_block
  #glass
  @loop
  #quartz_ore
  i0: setblock @loop netherrack
  scoreboard players test LOOP loopCounter 0 0
  ?: setblock @done redstone_block
  scoreboard players test LOOP loopCounter 1 *
  ?: scoreboard players remove LOOP loopCounter 1
  ?: setblock @body redstone_block
  #glass
  @body
  #quartz_ore
  i0: setblock @body netherrack
  !main()
  setblock @loop redstone_block
  #glass
  @done
  #quartz_ore
  i0: setblock @done netherrack

i0:
say before loop
!loop(5):
  say in loop
say after loop

`)
console.dir(stack)
stack = p.handleLabels(stack)
console.dir(stack)
console.log(CBU.summonStack(stack))
<commit_msg>Finally get comments started<commit_after>const groupFn = function(fn, label) {
  console.group(label || 'Group-function')
  const ret = fn()
  console.groupEnd()
  return ret
}

class Program {
  constructor() {
    this.functions = {}
    this.labels = {}
    this.defaultAttributes = {
      type: 'c', auto: true, conditional: false
    }
  }

  parseAttributes(str) {
    const timesAppearing = {}
    const validAttributes = [
      'c', 'i', 'r',
      '0', '1',
      '!', '?'
    ]
    const attributes = {}
    let contradictory
    let overallContradictory = 0
    const result = {}

    for (let char of str) {
      timesAppearing[char] = (timesAppearing[char] || 0) + 1
      if (timesAppearing[char] > 1) throw new Error(
        `In attributes "${str}" ${char} appears more than once`)

      if (!validAttributes.includes(char)) throw new Error(
        `In attributes "${str}" ${char} is an invalid attribute`)

      attributes[char] = true
    }

    contradictory = 0
    if (attributes['c']) {
      contradictory++
      result.type = 'c'
    }
    if (attributes['i']) {
      contradictory++
      result.type = 'i'
    }
    if (attributes['r']) {
      contradictory++
      result.type = 'r'
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['0']) {
      contradictory++
      result.auto = false
    }
    if (attributes['1']) {
      contradictory++
      result.auto = true
    }
    overallContradictory |= contradictory > 1

    contradictory = 0
    if (attributes['?']) {
      result.conditional = true
      contradictory++
    }
    if (attributes['!']) {
      result.conditional = false
      contradictory++
    }
    overallContradictory |= contradictory > 1

    if (overallContradictory) throw new Error(
      `Attributes "${str}" is contradictory`)

    const withDefaults = Object.assign({}, this.defaultAttributes, result)

    return withDefaults
  }

  compile(code, environment = {vars: {}}) {
    const lines = code.split('\n')
    const results = []

    LINE_LOOP:
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
      let line = lines[lineIndex]

      if (line === '' || line.startsWith('//'))
        continue

      if (line.startsWith('define')) {
        // Getting name
        let name = ''

        let defCharIndex = 6
        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === '(')
            break

          name += char
        }

        // Getting parameters
        let params = []
        let currentParam = ''

        while (true) {
          defCharIndex++
          const char = line[defCharIndex]

          if (defCharIndex >= line.length)
            break

          if (char === ')') {
            if (currentParam) {
              params.push(currentParam)
            }
            break
          }

          if (char === ',') {
            params.push(currentParam)
            currentParam = ''
            continue
          }

          if (char === ' ' && currentParam === '')
            continue

          currentParam += char
        }

        // Getting code lines
        const codeLines = []

        while (true) {
          lineIndex++
          line = lines[lineIndex]

          if (lineIndex > lines.length)
            break

          if (!line.startsWith('  '))
            break

          codeLines.push(line.slice(2))
        }

        // Storing function
        this.functions[name] = {codeLines, params}

        continue
      }

      if (line.startsWith('!')) {
        const env = Object.assign({}, environment)

        if (line.endsWith(':')) {
          const argCodeLines = []
          while (true) {
            lineIndex++
            const line = lines[lineIndex]
            if (lineIndex >= lines.length) break
            if (!line.startsWith('  ')) {
              lineIndex--
              break
            }
            argCodeLines.push(line.slice(2))
          }
          env.passedCode = argCodeLines
        }

        console.log('line:', line)

        const { funcResults, name } = this.processFunctionCall(
          line.slice(1), env)

        if (funcResults.length === 0) {
          console.warn(`Procedure call "${name}" had no results!`)
        }

        for (let r of funcResults) {
          results.push(r)
        }

        continue
      }

      if (line.startsWith('#')) {
        const blockName = line.slice(1)
        results.push({block: blockName})
        continue
      }

      if (line.startsWith('@')) {
        let labelName
        if (environment.labelSafe) {
          labelName = `__${environment.labelSafe}__${line.slice(1)}`
        } else {
          labelName = line.slice(1)
        }
        results.push({label: labelName})
        continue
      }

      let attributeStr = ''
      let charIndex = -1
      let char = ''
      while (true) {
        charIndex++
        char = line[charIndex]
        if (char === ' ') {
          attributeStr = ''
          break
        }
        if (charIndex >= line.length) {
          attributeStr = ''
          break
        }
        if (char === ':') break
        attributeStr += char
      }

      let commandPart
      if (attributeStr) {
        let char = ''
        do {
          charIndex++
          char = line[charIndex]
        } while (char === ' ')
        commandPart = line.slice(charIndex)
      } else {
        commandPart = line
      }
      const attributes = this.parseAttributes(attributeStr)
      // console.log('attributes proper:', attributes)

      const command = this.expression(commandPart, environment)

      const obj = Object.assign({command}, attributes)

      results.push(obj)
    }

    return results
  }

  expression(code, environment = {vars: {}}) {
    // TODO: put reused stuff in here because having identical code in multiple
    // parts of a program is BAAAD!

    // Parts of lines that should be put in here:
    // * everything past the command name part of code lines (past first space)
    //   (or just the whole command line)
    // * arguments (each in a separate expression call)

    let charIndex = -1
    let result = ''

    console.log('-')
    console.log('EXPRESSION:', code)
    console.log('ENVIRONMENT:', environment)

    while (true) {
      charIndex++
      let char = code[charIndex]
      if (charIndex >= code.length) break

      // Variables
      if (char === '$') {
        let name = ''
        while (true) {
          charIndex++
          char = code[charIndex]
          if (charIndex >= code.length) break

          if (char === ' ') {
            charIndex--
            break
          }

          name += char
        }

        // console.log('Variable:', name)
        // console.log('Environment:', environment)
        // console.log('Value:', environment.vars[name])

        if (!environment.vars.hasOwnProperty(name))
          throw new Error(`Access to undefined variable: "${name}"`)

        result += environment.vars[name]

        continue
      }

      // Function calls
      if (char === '^') {
        // console.group('fn call')

        const { funcResults, newCharIndex, name } = this.processFunctionCall(
          code.slice(charIndex + 1), environment)
        charIndex += newCharIndex

        const lastLine = funcResults.slice(-1)[0]

        if (funcResults.length > 1)
          console.warn(`Function call "${name}" had more than one result!`
                    + ' (Using last result.)')
        if (!lastLine) console.warn(`Function "${name}" returned nothing!`)

        result += lastLine.command

        // console.groupEnd()

        continue
      }

      // Labels - this is to add special codes for each scope kinda.
      // To avoid passed functions overwriting labels etc blah blah
      if (environment.labelSafe) {
        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = code[charIndex]
            if (char === ' ') {
              charIndex--
              break
            }
            if (charIndex >= code.length) break
            labelName += char
          }
          console.log('foo:', labelName)
          result += '@__' + environment.labelSafe + '__' + labelName
          continue
        }
      }

      result += char
    }

    console.log('RESULT:', result)

    return result
  }

  processFunctionCall(code, environment) {
    // PROCESS PROCESS PROCESS

    // Getting name.
    let name = ''

    let char = ''
    let charIndex = -1
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break

      if (char === '(') break

      name += char
    }

    let func
    console.log(environment.vars)
    if (environment.vars.hasOwnProperty(name)) {
      func = environment.vars[name]
      console.log('lel')
    } else {
      func = this.functions[name]
    }
    console.log('hi:', environment)
    console.log('fn:', func)
    const params = func.params
    const codeLines = func.codeLines
    const env = {
      vars: {},
      labelSafe: 'safe-' + Math.random()
    }

    // Getting arguments.
    const argsSliceStart = charIndex + 1
    let parenWeight = 0
    let currentArg = ''
    const args = []
    while (true) {
      charIndex++
      char = code[charIndex]
      if (charIndex >= code.length) break
      if (char === ')') {
        if (parenWeight === 0) {
          if (currentArg) {
            args.push(currentArg)
          }
          break
        } else {
          parenWeight--
        }
      }
      if (char === '(') {
        parenWeight++
      }
      if (char === ',' && parenWeight === 0) {
        args.push(currentArg)
        currentArg = ''
        continue
      }
      if (char === ' ' && currentArg == '') continue
      currentArg += char
    }
    const argCode = code.slice(argsSliceStart, charIndex)
    // console.log('Function name:', name)
    // console.log('Arguments:', args)

    console.log('test', environment.passedCode)
    for (let argIndex = 0; argIndex < params.length; argIndex++) {
      console.log('Param/arg', params[argIndex], '=', args[argIndex])

      if (args[argIndex]) {
        env.vars[params[argIndex]] = this.expression(
          args[argIndex], environment)
      }
    }

    if (environment.passedCode) {
      env.vars[params[params.length - 1]] = {
        codeLines: environment.passedCode,
        params: []
      }
    }

    const funcResults = this.compile(codeLines.join('\n'), env)
    return {newCharIndex: charIndex + 1, funcResults, name}
  }

  handleLabels(stack) {
    // postprocessing is s c a r y

    const results = []

    let labelPositions = {}

    // Set up label positions
    let blockIndex = -1
    let labelCount = 0
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break
      if (block.label) {
        // Labels aren't actual blocks so ignore them in the position.
        labelPositions[block.label] = blockIndex - labelCount
        labelCount++
      }
    }

    // Remove label "blocks"
    stack = stack.filter(t => !t.label)

    // Replace inline-labels with the correct relative positions
    blockIndex = -1
    while (true) {
      blockIndex++
      const block = stack[blockIndex]
      if (blockIndex >= stack.length) break

      if (!block.command) {
        results.push(block)
        continue
      }

      let newCommand = ''
      let charIndex = -1
      let char
      while (true) {
        charIndex++
        char = block.command[charIndex]
        if (charIndex >= block.command.length) break

        if (char === '@') {
          let labelName = ''
          while (true) {
            charIndex++
            char = block.command[charIndex]
            if (char === ' ') break
            if (charIndex >= block.command.length) break
            labelName += char
          }

          const y = labelPositions[labelName] - blockIndex
          newCommand += `~ ~${y} ~ `
          continue
        }

        newCommand += char
      }

      results.push(Object.assign({}, block, {command: newCommand}))
    }

    return results
  }
}

const p = new Program()
let stack
stack = p.compile(`

define loop(times, main)
  scoreboard players set LOOP loopCounter $times
  setblock ~ ~2 ~ redstone_block
  #glass
  @loop
  #quartz_ore
  i0: setblock @loop netherrack
  scoreboard players test LOOP loopCounter 0 0
  ?: setblock @done redstone_block
  scoreboard players test LOOP loopCounter 1 *
  ?: scoreboard players remove LOOP loopCounter 1
  ?: setblock @body redstone_block
  #glass
  @body
  #quartz_ore
  i0: setblock @body netherrack
  !main()
  setblock @loop redstone_block
  #glass
  @done
  #quartz_ore
  i0: setblock @done netherrack

// i0:
// say before loop
// !loop(5):
//   say in loop
// say after loop

say bwahaha, that's a comment

`)
console.dir(stack)
stack = p.handleLabels(stack)
console.dir(stack)
console.log(CBU.summonStack(stack))
<|endoftext|>


<commit_before>/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global $, define, brackets */

define(function (require, exports, module)
{
    "use strict";

	var _require = require;
	var _exports = exports;
	var _module = module;

	var AppInit				= brackets.getModule("utils/AppInit");
	var ExtensionUtils		= brackets.getModule("utils/ExtensionUtils");
    var NodeDomain			= brackets.getModule("utils/NodeDomain");
	var PanelManager		= brackets.getModule("view/PanelManager");
	var projectManager		= null;
    var tasksDomain			= null;
	var gulpPanel			= null;
	var gulpPanelManager	= null;
	var gulpTaskButtons		= [];

	// Load stylesheet
	ExtensionUtils.loadStyleSheet(module, "brackets-tasks.css");

	AppInit.appReady(function ()
	{
		console.log("Gulp tasks app ready");
		projectManager = brackets.getModule('project/ProjectManager');
		tasksDomain = new NodeDomain("tasks", ExtensionUtils.getModulePath(_module, "node/Tasks"));

		tasksDomain.exec("setProjectRoot", projectManager.getProjectRoot().fullPath)
		.done(function()
		{
			getGulpTasks();
		})
		.fail(function(err)
		{
			console.error("Error setting project root folder", err);
		});

		$(tasksDomain).on("start", function(event, task)
		{
			$("#gulp-panel .task[data-task='"+task+"']")
			.addClass("active");
		});

		$(tasksDomain).on("finish", function(event, task)
		{
			$("#gulp-panel .task[data-task='"+task+"']")
			.delay(500)
			.queue(function(nxt) { $(this).removeClass("active").addClass("finished"); nxt(); })
			.delay(1000)
			.queue(function(nxt) { $(this).removeClass("finished"); nxt(); });
		});

    });

	function getGulpTasks()
	{
		tasksDomain.exec("getGulpTaskList")
		.done(function(tasks)
		{
			console.log(tasks);
			gulpPanel = $("

");
			for(var task in tasks)
			{
				var taskButton = $("
" + task + "
");
				taskButton.click(gulpTaskClickHandler);
				console.log(taskButton);
				gulpPanel.append(taskButton);
				gulpTaskButtons.push(taskButton);
			}
			console.log(gulpPanel);
			gulpPanelManager = PanelManager.createBottomPanel("Gulp tasks", gulpPanel, 23 /* height */);
			gulpPanelManager.show();
		})
		.fail(function(err)
		{
			console.error("Error loading gulp tasks", err);
		});
	}

	function gulpTaskClickHandler()
	{
		tasksDomain.exec("runGulpTask", $(this).data('task'));
	}

});

<commit_msg>Re-init on project open event<commit_after>/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global $, define, brackets */

define(function (require, exports, module)
{
    "use strict";

	var _require = require;
	var _exports = exports;
	var _module = module;

	var AppInit				= brackets.getModule("utils/AppInit");
	var ExtensionUtils		= brackets.getModule("utils/ExtensionUtils");
    var NodeDomain			= brackets.getModule("utils/NodeDomain");
	var PanelManager		= brackets.getModule("view/PanelManager");
	var ProjectManager		= brackets.getModule("project/ProjectManager");
    var tasksDomain			= null;
	var gulpPanel			= $("

");
	var gulpPanelManager	= null;
	var gulpTaskButtons		= [];

	// Load stylesheet
	ExtensionUtils.loadStyleSheet(module, "brackets-tasks.css");

	AppInit.appReady(initTasks);

	function initTasks()
	{
		console.log("Gulp tasks app ready");
		tasksDomain = new NodeDomain("tasks", ExtensionUtils.getModulePath(_module, "node/Tasks"));

		tasksDomain.exec("setProjectRoot", ProjectManager.getProjectRoot().fullPath)
		.done(function() { getGulpTasks(); })
		.fail(function(err) { console.error("Error setting project root folder", err); });

		$(tasksDomain).on("start", function(event, task)
		{
			$("#gulp-panel .task[data-task='"+task+"']")
			.addClass("active");
		});

		$(tasksDomain).on("finish", function(event, task)
		{
			$("#gulp-panel .task[data-task='"+task+"']")
			.delay(500)
			.queue(function(nxt) { $(this).removeClass("active").addClass("finished"); nxt(); })
			.delay(1000)
			.queue(function(nxt) { $(this).removeClass("finished"); nxt(); });
		});
	}

	function getGulpTasks()
	{
		tasksDomain.exec("getGulpTaskList")
		.done(function(tasks)
		{
			console.log(tasks);
			for(var task in tasks)
			{
				var taskButton = $("
" + task + "
");
				taskButton.click(gulpTaskClickHandler);
				gulpPanel.append(taskButton);
				gulpTaskButtons.push(taskButton);
			}

			if(gulpPanelManager === null)
			{
				console.log("CREATE TASKS PANEL");
				gulpPanelManager = PanelManager.createBottomPanel("Gulp tasks", gulpPanel, 23 /* height */);
			}

			gulpPanelManager.show();

		})
		.fail(function(err) { console.error("Error loading gulp tasks", err); });
	}

	function gulpTaskClickHandler()
	{
		tasksDomain.exec("runGulpTask", $(this).data('task'));
	}

	$(ProjectManager).on("projectOpen", function()
	{
		// gulpPanelManager.hide();
		for(var i = 0; i < gulpTaskButtons.length; i++)
		{
			gulpTaskButtons[i].unbind("click");
			gulpTaskButtons[i].remove();
		}
		initTasks();
	});

});

<|endoftext|>


<commit_before>'use strict';
let mongoose = require('mongoose');
const TelegramBot = require('node-telegram-bot-api');

const config = require('./config.js');
const userAct = require('./src/usersActivity');
const models = require('./models');
const commands = require('./commands');

const bot = new TelegramBot(config.token, {polling: true});
const calculateKeyboardOptions = require('./src/chatFunctions').calculateKeyboardOptions;

mongoose.Promise = global.Promise;
mongoose.connect(config.mongoConnectionString, {useMongoClient: true});

// Admin messages
bot.onText(/\/newCoupon (.+)/, (msg, match) => {
  commands.createCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/updateCoupon (.+)/, (msg, match) => {
  commands.updateCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/deleteCoupon (.+)/, (msg, match) => {
  commands.deleteCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/users/, (msg, match) => {
  commands.listUsers(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/notify/, (msg, match) => {
  commands.notifyChanges(msg, match, function(users, msgEs, msgEn){
    // console.log('Llega');
    users.forEach(function(user) {
        bot.sendMessage(user.chatId, msgEs, config.basicOptions);
    }, this);

    bot.sendMessage(msg.chat.id, 'Enviadas ' +users.length + ' notificaciones.');
  });
});
bot.onText(/\/test/, (msg, match) => {
  commands.test(bot, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});

// Basic bot messages
bot.onText(/\/list/, (msg, match) => {
  let response = '';
  commands.getCoupons(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'list');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});
bot.onText(/\/subscribe/, (msg, match) => {
  let response = '';
  commands.subscribe(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'subscribe');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});
bot.onText(/\/unsubscribe/, (msg, match) => {
  let response = '';
  commands.unsubscribe(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'unsubscribe');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});

// Help and welcome
bot.onText(/\/start/, (msg, match) => {
  help(msg);
});
bot.onText(/\/help/, (msg, match) => {
  help(msg);
});
bot.onText(/\/about/, (msg, match) => {
  calculateKeyboardOptions(msg, 'about')
    .then((options) => {
      bot.sendMessage(msg.chat.id, 'En desarrollo: crawler para detectar nuevos cupones automaticamente \n Siguiente: Añadir otras webs y dar la opcion de subscribirse a una o varias.', options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err+'');
    });
});

function help(msg){
  let response = config.helpMessage;

  userAct.userUpsert(msg)
  .then((user) => {
    if(user === null){
      bot.sendMessage(config.adminId, 'New user!');
    }
    return calculateKeyboardOptions(msg, 'help');
  })
  .then((options) => {
    bot.sendMessage(msg.chat.id, response, options);
  })
  .catch( err => {
    bot.sendMessage(msg.chat.id, err+'');
  });
}

setInterval(() => {
  bot.sendMessage(config.adminId, 'No te olvides de mi!');
}, 1000*60*60*24);

setInterval(() => {
  commands.syncCodes(bot);
}, 1000*60*60);

setInterval(() => {
  commands.syncVoucher(bot);
}, 1000*5);<commit_msg>modified times<commit_after>'use strict';
let mongoose = require('mongoose');
const TelegramBot = require('node-telegram-bot-api');

const config = require('./config.js');
const userAct = require('./src/usersActivity');
const models = require('./models');
const commands = require('./commands');

const bot = new TelegramBot(config.token, {polling: true});
const calculateKeyboardOptions = require('./src/chatFunctions').calculateKeyboardOptions;

mongoose.Promise = global.Promise;
mongoose.connect(config.mongoConnectionString, {useMongoClient: true});

// Admin messages
bot.onText(/\/newCoupon (.+)/, (msg, match) => {
  commands.createCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/updateCoupon (.+)/, (msg, match) => {
  commands.updateCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/deleteCoupon (.+)/, (msg, match) => {
  commands.deleteCoupon(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/users/, (msg, match) => {
  commands.listUsers(msg, match, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});
bot.onText(/\/notify/, (msg, match) => {
  commands.notifyChanges(msg, match, function(users, msgEs, msgEn){
    // console.log('Llega');
    users.forEach(function(user) {
        bot.sendMessage(user.chatId, msgEs, config.basicOptions);
    }, this);

    bot.sendMessage(msg.chat.id, 'Enviadas ' +users.length + ' notificaciones.');
  });
});
bot.onText(/\/test/, (msg, match) => {
  commands.test(bot, function(response){
    bot.sendMessage(msg.chat.id, response, config.basicOptions);
  });
});

// Basic bot messages
bot.onText(/\/list/, (msg, match) => {
  let response = '';
  commands.getCoupons(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'list');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});
bot.onText(/\/subscribe/, (msg, match) => {
  let response = '';
  commands.subscribe(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'subscribe');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});
bot.onText(/\/unsubscribe/, (msg, match) => {
  let response = '';
  commands.unsubscribe(msg, match)
    .then((res) => {
      response = res;
      return calculateKeyboardOptions(msg, 'unsubscribe');
    })
    .then((options) => {
      bot.sendMessage(msg.chat.id, response, options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err);
    });
});

// Help and welcome
bot.onText(/\/start/, (msg, match) => {
  help(msg);
});
bot.onText(/\/help/, (msg, match) => {
  help(msg);
});
bot.onText(/\/about/, (msg, match) => {
  calculateKeyboardOptions(msg, 'about')
    .then((options) => {
      bot.sendMessage(msg.chat.id, 'En desarrollo: crawler para detectar nuevos cupones automaticamente \n Siguiente: Añadir otras webs y dar la opcion de subscribirse a una o varias.', options);
    })
    .catch( err => {
      bot.sendMessage(msg.chat.id, err+'');
    });
});

function help(msg){
  let response = config.helpMessage;

  userAct.userUpsert(msg)
  .then((user) => {
    if(user === null){
      bot.sendMessage(config.adminId, 'New user!');
    }
    return calculateKeyboardOptions(msg, 'help');
  })
  .then((options) => {
    bot.sendMessage(msg.chat.id, response, options);
  })
  .catch( err => {
    bot.sendMessage(msg.chat.id, err+'');
  });
}

commands.syncCodes(bot);
setInterval(() => {
  commands.syncCodes(bot);
}, 1000*60*60*2);

commands.syncVoucher(bot);
setInterval(() => {
  commands.syncVoucher(bot);
}, 1000*60*60*2);<|endoftext|>


<commit_before>var app = require('app')
var BrowserWindow = require('browser-window')
var crashReporter = require('crash-reporter')
var Menu = require('menu')
var ipc = require('ipc')
var dialog = require('dialog')
var fs = require('fs')
var path = require('path')

var darwinTemplate = require('./darwin-menu.js')
var otherTemplate = require('./other-menu.js')

var emptyData = require('./empty-data.json')

var mainWindow = null
var menu = null

crashReporter.start()

app.on('window-all-closed', function appQuit () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('ready', function appReady () {
  mainWindow = new BrowserWindow({width: 800, height: 900, title: 'Git-it'})
  mainWindow.loadUrl('file://' + __dirname + '/index.html')

  var userDataPath = path.join(app.getPath('userData'), 'user-data.json')

  fs.exists(userDataPath, function (exists) {
    if (!exists) {
      fs.writeFile(userDataPath, JSON.stringify(emptyData, null, ' '), function (err) {
        if (err) return console.log(err)
      })
    }
  })

  ipc.on('getUserDataPath', function (event) {
    event.sender.send('haveUserDataPath', userDataPath)
  })

  ipc.on('open-file-dialog', function (event) {
    var files = dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory' ]})
    if (files) {
      event.sender.send('selected-directory', files)
    }
  })

  if (process.platform === 'darwin') {
    menu = Menu.buildFromTemplate(darwinTemplate(app, mainWindow))
    Menu.setApplicationMenu(menu)
  } else {
    menu = Menu.buildFromTemplate(otherTemplate(mainWindow))
    mainWindow.setMenu(menu)
  }

  mainWindow.on('closed', function winClosed () {
    mainWindow = null
  })
})
<commit_msg>Change to sync ipc<commit_after>var app = require('app')
var BrowserWindow = require('browser-window')
var crashReporter = require('crash-reporter')
var Menu = require('menu')
var ipc = require('ipc')
var dialog = require('dialog')
var fs = require('fs')
var path = require('path')

var darwinTemplate = require('./darwin-menu.js')
var otherTemplate = require('./other-menu.js')

var emptyData = require('./empty-data.json')

var mainWindow = null
var menu = null

crashReporter.start()

app.on('window-all-closed', function appQuit () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('ready', function appReady () {
  mainWindow = new BrowserWindow({width: 800, height: 900, title: 'Git-it'})
  mainWindow.loadUrl('file://' + __dirname + '/index.html')

  var userDataPath = path.join(app.getPath('userData'), 'user-data.json')

  fs.exists(userDataPath, function (exists) {
    if (!exists) {
      fs.writeFile(userDataPath, JSON.stringify(emptyData, null, ' '), function (err) {
        if (err) return console.log(err)
      })
    }
  })

  ipc.on('getUserDataPath', function (event) {
    event.returnValue = userDataPath
  })

  ipc.on('open-file-dialog', function (event) {
    var files = dialog.showOpenDialog({ properties: [ 'openFile', 'openDirectory' ]})
    if (files) {
      event.sender.send('selected-directory', files)
    }
  })

  if (process.platform === 'darwin') {
    menu = Menu.buildFromTemplate(darwinTemplate(app, mainWindow))
    Menu.setApplicationMenu(menu)
  } else {
    menu = Menu.buildFromTemplate(otherTemplate(mainWindow))
    mainWindow.setMenu(menu)
  }

  mainWindow.on('closed', function winClosed () {
    mainWindow = null
  })
})
<|endoftext|>


<commit_before>var app = require('app'),
    BrowserWindow = require('browser-window'),
    Menu = require('menu'),
    noble = require('noble'),
    metadata = require('./metadata.js'),
    urldecode = require('./urldecode.js'),
    mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  app.quit();
});

noble.on('scanStart', function() {
  console.log('Scan started...');
  mainWindow.webContents.send('status', 'Scanning for Physical Web beacons', true);
});

noble.on('scanStop', function() {
  console.log('Scan stopped...');
  mainWindow.webContents.send('status', 'Scanning stopped. Press CMD + S to restart scan.');
});

noble.on('discover', function(peripheral) {
  var serviceData = peripheral.advertisement.serviceData;
  if (serviceData && serviceData.length) {
    var objects = [];
    for (var i in serviceData) {

      // check if Eddystone-URL
      if (serviceData[i].data.toString('hex').substr(0,2) === '10') {
        var url = urldecode(serviceData[i].data.toString('hex'));
        objects.push({url: url});
      }
    }
    if (objects.length) {
      metadata(objects, function(message) {
        mainWindow.webContents.send('url', message);
      });
    }
  }
});

app.on('ready', function() {

  var menuTemplate = [
    {
      label: 'Physical Web Scan',
      submenu: [
        {
          label: 'Scan for beacons',
          accelerator: 'Command+S',
          click: function() {
            if (noble.state === 'poweredOn') {
              noble.startScanning(['feaa']);
            } else {
              mainWindow.webContents.send('status', 'Bluetooth not ready or turned on. Press CMD + S to retry.');
            }
          }
        },
        {
          label: 'Stop scanning',
          accelerator: 'Command+T',
          click: function() {
            noble.stopScanning();
          }
        },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        }
      ]
    }
  ];

  menu = Menu.buildFromTemplate(menuTemplate);
  Menu.setApplicationMenu(menu);

  mainWindow = new BrowserWindow({width: 480, height: 600});

  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  mainWindow.webContents.on('did-finish-load', function() {
    console.log(noble.state);
    if (noble.state === 'poweredOn') {
      noble.startScanning(['feaa']);
    }

  });

});<commit_msg>add counter badge for found beacons when window not focused<commit_after>var app = require('app'),
    BrowserWindow = require('browser-window'),
    Menu = require('menu'),
    noble = require('noble'),
    metadata = require('./metadata.js'),
    urldecode = require('./urldecode.js'),
    mainWindow = null,
    counter = 0;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  app.quit();
});

noble.on('scanStart', function() {
  console.log('Scan started...');
  mainWindow.webContents.send('status', 'Scanning for Physical Web beacons', true);
  counter = 0;
  app.dock.setBadge('');
});

noble.on('scanStop', function() {
  console.log('Scan stopped...');
  mainWindow.webContents.send('status', 'Scanning stopped. Press CMD + S to restart scan.');
});

noble.on('discover', function(peripheral) {
  var serviceData = peripheral.advertisement.serviceData;
  if (serviceData && serviceData.length) {
    var objects = [];
    for (var i in serviceData) {

      // check if Eddystone-URL
      if (serviceData[i].data.toString('hex').substr(0,2) === '10') {
        var url = urldecode(serviceData[i].data.toString('hex'));
        objects.push({url: url});
      }
    }
    if (objects.length) {
      metadata(objects, function(message) {
        mainWindow.webContents.send('url', message);
        if (!mainWindow.isFocused()) {
          counter ++;
          app.dock.setBadge('' + counter + '');
        }
      });
    }
  }
});

app.on('ready', function() {

  var menuTemplate = [
    {
      label: 'Physical Web Scan',
      submenu: [
        {
          label: 'Scan for beacons',
          accelerator: 'Command+S',
          click: function() {
            if (noble.state === 'poweredOn') {
              noble.startScanning(['feaa']);
            } else {
              mainWindow.webContents.send('status', 'Bluetooth not ready or turned on. Press CMD + S to retry.');
            }
          }
        },
        {
          label: 'Stop scanning',
          accelerator: 'Command+T',
          click: function() {
            noble.stopScanning();
          }
        },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        }
      ]
    }
  ];

  menu = Menu.buildFromTemplate(menuTemplate);
  Menu.setApplicationMenu(menu);

  mainWindow = new BrowserWindow({width: 480, height: 600});

  mainWindow.loadUrl('file://' + __dirname + '/index.html');

  mainWindow.on('closed', function() {
    mainWindow = null;
  });

  mainWindow.webContents.on('did-finish-load', function() {
    console.log(noble.state);
    if (noble.state === 'poweredOn') {
      noble.startScanning(['feaa']);
    }

  });

});

app.on('browser-window-focus', function() {
  counter = 0;
  app.dock.setBadge('');
});<|endoftext|>


<commit_before>import h from 'virtual-dom/h';
import diff from 'virtual-dom/diff';
import patch from 'virtual-dom/patch';

import moment from 'moment';
import 'moment-duration-format';

import createElement from 'virtual-dom/create-element';
import Delegator from 'dom-delegator';
import EventEmitter from 'event-emitter';

// hh:mm:ss
var hh,mm,ss;

hh = {start: 0, end: 2, increment: 3600};
mm = {start: 3, end: 5, increment: 60};
ss = {start: 6, end: 8, increment: 1};

hh.next = mm;
mm.prev = hh;
mm.next = ss;
ss.prev = mm;

var formatSelectionPoints = {
    'hh:mm:ss': [
        hh, //0
        hh, //1
        hh, //2
        mm, //3
        mm, //4
        mm, //5
        ss, //6
        ss, //7
        ss  //8
    ]
};

const MAX = 3600 * 99 + 60 * 59 + 1 * 59;

const KEYLEFT = 37;
const KEYUP = 38;
const KEYRIGHT = 39;
const KEYDOWN = 40;

export default class Selection {
    constructor() {
        this.durationFormat = 'hh:mm:ss';
        this.value = 0;
        this.data = undefined;
    }

    formatDuration(duration) {
        return moment.duration(duration, 'seconds').format(this.durationFormat, {trim: false});
    }

    setSelection(input, start, end) {
        setTimeout(() => {
            input.setSelectionRange(start, end);
        }, 0);
    }

    render() {
        return h('div.timepoint-selection',
            h('input', {
                'type': 'text',
                'value': this.formatDuration(this.value),
                'ev-click': (e) => {
                    e.preventDefault();

                    let input = e.target;
                    let data = formatSelectionPoints[this.durationFormat][input.selectionStart];

                    this.setSelection(e.target, data.start, data.end);
                    this.data = data;
                },
                'ev-keydown': (e) => {
                    e.preventDefault();
                },
                'ev-keyup': (e) => {
                    e.preventDefault();

                    let input = e.target;

                    console.log(e);

                    switch(e.which) {
                        case KEYUP:
                            if ((this.value + this.data.increment) > MAX) {
                                this.value = this.value - MAX + this.data.increment - 1;
                            }
                            else {
                                this.value += this.data.increment;
                            }
                            input.value = this.formatDuration(this.value);
                            break;
                        case KEYDOWN:

                            if ((this.value - this.data.increment) < 0) {
                                this.value = this.value + MAX - this.data.increment + 1;
                            }
                            else {
                                this.value -= this.data.increment;
                            }
                            input.value = this.formatDuration(this.value);
                            break;
                        case KEYLEFT:
                            this.data = this.data.prev;
                            break;
                        case KEYRIGHT:
                            this.data = this.data.next;
                            break;
                    }

                    this.setSelection(input, this.data.start, this.data.end);
                }
            })
        );
    }
}

export default class SelectionRenderer {
    constructor(selection, el) {
        this.tree = selection.render();
        this.rootNode = createElement(this.tree);
        this.selection = selection;

        el.appendChild(this.rootNode);
    }

    draw() {
        window.requestAnimationFrame(() => {
            let newTree = this.selection.render();
            let patches = diff(this.tree, newTree);
            this.rootNode = patch(this.rootNode, patches);
            this.tree = newTree;
        });
    }

}

export function init(options={}, ee=EventEmitter(), delegator=Delegator()) {
    if (options.container === undefined) {
        throw new Error("DOM element container must be given.");
    }

    let selection = new Selection();
    let renderer = new SelectionRenderer(selection, options.container);
}<commit_msg>Track with units/index instead.<commit_after>import h from 'virtual-dom/h';
import diff from 'virtual-dom/diff';
import patch from 'virtual-dom/patch';

import moment from 'moment';
import 'moment-duration-format';

import createElement from 'virtual-dom/create-element';
import Delegator from 'dom-delegator';
import EventEmitter from 'event-emitter';

let units = [
    {start: 0, end: 1, increment: 36000},
    {start: 1, end: 2, increment: 3600},
    {start: 3, end: 4, increment: 600},
    {start: 4, end: 5, increment: 60},
    {start: 6, end: 7, increment: 10},
    {start: 7, end: 8, increment: 1}
];

let formatSelectionPoints = {
    'hh:mm:ss': [0, 1, 1, 2, 3, 3, 4, 5, 5]
};

const MAX = 3600 * 99 + 60 * 59 + 1 * 59;

const KEYLEFT = 37;
const KEYUP = 38;
const KEYRIGHT = 39;
const KEYDOWN = 40;

export default class Selection {
    constructor() {
        this.durationFormat = 'hh:mm:ss';
        this.value = 0;
        this.index = undefined;
    }

    formatDuration(duration) {
        return moment.duration(duration, 'seconds').format(this.durationFormat, {trim: false});
    }

    setSelection(input) {
        setTimeout(() => {
            let data = units[this.index];
            input.setSelectionRange(data.start, data.end);
        }, 0);
    }

    render() {
        return h('div.timepoint-selection',
            h('input', {
                'type': 'text',
                'value': this.formatDuration(this.value),
                'ev-click': (e) => {
                    e.preventDefault();

                    let input = e.target;
                    let index = formatSelectionPoints[this.durationFormat][input.selectionStart];
                    let data = units[index];

                    this.setSelection(e.target, data.start, data.end);
                    this.index = index;
                },
                'ev-keydown': (e) => {
                    e.preventDefault();
                },
                'ev-keyup': (e) => {
                    e.preventDefault();

                    let input = e.target;
                    let data = units[this.index]

                    switch(e.which) {
                        case KEYUP:
                            if ((this.value + data.increment) > MAX) {
                                this.value = this.value - MAX + data.increment - 1;
                            }
                            else {
                                this.value += data.increment;
                            }
                            input.value = this.formatDuration(this.value);
                            break;
                        case KEYDOWN:

                            if ((this.value - data.increment) < 0) {
                                this.value = this.value + MAX - data.increment + 1;
                            }
                            else {
                                this.value -= data.increment;
                            }
                            input.value = this.formatDuration(this.value);
                            break;
                        case KEYLEFT:
                            this.index -= 1;
                            break;
                        case KEYRIGHT:
                            this.index += 1;
                            break;
                    }

                    this.setSelection(input);
                }
            })
        );
    }
}

export default class SelectionRenderer {
    constructor(selection, el) {
        this.tree = selection.render();
        this.rootNode = createElement(this.tree);
        this.selection = selection;

        el.appendChild(this.rootNode);
    }

    draw() {
        window.requestAnimationFrame(() => {
            let newTree = this.selection.render();
            let patches = diff(this.tree, newTree);
            this.rootNode = patch(this.rootNode, patches);
            this.tree = newTree;
        });
    }

}

export function init(options={}, ee=EventEmitter(), delegator=Delegator()) {
    if (options.container === undefined) {
        throw new Error("DOM element container must be given.");
    }

    let selection = new Selection();
    let renderer = new SelectionRenderer(selection, options.container);
}<|endoftext|>


<commit_before>var app = {};

$(function() { //when DOM is ready...
	console.log("this is running");
	app.users = new UserCollection([
		{username:'Joseph'},
		{username:'Nathaniel'},
		{username:'Adam'}
	]);

	// app.tasks = new TaskCollection([
	// 	// test data here
	// ]);

	app.gui = new GUI(app.users,
						app.tasks,
						'#app');// selector of main div
} //end of inner function
); //end of parameter
//(); //call $
// console.log($);
<commit_msg>changed taskcollection to issuecollection<commit_after>var app = {};

$(function() { //when DOM is ready...
	console.log("this is running");
	app.users = new UserCollection([
		{username:'Joseph'},
		{username:'Nathaniel'},
		{username:'Adam'}
	]);

	app.tasks = new IssueCollection([
		// test data here
	]);

	app.gui = new GUI(app.users,
						app.tasks,
						'#app');// selector of main div
} //end of inner function
); //end of parameter
//(); //call $
// console.log($);
<|endoftext|>


<commit_before>Logs = new Meteor.Collection("logs");

if (Meteor.isClient) {

  Meteor.startup(function () {
    autoresize();
    $( "#text" ).focus();
  });

  ///// Demo /////
  // Run this when visitor is not signed in.
  if (!Meteor.userId()) {
    // Create a local collection called Demo to store the demo messages.
    var Demo = new Meteor.Collection(null);
    Session.set("demoContinue", true);
    demoLogs = ["I'm [open-sourced](http://github.com/lzl/log). Running on Meteor " + Meteor.release,
                "I'm the anti-social version of Twitter for introverts.\n\n这里只允许你自言自语，不被他人打扰，更不被他人偷窥。",
                "This is a new log. You can create one by yourself.\n\n你现在就可以试着提交一条日志。快，我等着你。"];
    demoInsertTimes = 0;

    function demoInsert () {
      if (demoInsertTimes < demoLogs.length) {
        Demo.insert({
          text: demoLogs[demoInsertTimes],
          created_at: new Date()
        });
        demoInsertTimes++;
      } else {
        console.log("You should signup by click the [Sign in] button.");
        Meteor.clearInterval(timeout);
      }
    }
    // Insert the message above every 2 seconds.
    timeout = Meteor.setInterval(demoInsert, 2000);

    Template.paper.demoLogs = function () {
      return Demo.find({}, {sort: {created_at: -1}});
    };
  }

  ///// Prototype & Undo/////
  Template.paper.userLogs = function () {
    // return Logs.find({}, {sort: {created_at: -1}});
    // I commented the old code above. It is
    // simple and elegant. But not suitable
    // with the new situation.

    // The new code is handcrafted by the tips
    // from Stack Overflow.
    var userLogs = Logs.find().fetch();
    var undoLogs = Undos.find().fetch();
    var allLogs = userLogs.concat(undoLogs);
    return _.sortBy(allLogs, function(doc) {
      return -doc.created_at;
    });
  };

  Template.paper.events({
    'submit form': function (e, tmpl) {
      e.preventDefault();
      var val = tmpl.find('#text').value;

      if (val && Meteor.userId()) {
        Logs.insert({
          text: val,
          user_id: Meteor.userId(),
          created_at: new Date()
        });
      }

      if (val && !Meteor.userId()) {
        Demo.insert({
          text: val,
          created_at: new Date()
        });
        if (Demo.find().count() > demoLogs.length && Session.get("demoContinue")) {
          (function() {
            demoLogs = ["You just got it. 你成功了。",
                        "Congratulations! 恭喜你！",
                        "Signup for free, then you can log and search your life.\n\n你可以选择免费注册我们的服务，从而储存并搜索今后提交的所有日志。\n\n![Sign up now](/signup.gif)"];
            demoInsertTimes = 0;
            timeout = Meteor.setInterval(demoInsert, 2000);
          })();
          Session.set("demoContinue", false);
        }
      }

      Session.set('searchKeyword', undefined);
      window.localStorage.autosave = "";
      tmpl.find('form').reset();
      tmpl.find('#text').focus();
      Session.set('showSearch', false);
      Session.set('showPreview', false);
      autoresize();
    }
  });

  ///// Delete & Undo/////
  // Create a local collection called Trash.
  var Trash = new Meteor.Collection(null);
  var Undos = new Meteor.Collection(null);

  Template.log.showRemove = function () {
    return this.user_id === Meteor.userId();
  };

  Template.log.events({
    'click .eraser': function (e, tmpl) {
      // Copy that log into Trash.
      Trash.insert(this);
      // Insert the Undo button with that
      // log's id information to retrieve
      // that log later.
      Undos.insert({
        _id: this._id,
        text: "[Undo](#undo)",
        created_at: this.created_at
      });
      // Remove that log from server.
      Logs.remove(this._id);
    },
    'click [href="#undo"]': function () {
      // Find that log with the id information
      // then save it into a variable.
      var undoLog = Trash.find({_id: this._id}).fetch()[0];
      // Remove that Undo button from client.
      Undos.remove(this._id);
      // Remove the cloned log from client.
      Trash.remove(this._id);
      // Insert that variable into Logs collection
      // on the server. That variable contains all
      // information about that removed log, including
      // the timestamp when it was created.
      Logs.insert(undoLog);
    }
  });

  ///// Date & Time /////
  Template.log.dateTime = function () {
    return moment(this.created_at).format("YYYY.MM.DD HH:mm:ss");
  };

  ///// Load more /////
  // Set the 'loadMore' session to 50 by default.
  Session.set('loadMore', 50);

  // Only show the 'Load more' button if the
  // number of items in the Logs collection
  // larger than 49;
  Template.paper.showMore = function () {
    return Logs.find().count() > 49;
  };

  Template.paper.disabledLoadMore = function () {
    var loadMore = Session.get('loadMore');
    var loaded = Logs.find().count();
    if (loadMore > loaded) {
      return disabled="disabled";
    }
  };

  Template.paper.disabledSearchMore = function () {
    var searchMore = Session.get('searchMore');
    var text = Session.get('searchKeyword');
    var query = new RegExp(text, 'i');
    var searched = Logs.find({text: query}).count();
    if (searchMore > searched) {
      return disabled="disabled";
    }
  };

  // Click the 'Load more' button to show 50 more logs.
  Template.paper.events({
    'click .load-more': function (e, tmpl) {
      e.preventDefault();
      Session.set('loadMore', Session.get('loadMore') + 50);
    }
  });

  // If the 'loadMore' session changed. It will
  // be rerunning, aka, subscribe userLogs again.
  Deps.autorun(function () {
    Meteor.subscribe('userLogs', Session.get('loadMore'));
  });

  ///// Search /////
  // Set the showSearch session to false by default.
  Session.set('showSearch', false);
  Session.set('searchMore', 10);

  Template.paper.showSearch = function () {
    return Session.get('showSearch');
  };

  Template.paper.searchedLogs = function () {
    var text = Session.get('searchKeyword');
    // RegExp is a big helper in this search step.
    // The RegExp constructor creates a regular
    // expression object for matching text with a pattern.
    // The 'i' flag means ignore case.
    var query = new RegExp(text, 'i');
    return Logs.find({text: query}, {sort: {created_at: -1}});
  };

  Template.paper.showMoreSearch = function () {
    var text = Session.get('searchKeyword');
    var query = new RegExp(text, 'i');
    return Logs.find({text: query}).count() > 9;
  };

  Template.paper.events({
    'keyup, #text': function (e, tmpl) {
      e.preventDefault();
      var val = tmpl.find('#text').value;
      Session.set('searchKeyword', val);
      window.localStorage.autosave = val;
      if (val && Meteor.userId()) {
        Session.set('showSearch', true);
        Session.set('showPreview',true);
        Session.set('textPreview', val);
      } else {
        Session.set('showSearch', false);
        Session.set('searchMore', 10);
        Session.set('showPreview', false);
      }
      autoresize();
    },
    'click .search-more': function (e, tmpl) {
      e.preventDefault();
      Session.set('searchMore', Session.get('searchMore') + 20);
    }
  });

  Deps.autorun(function () {
    Meteor.subscribe('searchedLogs', Session.get('searchKeyword'), Session.get('searchMore'));
  });

  ///// Preview /////
  Template.paper.showPreview = function () {
    return Session.get('showPreview');
  };

  Template.paper.textPreview = function () {
    return Session.get('textPreview');
  };

  ///// Autosave /////
  Template.paper.autosave = function () {
    return Session.get('searchKeyword') || window.localStorage.autosave;
  };

  ///// Autoresize /////
  // thx http://phaistonian.pblogs.gr/expanding-textareas-the-easy-and-clean-way.html
  function autoresize () {
    var t = document.getElementsByTagName('textarea')[0];
    var offset = !window.opera ? (t.offsetHeight - t.clientHeight) : (t.offsetHeight + parseInt(window.getComputedStyle(t, null).getPropertyValue('border-top-width')));
    t.style.height = 'auto';
    t.style.height = (t.scrollHeight  + offset + 27) + 'px';
  }
}

if (Meteor.isServer) {

  ///// Security  && Search/////
  // Limit the number of items while publish.
  // I added the sort back, because I wish the app
  // publish the latest items with 'limit' enabled.
  Meteor.publish("userLogs", function (limit) {
    return Logs.find({user_id: this.userId}, {sort: {created_at: -1}, limit: limit})
  });

  Meteor.publish("searchedLogs", function (text, limit) {
    var query = new RegExp(text, 'i');
    return Logs.find({user_id: this.userId, text: query}, {sort: {created_at: -1}, limit: limit});
  });

  Logs.allow({
    insert: function (userId, doc) {
      return doc.user_id === userId;
    },
    remove: function (userId, doc) {
      return doc.user_id === userId;
    }
  });
}
<commit_msg>Show help text while hover the Undo link.<commit_after>Logs = new Meteor.Collection("logs");

if (Meteor.isClient) {

  Meteor.startup(function () {
    autoresize();
    $( "#text" ).focus();
  });

  ///// Demo /////
  // Run this when visitor is not signed in.
  if (!Meteor.userId()) {
    // Create a local collection called Demo to store the demo messages.
    var Demo = new Meteor.Collection(null);
    Session.set("demoContinue", true);
    demoLogs = ["I'm [open-sourced](http://github.com/lzl/log). Running on Meteor " + Meteor.release,
                "I'm the anti-social version of Twitter for introverts.\n\n这里只允许你自言自语，不被他人打扰，更不被他人偷窥。",
                "This is a new log. You can create one by yourself.\n\n你现在就可以试着提交一条日志。快，我等着你。"];
    demoInsertTimes = 0;

    function demoInsert () {
      if (demoInsertTimes < demoLogs.length) {
        Demo.insert({
          text: demoLogs[demoInsertTimes],
          created_at: new Date()
        });
        demoInsertTimes++;
      } else {
        console.log("You should signup by click the [Sign in] button.");
        Meteor.clearInterval(timeout);
      }
    }
    // Insert the message above every 2 seconds.
    timeout = Meteor.setInterval(demoInsert, 2000);

    Template.paper.demoLogs = function () {
      return Demo.find({}, {sort: {created_at: -1}});
    };
  }

  ///// Prototype & Undo/////
  Template.paper.userLogs = function () {
    // return Logs.find({}, {sort: {created_at: -1}});
    // I commented the old code above. It is
    // simple and elegant. But not suitable
    // with the new situation.

    // The new code is handcrafted by the tips
    // from Stack Overflow.
    var userLogs = Logs.find().fetch();
    var undoLogs = Undos.find().fetch();
    var allLogs = userLogs.concat(undoLogs);
    return _.sortBy(allLogs, function(doc) {
      return -doc.created_at;
    });
  };

  Template.paper.events({
    'submit form': function (e, tmpl) {
      e.preventDefault();
      var val = tmpl.find('#text').value;

      if (val && Meteor.userId()) {
        Logs.insert({
          text: val,
          user_id: Meteor.userId(),
          created_at: new Date()
        });
      }

      if (val && !Meteor.userId()) {
        Demo.insert({
          text: val,
          created_at: new Date()
        });
        if (Demo.find().count() > demoLogs.length && Session.get("demoContinue")) {
          (function() {
            demoLogs = ["You just got it. 你成功了。",
                        "Congratulations! 恭喜你！",
                        "Signup for free, then you can log and search your life.\n\n你可以选择免费注册我们的服务，从而储存并搜索今后提交的所有日志。\n\n![Sign up now](/signup.gif)"];
            demoInsertTimes = 0;
            timeout = Meteor.setInterval(demoInsert, 2000);
          })();
          Session.set("demoContinue", false);
        }
      }

      Session.set('searchKeyword', undefined);
      window.localStorage.autosave = "";
      tmpl.find('form').reset();
      tmpl.find('#text').focus();
      Session.set('showSearch', false);
      Session.set('showPreview', false);
      autoresize();
    }
  });

  ///// Delete & Undo/////
  // Create a local collection called Trash.
  var Trash = new Meteor.Collection(null);
  var Undos = new Meteor.Collection(null);

  Template.log.showRemove = function () {
    return this.user_id === Meteor.userId();
  };

  Template.log.events({
    'click .eraser': function (e, tmpl) {
      // Copy that log into Trash.
      Trash.insert(this);
      // Insert the Undo button with that
      // log's id information to retrieve
      // that log later.
      Undos.insert({
        _id: this._id,
        text: "[Undo](#undo 'Move it back')",
        created_at: this.created_at
      });
      // Remove that log from server.
      Logs.remove(this._id);
    },
    'click [href="#undo"]': function () {
      // Find that log with the id information
      // then save it into a variable.
      var undoLog = Trash.find({_id: this._id}).fetch()[0];
      // Remove that Undo button from client.
      Undos.remove(this._id);
      // Remove the cloned log from client.
      Trash.remove(this._id);
      // Insert that variable into Logs collection
      // on the server. That variable contains all
      // information about that removed log, including
      // the timestamp when it was created.
      Logs.insert(undoLog);
    }
  });

  ///// Date & Time /////
  Template.log.dateTime = function () {
    return moment(this.created_at).format("YYYY.MM.DD HH:mm:ss");
  };

  ///// Load more /////
  // Set the 'loadMore' session to 50 by default.
  Session.set('loadMore', 50);

  // Only show the 'Load more' button if the
  // number of items in the Logs collection
  // larger than 49;
  Template.paper.showMore = function () {
    return Logs.find().count() > 49;
  };

  Template.paper.disabledLoadMore = function () {
    var loadMore = Session.get('loadMore');
    var loaded = Logs.find().count();
    if (loadMore > loaded) {
      return disabled="disabled";
    }
  };

  Template.paper.disabledSearchMore = function () {
    var searchMore = Session.get('searchMore');
    var text = Session.get('searchKeyword');
    var query = new RegExp(text, 'i');
    var searched = Logs.find({text: query}).count();
    if (searchMore > searched) {
      return disabled="disabled";
    }
  };

  // Click the 'Load more' button to show 50 more logs.
  Template.paper.events({
    'click .load-more': function (e, tmpl) {
      e.preventDefault();
      Session.set('loadMore', Session.get('loadMore') + 50);
    }
  });

  // If the 'loadMore' session changed. It will
  // be rerunning, aka, subscribe userLogs again.
  Deps.autorun(function () {
    Meteor.subscribe('userLogs', Session.get('loadMore'));
  });

  ///// Search /////
  // Set the showSearch session to false by default.
  Session.set('showSearch', false);
  Session.set('searchMore', 10);

  Template.paper.showSearch = function () {
    return Session.get('showSearch');
  };

  Template.paper.searchedLogs = function () {
    var text = Session.get('searchKeyword');
    // RegExp is a big helper in this search step.
    // The RegExp constructor creates a regular
    // expression object for matching text with a pattern.
    // The 'i' flag means ignore case.
    var query = new RegExp(text, 'i');
    return Logs.find({text: query}, {sort: {created_at: -1}});
  };

  Template.paper.showMoreSearch = function () {
    var text = Session.get('searchKeyword');
    var query = new RegExp(text, 'i');
    return Logs.find({text: query}).count() > 9;
  };

  Template.paper.events({
    'keyup, #text': function (e, tmpl) {
      e.preventDefault();
      var val = tmpl.find('#text').value;
      Session.set('searchKeyword', val);
      window.localStorage.autosave = val;
      if (val && Meteor.userId()) {
        Session.set('showSearch', true);
        Session.set('showPreview',true);
        Session.set('textPreview', val);
      } else {
        Session.set('showSearch', false);
        Session.set('searchMore', 10);
        Session.set('showPreview', false);
      }
      autoresize();
    },
    'click .search-more': function (e, tmpl) {
      e.preventDefault();
      Session.set('searchMore', Session.get('searchMore') + 20);
    }
  });

  Deps.autorun(function () {
    Meteor.subscribe('searchedLogs', Session.get('searchKeyword'), Session.get('searchMore'));
  });

  ///// Preview /////
  Template.paper.showPreview = function () {
    return Session.get('showPreview');
  };

  Template.paper.textPreview = function () {
    return Session.get('textPreview');
  };

  ///// Autosave /////
  Template.paper.autosave = function () {
    return Session.get('searchKeyword') || window.localStorage.autosave;
  };

  ///// Autoresize /////
  // thx http://phaistonian.pblogs.gr/expanding-textareas-the-easy-and-clean-way.html
  function autoresize () {
    var t = document.getElementsByTagName('textarea')[0];
    var offset = !window.opera ? (t.offsetHeight - t.clientHeight) : (t.offsetHeight + parseInt(window.getComputedStyle(t, null).getPropertyValue('border-top-width')));
    t.style.height = 'auto';
    t.style.height = (t.scrollHeight  + offset + 27) + 'px';
  }
}

if (Meteor.isServer) {

  ///// Security  && Search/////
  // Limit the number of items while publish.
  // I added the sort back, because I wish the app
  // publish the latest items with 'limit' enabled.
  Meteor.publish("userLogs", function (limit) {
    return Logs.find({user_id: this.userId}, {sort: {created_at: -1}, limit: limit})
  });

  Meteor.publish("searchedLogs", function (text, limit) {
    var query = new RegExp(text, 'i');
    return Logs.find({user_id: this.userId, text: query}, {sort: {created_at: -1}, limit: limit});
  });

  Logs.allow({
    insert: function (userId, doc) {
      return doc.user_id === userId;
    },
    remove: function (userId, doc) {
      return doc.user_id === userId;
    }
  });
}
<|endoftext|>


<commit_before>$(document).on("ready", function(){

	editor = {

		// variables
		fitHeightElements: $(".full-height"),
		wrappersMargin: $("#left-column > .wrapper:first").outerHeight(true) - $("#left-column > .wrapper:first").height(),
		markdownConverter: new Showdown.converter(),
		markdownSource: $("#markdown"),
		markdownPreview: $("#preview"),
		markdownTargets: $("#html, #preview"),
		markdownTargetsTriggers: $("#right-column .overlay .switch"),
		topPanels: $("#top_panels_container .top_panel"),
		topPanelsTriggers: $("#left-column .overlay .toppanel"),
		quickReferencePreText: $("#quick-reference pre"),
		activatedFeatures: [],
		featuresTriggers: $("#right-column .overlay .feature"),

		// functions
		init: function(){
			this.onloadEffect(0);
			this.bind();
			this.switchToTarget('preview');
			this.fitHeight();
			this.convert();
			this.onloadEffect(1);
		},
		bind: function(){
			$(window).on("resize", function(){
				editor.fitHeight();
			});
			this.markdownSource.on("keyup change", function(){
				editor.convert();
			});
			this.markdownSource.on("cut paste drop", function(){
				setTimeout(function(){
					editor.convert();
				}, 1);
			});
			this.markdownTargetsTriggers.on("click", function(e){
				e.preventDefault();
				editor.switchToTarget($(this).data("switchto"));
			});
			this.topPanelsTriggers.on("click", function(e){
				e.preventDefault();
				editor.toggleTopPanel($(this).data("toppanel"));
			});
			this.topPanels.children(".close").on("click", function(e){
				e.preventDefault();
				editor.closeTopPanel();
			});
			this.quickReferencePreText.on("click", function(){
				editor.addToMarkdownSource($(this).text());
			});
			this.featuresTriggers.on("click", function(){
				editor.toggleFeature($(this).data("feature"));
			});
		},
		fitHeight: function(){
			var newHeight = $(window).height() - this.wrappersMargin;
			this.fitHeightElements.each(function(){
				if($(this).closest("#left-column").length){
					var thisNewHeight = newHeight - $("#top_panels_container").outerHeight();
				} else {
					var thisNewHeight = newHeight;
				}
				$(this).css({ height: thisNewHeight +'px' });
			});
		},
		convert: function(){
			var markdown = this.markdownSource.val(),
				html = this.markdownConverter.makeHtml(markdown);
			$("#html").val(html);
			this.markdownPreview
				.html(html)
				.trigger("updated");
		},
		addToMarkdownSource: function(markdown){
			var markdownSourceValue = this.markdownSource.val();
			if(markdownSourceValue != '') markdownSourceValue += '\n\n';
			this.markdownSource.val(markdownSourceValue + markdown);
			this.convert();
		},
		switchToTarget: function(which){
			var target = $("#"+ which),
				targetTrigger = this.markdownTargetsTriggers.filter("[data-switchto="+ which +"]");
			this.markdownTargets.not(target).hide();
			target.show();
			this.markdownTargetsTriggers.not(targetTrigger).removeClass("active");
			targetTrigger.addClass("active");
		},
		toggleTopPanel: function(which){
			var panel = $("#"+ which),
				panelTrigger = this.topPanelsTriggers.filter("[data-toppanel="+ which +"]");
			this.topPanels.not(panel).hide();
			panel.toggle();
			this.topPanelsTriggers.not(panelTrigger).removeClass("active");
			panelTrigger.toggleClass("active");
			this.fitHeight();
		},
		closeTopPanel: function(){
			this.topPanels.hide();
			this.topPanelsTriggers.removeClass("active");
			this.fitHeight();
		},
		toggleFeature: function(which){
			var featureTrigger = this.featuresTriggers.filter("[data-feature="+ which +"]");
			switch(which){
				case "auto-scroll":
					this.toggleAutoScroll();
					break;
			}
			featureTrigger.toggleClass("active");
		},
		toggleAutoScroll: function(){
			var activatedFeaturesFeatureIndex = $.inArray("auto-scroll", this.activatedFeatures);
			if(activatedFeaturesFeatureIndex == -1){
				this.markdownPreview.on("updated", function(){
					this.scrollTop = this.scrollHeight;
				});
				this.markdownPreview.trigger("updated");
				this.activatedFeatures.push("auto-scroll");
			} else {
				this.markdownPreview.off("updated");
				this.activatedFeatures.splice(activatedFeaturesFeatureIndex, 1);
			}
		},
		onloadEffect: function(step){
			var theBody = $(document.body);
			switch(step){
				case 0:
					theBody.fadeTo(0, 0);
					break;
				case 1:
					theBody.fadeTo(1000, 1);
					break;
			}
		}

	};

	editor.init();

});<commit_msg>Updated auto-scrolling<commit_after>$(document).on("ready", function(){

	editor = {

		// variables
		fitHeightElements: $(".full-height"),
		wrappersMargin: $("#left-column > .wrapper:first").outerHeight(true) - $("#left-column > .wrapper:first").height(),
		markdownConverter: new Showdown.converter(),
		markdownSource: $("#markdown"),
		markdownPreview: $("#preview"),
		markdownTargets: $("#html, #preview"),
		markdownTargetsTriggers: $("#right-column .overlay .switch"),
		topPanels: $("#top_panels_container .top_panel"),
		topPanelsTriggers: $("#left-column .overlay .toppanel"),
		quickReferencePreText: $("#quick-reference pre"),
		activatedFeatures: [],
		featuresTriggers: $("#right-column .overlay .feature"),

		// functions
		init: function(){
			this.onloadEffect(0);
			this.bind();
			this.switchToTarget('preview');
			this.fitHeight();
			this.convert();
			this.onloadEffect(1);
		},
		bind: function(){
			$(window).on("resize", function(){
				editor.fitHeight();
			});
			this.markdownSource.on("keyup change", function(){
				editor.convert();
			});
			this.markdownSource.on("cut paste drop", function(){
				setTimeout(function(){
					editor.convert();
				}, 1);
			});
			this.markdownTargetsTriggers.on("click", function(e){
				e.preventDefault();
				editor.switchToTarget($(this).data("switchto"));
			});
			this.topPanelsTriggers.on("click", function(e){
				e.preventDefault();
				editor.toggleTopPanel($(this).data("toppanel"));
			});
			this.topPanels.children(".close").on("click", function(e){
				e.preventDefault();
				editor.closeTopPanel();
			});
			this.quickReferencePreText.on("click", function(){
				editor.addToMarkdownSource($(this).text());
			});
			this.featuresTriggers.on("click", function(e){
				e.preventDefault();
				editor.toggleFeature($(this).data("feature"));
			});
		},
		fitHeight: function(){
			var newHeight = $(window).height() - this.wrappersMargin;
			this.fitHeightElements.each(function(){
				if($(this).closest("#left-column").length){
					var thisNewHeight = newHeight - $("#top_panels_container").outerHeight();
				} else {
					var thisNewHeight = newHeight;
				}
				$(this).css({ height: thisNewHeight +'px' });
			});
		},
		convert: function(){
			var markdown = this.markdownSource.val(),
				html = this.markdownConverter.makeHtml(markdown);
			$("#html").val(html);
			this.markdownPreview
				.html(html)
				.trigger("updated");
		},
		addToMarkdownSource: function(markdown){
			var markdownSourceValue = this.markdownSource.val();
			if(markdownSourceValue != '') markdownSourceValue += '\n\n';
			this.markdownSource.val(markdownSourceValue + markdown);
			this.convert();
		},
		switchToTarget: function(which){
			var target = $("#"+ which),
				targetTrigger = this.markdownTargetsTriggers.filter("[data-switchto="+ which +"]");
			this.markdownTargets.not(target).hide();
			target.show();
			this.markdownTargetsTriggers.not(targetTrigger).removeClass("active");
			targetTrigger.addClass("active");
		},
		toggleTopPanel: function(which){
			var panel = $("#"+ which),
				panelTrigger = this.topPanelsTriggers.filter("[data-toppanel="+ which +"]");
			this.topPanels.not(panel).hide();
			panel.toggle();
			this.topPanelsTriggers.not(panelTrigger).removeClass("active");
			panelTrigger.toggleClass("active");
			this.fitHeight();
		},
		closeTopPanel: function(){
			this.topPanels.hide();
			this.topPanelsTriggers.removeClass("active");
			this.fitHeight();
		},
		toggleFeature: function(which){
			var featureTrigger = this.featuresTriggers.filter("[data-feature="+ which +"]");
			switch(which){
				case "auto-scroll":
					this.toggleAutoScroll();
					break;
			}
			featureTrigger.toggleClass("active");
		},
		toggleAutoScroll: function(){
			var activatedFeaturesFeatureIndex = $.inArray("auto-scroll", this.activatedFeatures);
			if(activatedFeaturesFeatureIndex == -1){
				this.markdownPreview
					.on("updated", function(){
						this.scrollTop = this.scrollHeight;
					})
					.trigger("updated");
				this.activatedFeatures.push("auto-scroll");
			} else {
				this.markdownPreview.off("updated");
				this.activatedFeatures.splice(activatedFeaturesFeatureIndex, 1);
			}
		},
		onloadEffect: function(step){
			var theBody = $(document.body);
			switch(step){
				case 0:
					theBody.fadeTo(0, 0);
					break;
				case 1:
					theBody.fadeTo(1000, 1);
					break;
			}
		}

	};

	editor.init();

});<|endoftext|>


<commit_before>var satAttr = 'Modified Copernicus' +
    ' Sentinel data 2016 by ' +
    'SentinelMap' ;

var osmAttr = ' | © OSM' +
    ' contributors data by ' +
    'Mapzen' ;

var Attr = satAttr + osmAttr ;

var OSM0 = 'View on OSM

'
var iD0 = 'Edit with iD'

var scLayer = Tangram.leafletLayer({
    scene: 'scene.yaml',
    attribution: Attr,
    events: {
	hover: function(selection) {
	    if (selection.feature != null) {
		document.getElementById('map').style.cursor = 'pointer'
	    } else {
		document.getElementById('map').style.cursor = ''
	    }
	},
	click: function(selection) {
	    console.log(selection);
	    if (selection.feature != null) {
		fid = String(selection.feature.properties.id);
		latlng = selection.leaflet_event.latlng;
		var popup = L.popup()
		    .setLatLng(latlng)
		    .setContent(OSM0 + fid + OSM1 + iD0 + fid + iD1)
		    .openOn(map);
	    } //end of if
	} //end of click
    } //end of events
});

var map = L.map('map' , {
    center: [ 42.629, 13.2872],
    zoom: 15,
    maxZoom: 18,
    minZoom: 6,
    layers: [scLayer],
    zoomControl: false
});

var hash = new L.Hash(map);

var baseLayers = {
    "Sentinel-2": scLayer,
};

L.control.zoom({position: 'bottomleft'}).addTo(map);

// DEBUG
/*
scLayer.scene.subscribe({
    load: function (e) {
        console.log('scene loaded:', e);
    }
});

scLayer.scene.subscribe({
    view_complete: function () {
	console.log('scene view complete');
    }
});
*/
<commit_msg>Add min zoom for data editing<commit_after>var satAttr = 'Modified Copernicus' +
    ' Sentinel data 2016 by ' +
    'SentinelMap' ;

var osmAttr = ' | © OSM' +
    ' contributors data by ' +
    'Mapzen' ;

var Attr = satAttr + osmAttr ;

var OSM0 = 'View on OSM

'
var iD0 = 'Edit with iD'

var scLayer = Tangram.leafletLayer({
    scene: 'scene.yaml',
    attribution: Attr,
    events: {
	function() {
	hover: function(selection) {
	    if (selection.feature != null & map.getZoom() >= 17) {
		document.getElementById('map').style.cursor = 'pointer'
	    } else {
		document.getElementById('map').style.cursor = ''
	    }
	},
	click: function(selection) {
	    //console.log(selection);
	    if (selection.feature != null & map.getZoom() >= 17) {
		fid = String(selection.feature.properties.id);
		latlng = selection.leaflet_event.latlng;
		var popup = L.popup()
		    .setLatLng(latlng)
		    .setContent(OSM0 + fid + OSM1 + iD0 + fid + iD1)
		    .openOn(map);
	    } //end of if
	} //end of click
    } //end of events
});

var map = L.map('map' , {
    center: [ 42.629, 13.2872],
    zoom: 15,
    maxZoom: 18,
    minZoom: 6,
    layers: [scLayer],
    zoomControl: false
});

var hash = new L.Hash(map);

var baseLayers = {
    "Sentinel-2": scLayer,
};

L.control.zoom({position: 'bottomleft'}).addTo(map);

// DEBUG
/*
scLayer.scene.subscribe({
    load: function (e) {
        console.log('scene loaded:', e);
    }
});

scLayer.scene.subscribe({
    view_complete: function () {
	console.log('scene view complete');
    }
});
*/
<|endoftext|>


<commit_before>const electron = require('electron')
const path = require("path")
// Module to control application life.
const defaultMenu = require('electron-default-menu');
const { Menu, app, shell,dialog } = electron;
const os = require('os');

const ipcMain = electron.ipcMain

const isVerbose = process.argv.filter(arg => arg.indexOf("--verbose") >= 0).length > 0

// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const webContents = electron.webContents

ipcMain.on("cross-browser-ipc", (event, arg) => {
    const destinationId = arg.meta.destinationId
    const destinationWebContents = webContents.fromId(destinationId)

    log(`sending message to destinationId: ${destinationId}`)
    destinationWebContents.send("cross-browser-ipc", arg)
})

ipcMain.on("focus-next-instance", () => {
    log("focus-next-instance")
    focusNextInstance(1)
})

ipcMain.on("focus-previous-instance", () => {
    log("focus-previous-instance")
    focusNextInstance(-1)
})

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let windows = []

// Only enable 'single-instance' mode when we're not in the hot-reload mode
// Otherwise, all other open instances will also pick up the webpack bundle
if (process.env.NODE_ENV !== "development") {
    const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
        createWindow(commandLine.slice(2), workingDirectory)
    })

    if (shouldQuit) {
        app.quit()
    }
}

function createWindow(commandLineArguments, workingDirectory) {
    log(`Creating window with arguments: ${commandLineArguments} and working directory: ${workingDirectory}`)

    // Create the browser window.
    let mainWindow = new BrowserWindow({ width: 800, height: 600, icon: path.join(__dirname, "images", "Oni_128.png") })
    let menu = defaultMenu(app, shell);

  let firstMenu = os.platform()=="win32" ? 'File':'Oni';
  menu.unshift ({
    label: firstMenu,
    submenu: [
      {
        label: 'Quit',
        click: (item, focusedWindow) => {
          app.quit()
        }
      }
    ]
  })

//    deleting 'Toggle Developer Tools'
   menu[2].submenu.splice(2,1)
   menu[4].submenu = [
       {
        label: 'Learn more',
        click: (item, focusedWindow) => {
        shell.openExternal('https://github.com/extr0py/oni#introduction');
        }
      },
        {
        label: 'Issues',
        click: (item, focusedWindow) => {
          shell.openExternal('https://github.com/extr0py/oni/issues');
        }
      },
      {
        label: 'Github',
        click: (item, focusedWindow) => {
          shell.openExternal('https://github.com/extr0py/oni');
        }
      }
   ]

  Menu.setApplicationMenu(Menu.buildFromTemplate(menu));

    mainWindow.webContents.on("did-finish-load", () => {
        mainWindow.webContents.send("init", {
            args: commandLineArguments,
            workingDirectory: workingDirectory
        })
    })

    // and load the index.html of the app.
    mainWindow.loadURL(`file://${__dirname}/index.html`)

    // Open the DevTools.
    if (process.env.NODE_ENV === "development")
        mainWindow.webContents.openDevTools()

    // Emitted when the window is closed.
    mainWindow.on('closed', function() {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        windows = windows.filter(m => m !== mainWindow)
        mainWindow = null

    })

    windows.push(mainWindow)
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
    createWindow(process.argv.slice(2), process.cwd())
})

// Quit when all windows are closed.
app.on('window-all-closed', function() {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', function() {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (windows.length === 0) {
        createWindow()
    }
})

function focusNextInstance(direction) {
    const currentFocusedWindows = windows.filter(f => f.isFocused())

    if (currentFocusedWindows.length === 0) {
        log("No window currently focused")
        return
    }

    const currentFocusedWindow = currentFocusedWindows[0]
    const currentWindowIdx = windows.indexOf(currentFocusedWindow)
    let newFocusWindowIdx = (currentWindowIdx + direction) % windows.length

    if (newFocusWindowIdx < 0)
        newFocusWindowIdx = windows.length - 1

    log(`Focusing index: ${newFocusWindowIdx}`)
    windows[newFocusWindowIdx].focus()
}

function log(message) {
    if (isVerbose) {
        console.log(message)
    }
}
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<commit_msg>Bring back developer tools (#160)<commit_after>const electron = require('electron')
const path = require("path")
// Module to control application life.
const defaultMenu = require('electron-default-menu');
const { Menu, app, shell, dialog } = electron;
const os = require('os');

const ipcMain = electron.ipcMain

const isVerbose = process.argv.filter(arg => arg.indexOf("--verbose") >= 0).length > 0

// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow
const webContents = electron.webContents

ipcMain.on("cross-browser-ipc", (event, arg) => {
    const destinationId = arg.meta.destinationId
    const destinationWebContents = webContents.fromId(destinationId)

    log(`sending message to destinationId: ${destinationId}`)
    destinationWebContents.send("cross-browser-ipc", arg)
})

ipcMain.on("focus-next-instance", () => {
    log("focus-next-instance")
    focusNextInstance(1)
})

ipcMain.on("focus-previous-instance", () => {
    log("focus-previous-instance")
    focusNextInstance(-1)
})

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let windows = []

// Only enable 'single-instance' mode when we're not in the hot-reload mode
// Otherwise, all other open instances will also pick up the webpack bundle
if (process.env.NODE_ENV !== "development") {
    const shouldQuit = app.makeSingleInstance((commandLine, workingDirectory) => {
        createWindow(commandLine.slice(2), workingDirectory)
    })

    if (shouldQuit) {
        app.quit()
    }
}

function createWindow(commandLineArguments, workingDirectory) {
    log(`Creating window with arguments: ${commandLineArguments} and working directory: ${workingDirectory}`)

    // Create the browser window.
    let mainWindow = new BrowserWindow({ width: 800, height: 600, icon: path.join(__dirname, "images", "Oni_128.png") })
    let menu = defaultMenu(app, shell);

    let firstMenu = os.platform() == "win32" ? 'File' : 'Oni';
    menu.unshift({
        label: firstMenu,
        submenu: [
            {
                label: 'Quit',
                click: (item, focusedWindow) => {
                    app.quit()
                }
            }
        ]
    })

    menu[4].submenu = [
        {
            label: 'Learn more',
            click: (item, focusedWindow) => {
                shell.openExternal('https://github.com/extr0py/oni#introduction');
            }
        },
        {
            label: 'Issues',
            click: (item, focusedWindow) => {
                shell.openExternal('https://github.com/extr0py/oni/issues');
            }
        },
        {
            label: 'Github',
            click: (item, focusedWindow) => {
                shell.openExternal('https://github.com/extr0py/oni');
            }
        }
    ]

    Menu.setApplicationMenu(Menu.buildFromTemplate(menu));

    mainWindow.webContents.on("did-finish-load", () => {
        mainWindow.webContents.send("init", {
            args: commandLineArguments,
            workingDirectory: workingDirectory
        })
    })

    // and load the index.html of the app.
    mainWindow.loadURL(`file://${__dirname}/index.html`)

    // Open the DevTools.
    if (process.env.NODE_ENV === "development")
        mainWindow.webContents.openDevTools()

    // Emitted when the window is closed.
    mainWindow.on('closed', function() {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        windows = windows.filter(m => m !== mainWindow)
        mainWindow = null

    })

    windows.push(mainWindow)
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', () => {
    createWindow(process.argv.slice(2), process.cwd())
})

// Quit when all windows are closed.
app.on('window-all-closed', function() {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', function() {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (windows.length === 0) {
        createWindow()
    }
})

function focusNextInstance(direction) {
    const currentFocusedWindows = windows.filter(f => f.isFocused())

    if (currentFocusedWindows.length === 0) {
        log("No window currently focused")
        return
    }

    const currentFocusedWindow = currentFocusedWindows[0]
    const currentWindowIdx = windows.indexOf(currentFocusedWindow)
    let newFocusWindowIdx = (currentWindowIdx + direction) % windows.length

    if (newFocusWindowIdx < 0)
        newFocusWindowIdx = windows.length - 1

    log(`Focusing index: ${newFocusWindowIdx}`)
    windows[newFocusWindowIdx].focus()
}

function log(message) {
    if (isVerbose) {
        console.log(message)
    }
}
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
<|endoftext|>