<commit_before>const ClientEngine = require('Incheon').ClientEngine;
const GameWorld = require('Incheon').GameWorld;

var Ship = require("../common/Ship");
var Missile = require("../common/Missile");

class SpaaaceClientEngine extends ClientEngine{
    constructor(gameEngine){
        super(gameEngine);

        this.sprites = {};
    }

    start(){
        super.start();

        //  Game input
        this.cursors = game.input.keyboard.createCursorKeys();
        this.spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

    }

    step(){
        //important to process inputs before running the game engine loop
        this.processInputs();
        super.step();

        //update player object
        var world = this.gameEngine.world;
        for (var objId in world.objects) {
            if (world.objects.hasOwnProperty(objId)) {
                if (this.playerId == objId){
                    let objectData = world.objects[objId];

                    this.sprites[objectData.id].x = objectData.x;
                    this.sprites[objectData.id].y = objectData.y;
                    this.sprites[objectData.id].angle = objectData.angle;
                }
            }
        }

        //todo alter step count based on lag
        var stepToPlay = this.gameEngine.world.stepCount - 6;
        var previousWorldIndex;
        var nextWorldIndex;
        var previousWorld = null;
        var nextWorld = null;

        for (let x=0; x<this.worldBuffer.length; x++ ){
            if (this.worldBuffer[x].stepCount < stepToPlay){
                previousWorld = this.worldBuffer[x];
                previousWorldIndex = x;
            }
            if (this.worldBuffer[x].stepCount >= stepToPlay){
                nextWorld = this.worldBuffer[x];
                nextWorldIndex = x;
                break;
            }
        }

        if (previousWorld && nextWorld){
            let sprite;
            for (let objId in nextWorld.objects) {
                if (nextWorld.objects.hasOwnProperty(objId)) {
                    let prevObj = previousWorld.objects[objId];
                    let nextObj = nextWorld.objects[objId];
                    //todo refactor
                    if (prevObj == null) {
                        prevObj = nextObj;
                    }

                    if (this.sprites[objId] == null){

                        if (nextObj.class == Ship) {
                            let localObj = this.gameEngine.world.objects[objId] = new Ship(nextObj.id, nextObj.x, nextObj.y);
                            localObj.velocity.set(nextObj.velX, nextObj.velY);
                            localObj.isPlayerControlled = this.playerId == nextObj.id;

                            sprite = window.game.add.sprite(nextObj.x, nextObj.y, 'ship');
                            this.sprites[objId] = sprite;
                            //if own player's ship - color it
                            if (this.playerId == nextObj.id) {
                                sprite.tint = 0XFF00FF;
                            }

                            sprite.anchor.setTo(0.5, 0.5);
                            sprite.width = 50;
                            sprite.height = 45;
                        }

                        if (nextObj.class == Missile) {
                            let localObj = this.gameEngine.world.objects[objId] = new Missile(nextObj.id, nextObj.x, nextObj.y);
                            localObj.angle = nextObj.angle;

                            sprite = window.game.add.sprite(nextObj.x, nextObj.y, 'missile');
                            this.sprites[objId] = sprite;
                            //if own player's ship - color it
                            // if (this.playerId == nextObj.id) {
                            //     sprite.tint = 0XFF00FF;
                            // }

                            sprite.width = 81 * 0.5;
                            sprite.height = 46 * 0.5;
                            sprite.anchor.setTo(0.5, 0.5);
                        }

                    }
                    else{
                        sprite = this.sprites[objId];
                    }

                    //update other objects with interpolation
                    //todo refactor into general interpolation class
                    if (this.playerId != nextObj.id){

                        var playPercentage = (stepToPlay - previousWorld.stepCount)/(nextWorld.stepCount - previousWorld.stepCount);

                        if (Math.abs(nextObj.x - prevObj.x) > this.gameEngine.worldSettings.height /2){ //fix for world wraparound
                            sprite.x = nextObj.x;
                        }
                        else{
                            sprite.x = (nextObj.x - prevObj.x) * playPercentage + prevObj.x;
                        }

                        if (Math.abs(nextObj.y - prevObj.y) > this.gameEngine.worldSettings.height/2) { //fix for world wraparound
                            sprite.y = nextObj.y;
                        }
                        else{
                            sprite.y = (nextObj.y - prevObj.y) * playPercentage + prevObj.y;
                        }

                        var shortest_angle=((((nextObj.angle - prevObj.angle) % 360) + 540) % 360) - 180; //todo wrap this in a util
                        sprite.angle = prevObj.angle + shortest_angle *  playPercentage;
                    }

                }
            }

            //go over previous world to remove objects
            for (let objId in previousWorld.objects) {
                if (previousWorld.objects.hasOwnProperty(objId) && !nextWorld.objects.hasOwnProperty(objId)) {
                    delete this.gameEngine.world.objects[objId];
                    if (this.sprites[objId]) {
                        this.sprites[objId].destroy();
                    }
                    delete this.sprites[objId];
                }
            }
        }

    }

    processInputs(){
        //continuous press
        if (this.cursors.up.isDown) {
            this.sendInput('up');
        }

        if (this.cursors.left.isDown) {
            this.sendInput('left');
        }

        if (this.cursors.right.isDown) {
            this.sendInput('right');
        }

        //single press
        if (this.spaceKey.isDown && this.spaceKey.repeats == 0){
            this.sendInput('space');
        }
    }

}

module.exports = SpaaaceClientEngine;<commit_msg>use playerControlled in update step<commit_after>const ClientEngine = require('Incheon').ClientEngine;
const GameWorld = require('Incheon').GameWorld;

var Ship = require("../common/Ship");
var Missile = require("../common/Missile");

class SpaaaceClientEngine extends ClientEngine{
    constructor(gameEngine){
        super(gameEngine);

        this.sprites = {};
    }

    start(){
        super.start();

        //  Game input
        this.cursors = game.input.keyboard.createCursorKeys();
        this.spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

    }

    step(){
        //important to process inputs before running the game engine loop
        this.processInputs();
        super.step();

        //update player object
        var world = this.gameEngine.world;
        for (var objId in world.objects) {
            if (world.objects.hasOwnProperty(objId)) {
                if (world.objects[objId].isPlayerControlled){
                    let objectData = world.objects[objId];

                    this.sprites[objectData.id].x = objectData.x;
                    this.sprites[objectData.id].y = objectData.y;
                    this.sprites[objectData.id].angle = objectData.angle;
                }
            }
        }

        //todo alter step count based on lag
        var stepToPlay = this.gameEngine.world.stepCount - 6;
        var previousWorldIndex;
        var nextWorldIndex;
        var previousWorld = null;
        var nextWorld = null;

        for (let x=0; x<this.worldBuffer.length; x++ ){
            if (this.worldBuffer[x].stepCount < stepToPlay){
                previousWorld = this.worldBuffer[x];
                previousWorldIndex = x;
            }
            if (this.worldBuffer[x].stepCount >= stepToPlay){
                nextWorld = this.worldBuffer[x];
                nextWorldIndex = x;
                break;
            }
        }

        if (previousWorld && nextWorld){
            let sprite;
            for (let objId in nextWorld.objects) {
                if (nextWorld.objects.hasOwnProperty(objId)) {
                    let prevObj = previousWorld.objects[objId];
                    let nextObj = nextWorld.objects[objId];
                    //todo refactor
                    if (prevObj == null) {
                        prevObj = nextObj;
                    }

                    if (this.sprites[objId] == null){

                        if (nextObj.class == Ship) {
                            let localObj = this.gameEngine.world.objects[objId] = new Ship(nextObj.id, nextObj.x, nextObj.y);
                            localObj.velocity.set(nextObj.velX, nextObj.velY);
                            localObj.isPlayerControlled = this.playerId == nextObj.id;

                            sprite = window.game.add.sprite(nextObj.x, nextObj.y, 'ship');
                            this.sprites[objId] = sprite;
                            //if own player's ship - color it
                            if (this.playerId == nextObj.id) {
                                sprite.tint = 0XFF00FF;
                            }

                            sprite.anchor.setTo(0.5, 0.5);
                            sprite.width = 50;
                            sprite.height = 45;
                        }

                        if (nextObj.class == Missile) {
                            let localObj = this.gameEngine.world.objects[objId] = new Missile(nextObj.id, nextObj.x, nextObj.y);
                            localObj.angle = nextObj.angle;

                            sprite = window.game.add.sprite(nextObj.x, nextObj.y, 'missile');
                            this.sprites[objId] = sprite;
                            //if own player's ship - color it
                            // if (this.playerId == nextObj.id) {
                            //     sprite.tint = 0XFF00FF;
                            // }

                            sprite.width = 81 * 0.5;
                            sprite.height = 46 * 0.5;
                            sprite.anchor.setTo(0.5, 0.5);
                        }

                    }
                    else{
                        sprite = this.sprites[objId];
                    }

                    //update other objects with interpolation
                    //todo refactor into general interpolation class
                    if (this.playerId != nextObj.id){

                        var playPercentage = (stepToPlay - previousWorld.stepCount)/(nextWorld.stepCount - previousWorld.stepCount);

                        if (Math.abs(nextObj.x - prevObj.x) > this.gameEngine.worldSettings.height /2){ //fix for world wraparound
                            sprite.x = nextObj.x;
                        }
                        else{
                            sprite.x = (nextObj.x - prevObj.x) * playPercentage + prevObj.x;
                        }

                        if (Math.abs(nextObj.y - prevObj.y) > this.gameEngine.worldSettings.height/2) { //fix for world wraparound
                            sprite.y = nextObj.y;
                        }
                        else{
                            sprite.y = (nextObj.y - prevObj.y) * playPercentage + prevObj.y;
                        }

                        var shortest_angle=((((nextObj.angle - prevObj.angle) % 360) + 540) % 360) - 180; //todo wrap this in a util
                        sprite.angle = prevObj.angle + shortest_angle *  playPercentage;
                    }

                }
            }

            //go over previous world to remove objects
            for (let objId in previousWorld.objects) {
                if (previousWorld.objects.hasOwnProperty(objId) && !nextWorld.objects.hasOwnProperty(objId)) {
                    delete this.gameEngine.world.objects[objId];
                    if (this.sprites[objId]) {
                        this.sprites[objId].destroy();
                    }
                    delete this.sprites[objId];
                }
            }
        }

    }

    processInputs(){
        //continuous press
        if (this.cursors.up.isDown) {
            this.sendInput('up');
        }

        if (this.cursors.left.isDown) {
            this.sendInput('left');
        }

        if (this.cursors.right.isDown) {
            this.sendInput('right');
        }

        //single press
        if (this.spaceKey.isDown && this.spaceKey.repeats == 0){
            this.sendInput('space');
        }
    }

}

module.exports = SpaaaceClientEngine;<|endoftext|>


<commit_before><commit_msg>aha20<commit_after><|endoftext|>


<commit_before><commit_msg>Move section highlighting to its own context<commit_after><|endoftext|>


<commit_before>import chalk from "chalk"
import path from "path"
import _ from "lodash"
import symbols from "log-symbols"
import stringWidth from "string-width"
import table, { getBorderCharacters } from "table"
import utils from "postcss-reporter/lib/util"

const MARGIN_WIDTHS = 9

const levelColors = {
  info: "blue",
  warning: "yellow",
  error: "red",
}

function deprecationsFormatter(results) {
  const allDeprecationWarnings = _.flatMap(results, "deprecations")
  const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, "text")

  if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) { return "" }

  return uniqueDeprecationWarnings.reduce((output, warning) => {
    output += chalk.yellow("Deprecation Warning: ")
    output += warning.text
    if (warning.reference) {
      output += chalk.dim(" See: ")
      output += chalk.dim.underline(warning.reference)
    }
    return output + "\n"
  }, "\n")
}

function invalidOptionsFormatter(results) {
  const allInvalidOptionWarnings = _.flatMap(results, r => r.invalidOptionWarnings.map(w => w.text))
  const uniqueInvalidOptionWarnings = _.uniq(allInvalidOptionWarnings)

  return uniqueInvalidOptionWarnings.reduce((output, warning) => {
    output += chalk.red("Invalid Option: ")
    output += warning
    return output + "\n"
  }, "\n")
}

function logFrom(fromValue) {
  if (fromValue.charAt(0) === "<") return fromValue
  return path.relative(process.cwd(), fromValue).split(path.sep).join("/")
}

function getMessageWidth(columnWidths) {
  if (!process.stdout.isTTY) {
    return columnWidths[3]
  }

  const availableWidth = process.stdout.columns
  const fullWidth = _.sum(_.values(columnWidths))

  // If there is no reason to wrap the text, we won't align the last column to the right
  if (availableWidth > fullWidth + MARGIN_WIDTHS) {
    return columnWidths[3]
  }

  return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS)
}

function formatter(messages, source) {
  if (!messages.length) return ""

  const orderedMessages = _.sortBy(
    messages,
    (m) => m.line ? 2 : 1, // positionless first
    (m) => m.line ,
    (m) => m.column
  )

  // Create a list of column widths, needed to calculate
  // the size of the message column and if needed wrap it.
  const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 }

  const calculateWidths = function (columns) {

    _.forOwn(columns, (value, key) => {
      columnWidths[key] = Math.max(columnWidths[key], stringWidth(value))
    })

    return columns
  }

  let output = "\n"

  if (source) {
    output += chalk.underline(logFrom(source)) + "\n"
  }

  const cleanedMessages = orderedMessages.map(
    (message) => {
      const location = utils.getLocation(message)
      const severity = message.severity

      const row = [
        location.line || "",
        location.column || "",
        symbols[severity] ? chalk[levelColors[severity]](symbols[severity]) : severity,
        message.text.replace(/\.$/, "").replace(new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"), ""),
        chalk.gray(message.rule || ""),
      ]

      calculateWidths(row)

      return row
    })

  output += table(
    cleanedMessages,
    {
      border: getBorderCharacters("void"),
      columns: {
        0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
        1: { alignment: "left", width: columnWidths[1] },
        2: { alignment: "center", width: columnWidths[2] },
        3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true },
        4: { alignment: "left", width: columnWidths[4], paddingRight: 0 },
      },
      drawHorizontalLine: () => false,
    }
    )
    .split("\n")
    .map((el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.gray(p1 + ":" + p2)))
    .join("\n")

  return output
}

export default function (results) {
  let output = invalidOptionsFormatter(results)
  output += deprecationsFormatter(results)

  output = results.reduce((output, result) => {
    output += formatter(result.warnings, result.source)
    return output
  }, output)

  // Ensure consistent padding
  output = output.trim()

  if (output !== "") {
    output = "\n" + output + "\n\n"
  }

  return output
}
<commit_msg>Fix stringFormatter<commit_after>import chalk from "chalk"
import path from "path"
import _ from "lodash"
import symbols from "log-symbols"
import stringWidth from "string-width"
import table, { getBorderCharacters } from "table"
import utils from "postcss-reporter/lib/util"

const MARGIN_WIDTHS = 9

const levelColors = {
  info: "blue",
  warning: "yellow",
  error: "red",
}

function deprecationsFormatter(results) {
  const allDeprecationWarnings = _.flatMap(results, "deprecations")
  const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, "text")

  if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) { return "" }

  return uniqueDeprecationWarnings.reduce((output, warning) => {
    output += chalk.yellow("Deprecation Warning: ")
    output += warning.text
    if (warning.reference) {
      output += chalk.dim(" See: ")
      output += chalk.dim.underline(warning.reference)
    }
    return output + "\n"
  }, "\n")
}

function invalidOptionsFormatter(results) {
  const allInvalidOptionWarnings = _.flatMap(results, r => r.invalidOptionWarnings.map(w => w.text))
  const uniqueInvalidOptionWarnings = _.uniq(allInvalidOptionWarnings)

  return uniqueInvalidOptionWarnings.reduce((output, warning) => {
    output += chalk.red("Invalid Option: ")
    output += warning
    return output + "\n"
  }, "\n")
}

function logFrom(fromValue) {
  if (fromValue.charAt(0) === "<") return fromValue
  return path.relative(process.cwd(), fromValue).split(path.sep).join("/")
}

function getMessageWidth(columnWidths) {
  if (!process.stdout.isTTY) {
    return columnWidths[3]
  }

  const availableWidth = process.stdout.columns
  const fullWidth = _.sum(_.values(columnWidths))

  // If there is no reason to wrap the text, we won't align the last column to the right
  if (availableWidth > fullWidth + MARGIN_WIDTHS) {
    return columnWidths[3]
  }

  return availableWidth - (fullWidth - columnWidths[3] + MARGIN_WIDTHS)
}

function formatter(messages, source) {
  if (!messages.length) return ""

  const orderedMessages = _.sortBy(
    messages,
    (m) => m.line ? 2 : 1, // positionless first
    (m) => m.line ,
    (m) => m.column
  )

  // Create a list of column widths, needed to calculate
  // the size of the message column and if needed wrap it.
  const columnWidths = { 0: 1, 1: 1, 2: 1, 3: 1, 4: 1 }

  const calculateWidths = function (columns) {

    _.forOwn(columns, (value, key) => {
      columnWidths[key] = Math.max(columnWidths[key], stringWidth(value.toString()))
    })

    return columns
  }

  let output = "\n"

  if (source) {
    output += chalk.underline(logFrom(source)) + "\n"
  }

  const cleanedMessages = orderedMessages.map(
    (message) => {
      const location = utils.getLocation(message)
      const severity = message.severity

      const row = [
        location.line || "",
        location.column || "",
        symbols[severity] ? chalk[levelColors[severity]](symbols[severity]) : severity,
        message.text.replace(/\.$/, "").replace(new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"), ""),
        chalk.gray(message.rule || ""),
      ]

      calculateWidths(row)

      return row
    })

  output += table(
    cleanedMessages,
    {
      border: getBorderCharacters("void"),
      columns: {
        0: { alignment: "right", width: columnWidths[0], paddingRight: 0 },
        1: { alignment: "left", width: columnWidths[1] },
        2: { alignment: "center", width: columnWidths[2] },
        3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true },
        4: { alignment: "left", width: columnWidths[4], paddingRight: 0 },
      },
      drawHorizontalLine: () => false,
    }
    )
    .split("\n")
    .map((el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.gray(p1 + ":" + p2)))
    .join("\n")

  return output
}

export default function (results) {
  let output = invalidOptionsFormatter(results)
  output += deprecationsFormatter(results)

  output = results.reduce((output, result) => {
    output += formatter(result.warnings, result.source)
    return output
  }, output)

  // Ensure consistent padding
  output = output.trim()

  if (output !== "") {
    output = "\n" + output + "\n\n"
  }

  return output
}
<|endoftext|>


<commit_before>import React, { Component } from "react";
import style from "./Pedido.css";

export default class Pedido extends Component {
  props: {
    nombre: string,
    prioridad: number,
    fechaSolicitud: Date,
    ordenes: Array
  }

  clasePrioridadPedido() {
    const coloresPrioridades = [
      style.prioridadBaja,
      style.prioridadNormal,
      style.prioridadAlta,
      style.prioridadMuyAlta
    ];

    let { prioridad } = this.props;
    if (prioridad > 3) {
      prioridad = 3;
    } else if (prioridad < 0) {
      prioridad = 0;
    }

    return [style.pedido, coloresPrioridades[prioridad]].join(" ");
  }

  render() {
    const { nombre, fechaSolicitud, ordenes, prioridad } = this.props;
    const nombreClasePedido = this.clasePrioridadPedido();
    return (

          {nombre}

          {
            ordenes.map((orden) => (

                {orden.cantidad} x {orden.nombre}

            ))
          }

          {fechaSolicitud.toLocaleString()}

    );
  }
}
<commit_msg>Se elimina variable no utilizada<commit_after>import React, { Component } from "react";
import style from "./Pedido.css";

export default class Pedido extends Component {
  props: {
    nombre: string,
    prioridad: number,
    fechaSolicitud: Date,
    ordenes: Array
  }

  clasePrioridadPedido() {
    const coloresPrioridades = [
      style.prioridadBaja,
      style.prioridadNormal,
      style.prioridadAlta,
      style.prioridadMuyAlta
    ];

    let { prioridad } = this.props;
    if (prioridad > 3) {
      prioridad = 3;
    } else if (prioridad < 0) {
      prioridad = 0;
    }

    return [style.pedido, coloresPrioridades[prioridad]].join(" ");
  }

  render() {
    const { nombre, fechaSolicitud, ordenes } = this.props;
    const nombreClasePedido = this.clasePrioridadPedido();
    return (

          {nombre}

          {
            ordenes.map((orden) => (

                {orden.cantidad} x {orden.nombre}

            ))
          }

          {fechaSolicitud.toLocaleString()}

    );
  }
}
<|endoftext|>


<commit_before>$.index.open();<commit_msg>pull test<commit_after>$.index.open();
// good
<|endoftext|>


<commit_before>//////////////////////////////////////////////////
// Build View

// You can build accordion views from controllers

function makeLab(text, size, col, cls){
  return Titanium.UI.createLabel({
    width:  "70%",
    height: Ti.UI.SIZE,
    text:   text,
    font:   { fontSize:size, },
    color:  col,
    // classes:  [cls],
  });
}

msgs = [
  ["^ What is it?","Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."],
  ["^ Why do we use it?","It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)."],
  ["^ Where does it come from?",'Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.'],
  ["^ Where can I get some?","There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making lipsum.com the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc."],
  ["^ The standard Lorem Ipsum passage","Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],
];

for (var i=0; i<msgs.length; i++){
  ///////
  // Make accordion view

  var title = msgs[i][0];
  var text  = msgs[i][1];

  // First child of this will be the clickable item
  var header = makeLab(title, 16, "#111", "toggleLab");
  header.applyProperties({class:"toggleLab"})
  // rest of children are hidden on clicks
  var body   = makeLab(text,  12, "#222", "lab");
  body.applyProperties({class:"lab"})
  // make parent
  var par = Titanium.UI.createView({
    width:       "80%",
    height:      Ti.UI.SIZE,
    layout:      "vertical", // <-- this is force set by the module anyway
    borderWidth: 2,
    borderColor: "yellow",
    accordion:   "true" // <------------------ Notice
  });
  par.accordion = "true";
  par.add(header);
  par.add(body);

  //////
  // Make spacer

  var spacer = Titanium.UI.createView({
    height: 10,
    width:  1,
  })

  //////
  // add to UI
  $.lorem.add(par);
  $.lorem.add(spacer);
}

$.img.image = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, 'images/seinfeld.png');

// You can also build accordion views directly in a view
// click callback function for view element
function btn(e){
  alert("You clickied me!");
}

//////////////////////////////////////////////////
// setup

// Open (render) the window before running accordion.setup
$.win.open();

// require module
var accordion = require("accordion");

//////////////////////////////////////////////////
// define input parameters and run

function foldCallback(view){
  // this runs whenever an accordion view element is clicked
  // the "view" argument is the tag that has accordion="true" attached
  // example: use this to toggle an icon indicating expanded/collapsed state
  var child = view.getChildren()[0];
  var tx = child.text;
  if (tx[0] == "v"){
    tx = '^'+tx.slice(1);
  } else if (tx[0] == "^"){
    tx = "v"+tx.slice(1);
  };
  child.text = tx;
}

// define recursion depth when searching through Alloy view XML tree
var depth = -1 // no max depth

// initial state of the elements in the accordion menu
var startCollapsed = true;

// automatically add all event handlers and sizing logic to the defined elements
accordion.setup($.lorem, depth, startCollapsed, foldCallback);<commit_msg>cleaned up a bit<commit_after>//////////////////////////////////////////////////
// Build View

// You can build accordion views from controllers

function makeLab(text, size, col){
  return Titanium.UI.createLabel({
    width:  "70%",
    height: Ti.UI.SIZE,
    text:   text,
    font:   { fontSize:size, },
    color:  col,
  });
}

msgs = [
  ["^ What is it?","Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."],
  ["^ Why do we use it?","It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like)."],
  ["^ Where does it come from?",'Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.'],
  ["^ Where can I get some?","There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making lipsum.com the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc."],
  ["^ The standard Lorem Ipsum passage","Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."],
];

for (var i=0; i<msgs.length; i++){
  ///////
  // Make accordion view

  var title = msgs[i][0];
  var text  = msgs[i][1];

  // First child of this will be the clickable item
  var header = makeLab(title, 16, "#111");

  // rest of children are hidden on clicks
  var body   = makeLab(text,  12, "#222");

  // make parent
  var par = Titanium.UI.createView({
    width:       "80%",
    height:      Ti.UI.SIZE,
    layout:      "vertical", // <-- this is force set by the module anyway
    borderWidth: 2,
    borderColor: "yellow",
    accordion:   "true" // <------------------ Notice
  });
  par.accordion = "true";
  par.add(header);
  par.add(body);

  //////
  // Make spacer

  var spacer = Titanium.UI.createView({
    height: 10,
    width:  1,
  })

  //////
  // add to UI
  $.lorem.add(par);
  $.lorem.add(spacer);
}

$.img.image = Ti.Filesystem.getFile(Ti.Filesystem.resourcesDirectory, 'images/seinfeld.png');

// You can also build accordion views directly in a view
// click callback function for view element
function btn(e){
  alert("You clickied me!");
}

//////////////////////////////////////////////////
// setup

// Open (render) the window before running accordion.setup
$.win.open();

// require module
var accordion = require("accordion");

//////////////////////////////////////////////////
// define input parameters and run

function foldCallback(view){
  // this runs whenever an accordion view element is clicked
  // the "view" argument is the tag that has accordion="true" attached
  // example: use this to toggle an icon indicating expanded/collapsed state
  var child = view.getChildren()[0];
  var tx = child.text;
  if (tx[0] == "v"){
    tx = '^'+tx.slice(1);
  } else if (tx[0] == "^"){
    tx = "v"+tx.slice(1);
  };
  child.text = tx;
}

// define recursion depth when searching through Alloy view XML tree
var depth = -1 // no max depth

// initial state of the elements in the accordion menu
var startCollapsed = true;

// automatically add all event handlers and sizing logic to the defined elements
accordion.setup($.lorem, depth, startCollapsed, foldCallback);<|endoftext|>


<commit_before>import Controller, {inject as controller} from '@ember/controller';
import config from 'ghost-admin/config/environment';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {ghPriceAmount} from '../helpers/gh-price-amount';
import {inject as service} from '@ember/service';
import {slugify} from '@tryghost/string';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

export default class OffersController extends Controller {
    @controller offers;
    @service config;
    @service settings;
    @service store;
    @service modals;
    @service feature;
    @service membersUtils;
    @service notifications;

    @tracked cadences = [];
    @tracked products = [];
    @tracked portalPreviewUrl = '';
    @tracked showUnsavedChangesModal = false;

    @tracked defaultSiteUrl = this.config.get('blogUrl');

    @tracked durations = [
        {
            label: 'Forever',
            duration: 'forever'
        },
        {
            label: 'First-payment',
            duration: 'once'
        },
        {
            label: 'Multiple-months',
            duration: 'repeating'
        }
    ];

    @tracked offertypes = [
        {
            label: '%',
            offertype: 'percent'
        },
        {
            label: 'USD',
            offertype: 'fixed'
        }
    ];

    @tracked defaultProps = null;

    leaveScreenTransition = null;
    portalPreviewGuid = Date.now().valueOf();

    constructor() {
        super(...arguments);
        if (this.isTesting === undefined) {
            this.isTesting = config.environment === 'test';
        }
    }

    get offer() {
        return this.model;
    }

    set offer(offer) {
        this.model = offer;
    }

    get scratchOffer() {
        return {
            ...this.offer
        };
    }

    get cadence() {
        if (this.offer.tier && this.offer.cadence) {
            const product = this.products.findBy('id', this.offer.tier.id);
            return `${this.offer.tier.id}-${this.offer.cadence}-${product?.monthlyPrice?.currency}`;
        } else if (this.defaultProps) {
            const product = this.products.findBy('id', this.defaultProps.tier.id);
            return `${this.defaultProps.tier.id}-${this.defaultProps.cadence}-${product?.monthlyPrice?.currency}`;
        }
        return '';
    }

    get isDiscountSectionDisabled() {
        return !this.offer.isNew;
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *fetchProducts() {
        this.products = yield this.store.query('product', {include: 'monthly_price,yearly_price'});
        const cadences = [];
        this.products.forEach((product) => {
            let monthlyLabel;
            let yearlyLabel;
            const productCurrency = product.monthlyPrice.currency;
            const productCurrencySymbol = productCurrency.toUpperCase();
            if (this.feature.get('multipleProducts')) {
                monthlyLabel = `${product.name} - Monthly (${ghPriceAmount(product.monthlyPrice.amount)} ${productCurrencySymbol})`;
                yearlyLabel = `${product.name} - Yearly (${ghPriceAmount(product.yearlyPrice.amount)} ${productCurrencySymbol})`;
            } else {
                monthlyLabel = `Monthly (${ghPriceAmount(product.monthlyPrice.amount)} ${productCurrencySymbol})`;
                yearlyLabel = `Yearly (${ghPriceAmount(product.yearlyPrice.amount)} ${productCurrencySymbol})`;
            }

            cadences.push({
                label: monthlyLabel,
                name: `${product.id}-month-${productCurrency}`
            });

            cadences.push({
                label: yearlyLabel,
                name: `${product.id}-year-${productCurrency}`
            });
        });
        this.cadences = cadences;
        const defaultCadence = this.cadences[0]?.name;
        const [,interval, defaultCurrency] = (defaultCadence || '').split('-');

        this.updateDurations(interval);
        if (this.offer && !this.offer.tier) {
            this.defaultProps = {};
            this.updateCadence(defaultCadence, this.defaultProps);
            this.updatePortalPreview({forceRefresh: false});
        } else if (defaultCadence) {
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: defaultCurrency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @task({drop: true})
    *copyOfferUrl() {
        copyTextToClipboard(this.offerUrl);
        yield timeout(this.isTesting ? 50 : 500);
        return true;
    }

    @task({drop: true})
    *saveTask() {
        let {offer} = this;

        if (!offer.tier && this.defaultProps) {
            this.offer.tier = {
                id: this.defaultProps?.tier.id
            };
            this.offer.cadence = this.defaultProps.cadence;
            this.offer.currency = this.defaultProps.currency;
        }

        try {
            yield this.offer.validate();
            yield offer.save();

            // replace 'offer.new' route with 'offer' route
            this.replaceRoute('offer', offer);

            return offer;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'offer.save'});
            }
        }
    }

    @task
    *fetchOfferTask(offerId) {
        this.isLoading = true;

        this.offer = yield this.store.queryRecord('offer', {
            id: offerId
        });

        this.isLoading = false;
    }

    @action
    portalPreviewInserted(iframe) {
        this.portalPreviewIframe = iframe;

        if (!this.portalMessageListener) {
            this.portalMessageListener = (event) => {
                const resizeEvents = ['portal-ready', 'portal-preview-updated'];
                if (resizeEvents.includes(event.data.type) && event.data.payload?.height && this.portalPreviewIframe.parentNode) {
                    this.portalPreviewIframe.parentNode.style.height = `${event.data.payload.height}px`;
                }
            };

            window.addEventListener('message', this.portalMessageListener, true);
        }
    }

    @action
    portalPreviewDestroyed() {
        this.portalPreviewIframe = null;

        if (this.portalMessageListener) {
            window.removeEventListener('message', this.portalMessageListener);
        }
    }

    @action
    updatePortalPreview({forceRefresh} = {forceRefresh: false}) {
        const newUrl = new URL(this.membersUtils.getOfferPortalPreviewUrl({
            name: this.offer.name || 'No Name',
            code: this.offer.code || 'no-code',
            displayTitle: this.offer.displayTitle,
            displayDescription: this.offer.displayDescription,
            type: this.offer.type,
            cadence: this.offer.cadence || this.defaultProps?.cadence,
            amount: this.offer.amount,
            duration: this.offer.duration,
            durationInMonths: this.offer.durationInMonths,
            currency: this.offer.currency || this.defaultProps?.currency,
            status: this.offer.currency,
            tierId: this.offer?.tier?.id || this.defaultProps?.tier?.id
        }));

        if (forceRefresh) {
            this.portalPreviewGuid = Date.now().valueOf();
        }
        newUrl.searchParams.set('v', `${this.portalPreviewGuid}`);

        this.portalPreviewUrl = newUrl;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    leaveScreen() {
        this.offer.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    setup() {
        this.fetchProducts.perform();
    }

    @action
    setProperty(propKey, value) {
        this._saveOfferProperty(propKey, value);
    }

    @action
    setDiscountType(discountType) {
        if (!this.isDiscountSectionDisabled) {
            const amount = this.offer.amount || 0;
            this._saveOfferProperty('type', discountType);
            if (this.offer.type === 'fixed' && this.offer.amount !== '') {
                this.offer.amount = amount * 100;
            } else if (this.offer.amount !== '') {
                this.offer.amount = amount / 100;
            }
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    setDiscountAmount(e) {
        let amount = e.target.value;
        if (this.offer.type === 'fixed' && amount !== '') {
            amount = parseFloat(amount) * 100;
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setOfferName(e) {
        this._saveOfferProperty('name', e.target.value);
    }

    @action
    setPortalTitle(e) {
        this._saveOfferProperty('displayTitle', e.target.value);
    }

    @action
    setPortalDescription(e) {
        this._saveOfferProperty('displayDescription', e.target.value);
    }

    @action
    setOfferCode(e) {
        this._saveOfferProperty('code', e.target.value);
    }

    @action
    setDurationInMonths(e) {
        this._saveOfferProperty('durationInMonths', e.target.value);
    }

    @action
    openConfirmArchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open('modals/archive-offer', {
                offer: this.offer
            }, {
                className: 'fullscreen-modal fullscreen-modal-action fullscreen-modal-wide'
            });
        }
    }

    @action
    openConfirmUnarchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open('modals/unarchive-offer', {
                offer: this.offer
            }, {
                className: 'fullscreen-modal fullscreen-modal-action fullscreen-modal-wide'
            });
        }
    }

    get offerUrl() {
        const code = this.offer?.code || '';
        if (code) {
            const siteUrl = this.config.get('blogUrl');
            return `${siteUrl}/${slugify(code)}`;
        }
        return '';
    }

    get displayCurrency() {
        const tierId = this.offer?.tier?.id;
        if (!tierId) {
            return '$';
        }
        const product = this.products.findBy('id', tierId);
        const productCurrency = product?.monthlyPrice?.currency || 'usd';
        return getSymbol(productCurrency);
    }

    get currencyLength() {
        return this.displayCurrency.length;
    }

    @action
    updateDurations(cadence) {
        if (cadence) {
            if (cadence === 'month') {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Multiple-months',
                        duration: 'repeating'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
            } else {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
                if (this.offer.duration === 'repeating') {
                    this._saveOfferProperty('duration', 'once');
                }
            }
        }
    }

    @action
    updateCadence(cadence, offerObj) {
        offerObj = offerObj || this.offer;
        if (cadence) {
            const [tierId, tierCadence, currency] = cadence.split('-');
            offerObj.tier = {
                id: tierId
            };
            offerObj.cadence = tierCadence;
            offerObj.currency = currency;
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: currency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updateDurations(tierCadence);
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    updateDuration(duration) {
        this._saveOfferProperty('duration', duration);
    }

    // Private -----------------------------------------------------------------

    _saveOfferProperty(propKey, newValue) {
        let currentValue = this.offer[propKey];

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.offer[propKey] = newValue;
        this.updatePortalPreview({forceRefresh: false});
    }
}
<commit_msg>Added offer name as default value for display title<commit_after>import Controller, {inject as controller} from '@ember/controller';
import config from 'ghost-admin/config/environment';
import copyTextToClipboard from 'ghost-admin/utils/copy-text-to-clipboard';
import {action} from '@ember/object';
import {getSymbol} from 'ghost-admin/utils/currency';
import {ghPriceAmount} from '../helpers/gh-price-amount';
import {inject as service} from '@ember/service';
import {slugify} from '@tryghost/string';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

export default class OffersController extends Controller {
    @controller offers;
    @service config;
    @service settings;
    @service store;
    @service modals;
    @service feature;
    @service membersUtils;
    @service notifications;

    @tracked cadences = [];
    @tracked products = [];
    @tracked portalPreviewUrl = '';
    @tracked showUnsavedChangesModal = false;

    @tracked defaultSiteUrl = this.config.get('blogUrl');

    @tracked durations = [
        {
            label: 'Forever',
            duration: 'forever'
        },
        {
            label: 'First-payment',
            duration: 'once'
        },
        {
            label: 'Multiple-months',
            duration: 'repeating'
        }
    ];

    @tracked offertypes = [
        {
            label: '%',
            offertype: 'percent'
        },
        {
            label: 'USD',
            offertype: 'fixed'
        }
    ];

    @tracked defaultProps = null;
    @tracked isDisplayTitleEdited = false;

    leaveScreenTransition = null;
    portalPreviewGuid = Date.now().valueOf();

    constructor() {
        super(...arguments);
        if (this.isTesting === undefined) {
            this.isTesting = config.environment === 'test';
        }
    }

    get offer() {
        return this.model;
    }

    set offer(offer) {
        this.model = offer;
    }

    get scratchOffer() {
        return {
            ...this.offer
        };
    }

    get cadence() {
        if (this.offer.tier && this.offer.cadence) {
            const product = this.products.findBy('id', this.offer.tier.id);
            return `${this.offer.tier.id}-${this.offer.cadence}-${product?.monthlyPrice?.currency}`;
        } else if (this.defaultProps) {
            const product = this.products.findBy('id', this.defaultProps.tier.id);
            return `${this.defaultProps.tier.id}-${this.defaultProps.cadence}-${product?.monthlyPrice?.currency}`;
        }
        return '';
    }

    get isDiscountSectionDisabled() {
        return !this.offer.isNew;
    }

    // Tasks -------------------------------------------------------------------

    @task({drop: true})
    *fetchProducts() {
        this.products = yield this.store.query('product', {include: 'monthly_price,yearly_price'});
        const cadences = [];
        this.products.forEach((product) => {
            let monthlyLabel;
            let yearlyLabel;
            const productCurrency = product.monthlyPrice.currency;
            const productCurrencySymbol = productCurrency.toUpperCase();
            if (this.feature.get('multipleProducts')) {
                monthlyLabel = `${product.name} - Monthly (${ghPriceAmount(product.monthlyPrice.amount)} ${productCurrencySymbol})`;
                yearlyLabel = `${product.name} - Yearly (${ghPriceAmount(product.yearlyPrice.amount)} ${productCurrencySymbol})`;
            } else {
                monthlyLabel = `Monthly (${ghPriceAmount(product.monthlyPrice.amount)} ${productCurrencySymbol})`;
                yearlyLabel = `Yearly (${ghPriceAmount(product.yearlyPrice.amount)} ${productCurrencySymbol})`;
            }

            cadences.push({
                label: monthlyLabel,
                name: `${product.id}-month-${productCurrency}`
            });

            cadences.push({
                label: yearlyLabel,
                name: `${product.id}-year-${productCurrency}`
            });
        });
        this.cadences = cadences;
        const defaultCadence = this.cadences[0]?.name;
        const [,interval, defaultCurrency] = (defaultCadence || '').split('-');

        this.updateDurations(interval);
        if (this.offer && !this.offer.tier) {
            this.defaultProps = {};
            this.updateCadence(defaultCadence, this.defaultProps);
            this.updatePortalPreview({forceRefresh: false});
        } else if (defaultCadence) {
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: defaultCurrency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @task({drop: true})
    *copyOfferUrl() {
        copyTextToClipboard(this.offerUrl);
        yield timeout(this.isTesting ? 50 : 500);
        return true;
    }

    @task({drop: true})
    *saveTask() {
        let {offer} = this;

        if (!offer.tier && this.defaultProps) {
            this.offer.tier = {
                id: this.defaultProps?.tier.id
            };
            this.offer.cadence = this.defaultProps.cadence;
            this.offer.currency = this.defaultProps.currency;
        }

        try {
            yield this.offer.validate();
            yield offer.save();

            // replace 'offer.new' route with 'offer' route
            this.replaceRoute('offer', offer);

            return offer;
        } catch (error) {
            if (error) {
                this.notifications.showAPIError(error, {key: 'offer.save'});
            }
        }
    }

    @task
    *fetchOfferTask(offerId) {
        this.isLoading = true;

        this.offer = yield this.store.queryRecord('offer', {
            id: offerId
        });

        this.isLoading = false;
    }

    @action
    portalPreviewInserted(iframe) {
        this.portalPreviewIframe = iframe;

        if (!this.portalMessageListener) {
            this.portalMessageListener = (event) => {
                const resizeEvents = ['portal-ready', 'portal-preview-updated'];
                if (resizeEvents.includes(event.data.type) && event.data.payload?.height && this.portalPreviewIframe.parentNode) {
                    this.portalPreviewIframe.parentNode.style.height = `${event.data.payload.height}px`;
                }
            };

            window.addEventListener('message', this.portalMessageListener, true);
        }
    }

    @action
    portalPreviewDestroyed() {
        this.portalPreviewIframe = null;

        if (this.portalMessageListener) {
            window.removeEventListener('message', this.portalMessageListener);
        }
    }

    @action
    updatePortalPreview({forceRefresh} = {forceRefresh: false}) {
        const newUrl = new URL(this.membersUtils.getOfferPortalPreviewUrl({
            name: this.offer.name || 'No Name',
            code: this.offer.code || 'no-code',
            displayTitle: this.offer.displayTitle,
            displayDescription: this.offer.displayDescription,
            type: this.offer.type,
            cadence: this.offer.cadence || this.defaultProps?.cadence,
            amount: this.offer.amount,
            duration: this.offer.duration,
            durationInMonths: this.offer.durationInMonths,
            currency: this.offer.currency || this.defaultProps?.currency,
            status: this.offer.currency,
            tierId: this.offer?.tier?.id || this.defaultProps?.tier?.id
        }));

        if (forceRefresh) {
            this.portalPreviewGuid = Date.now().valueOf();
        }
        newUrl.searchParams.set('v', `${this.portalPreviewGuid}`);

        this.portalPreviewUrl = newUrl;
    }

    @action
    save() {
        return this.saveTask.perform();
    }

    @action
    leaveScreen() {
        this.offer.rollbackAttributes();
        return this.leaveScreenTransition.retry();
    }

    @action
    toggleUnsavedChangesModal(transition) {
        let leaveTransition = this.leaveScreenTransition;

        if (!transition && this.showUnsavedChangesModal) {
            this.leaveScreenTransition = null;
            this.showUnsavedChangesModal = false;
            return;
        }

        if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
            this.leaveScreenTransition = transition;

            // if a save is running, wait for it to finish then transition
            if (this.save.isRunning) {
                return this.save.last.then(() => {
                    transition.retry();
                });
            }

            // we genuinely have unsaved data, show the modal
            this.showUnsavedChangesModal = true;
        }
    }

    @action
    setup() {
        this.fetchProducts.perform();
    }

    @action
    setProperty(propKey, value) {
        this._saveOfferProperty(propKey, value);
    }

    @action
    setDiscountType(discountType) {
        if (!this.isDiscountSectionDisabled) {
            const amount = this.offer.amount || 0;
            this._saveOfferProperty('type', discountType);
            if (this.offer.type === 'fixed' && this.offer.amount !== '') {
                this.offer.amount = amount * 100;
            } else if (this.offer.amount !== '') {
                this.offer.amount = amount / 100;
            }
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    setDiscountAmount(e) {
        let amount = e.target.value;
        if (this.offer.type === 'fixed' && amount !== '') {
            amount = parseFloat(amount) * 100;
        }
        this._saveOfferProperty('amount', amount);
    }

    @action
    setOfferName(e) {
        this._saveOfferProperty('name', e.target.value);
        if (!this.isDisplayTitleEdited && this.offer.isNew) {
            this._saveOfferProperty('displayTitle', e.target.value);
        }
    }

    @action
    setPortalTitle(e) {
        this.isDisplayTitleEdited = true;
        this._saveOfferProperty('displayTitle', e.target.value);
    }

    @action
    setPortalDescription(e) {
        this._saveOfferProperty('displayDescription', e.target.value);
    }

    @action
    setOfferCode(e) {
        this._saveOfferProperty('code', e.target.value);
    }

    @action
    setDurationInMonths(e) {
        this._saveOfferProperty('durationInMonths', e.target.value);
    }

    @action
    openConfirmArchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open('modals/archive-offer', {
                offer: this.offer
            }, {
                className: 'fullscreen-modal fullscreen-modal-action fullscreen-modal-wide'
            });
        }
    }

    @action
    openConfirmUnarchiveModal() {
        if (!this.offer.isNew) {
            this.modals.open('modals/unarchive-offer', {
                offer: this.offer
            }, {
                className: 'fullscreen-modal fullscreen-modal-action fullscreen-modal-wide'
            });
        }
    }

    get offerUrl() {
        const code = this.offer?.code || '';
        if (code) {
            const siteUrl = this.config.get('blogUrl');
            return `${siteUrl}/${slugify(code)}`;
        }
        return '';
    }

    get displayCurrency() {
        const tierId = this.offer?.tier?.id;
        if (!tierId) {
            return '$';
        }
        const product = this.products.findBy('id', tierId);
        const productCurrency = product?.monthlyPrice?.currency || 'usd';
        return getSymbol(productCurrency);
    }

    get currencyLength() {
        return this.displayCurrency.length;
    }

    @action
    updateDurations(cadence) {
        if (cadence) {
            if (cadence === 'month') {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Multiple-months',
                        duration: 'repeating'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
            } else {
                this.durations = [
                    {
                        label: 'First-payment',
                        duration: 'once'
                    },
                    {
                        label: 'Forever',
                        duration: 'forever'
                    }
                ];
                if (this.offer.duration === 'repeating') {
                    this._saveOfferProperty('duration', 'once');
                }
            }
        }
    }

    @action
    updateCadence(cadence, offerObj) {
        offerObj = offerObj || this.offer;
        if (cadence) {
            const [tierId, tierCadence, currency] = cadence.split('-');
            offerObj.tier = {
                id: tierId
            };
            offerObj.cadence = tierCadence;
            offerObj.currency = currency;
            this.offertypes = [
                {
                    label: '%',
                    offertype: 'percent'
                },
                {
                    label: currency.toUpperCase(),
                    offertype: 'fixed'
                }
            ];
            this.updateDurations(tierCadence);
            this.updatePortalPreview({forceRefresh: false});
        }
    }

    @action
    updateDuration(duration) {
        this._saveOfferProperty('duration', duration);
    }

    // Private -----------------------------------------------------------------

    _saveOfferProperty(propKey, newValue) {
        let currentValue = this.offer[propKey];

        // avoid modifying empty values and triggering inadvertant unsaved changes modals
        if (newValue !== false && !newValue && !currentValue) {
            return;
        }

        this.offer[propKey] = newValue;
        this.updatePortalPreview({forceRefresh: false});
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 * Copyright (c) 2016 Heat Ledger Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * */

const electron = require('electron')
const path = require("path")
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const spawn = require('child_process').spawn;
const APP_DIR = path.join(__dirname,'..')

// 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 mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    icon:`${APP_DIR}/electron/icon.png`
  })
  mainWindow.loadURL(`file://${APP_DIR}/index.html`)
  // mainWindow.webContents.openDevTools()

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

app.on('ready', createWindow)

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

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

var run = function(args, done) {
  var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
  spawn(updateExe, args, {
    detached: true
  }).on('close', done);
};

var check = function() {
  if (process.platform === 'win32') {
    var cmd = process.argv[1];
    var target = path.basename(process.execPath);
    if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
      run(['--createShortcut=' + target + ''], app.quit);
      return true;
    }
    if (cmd === '--squirrel-uninstall') {
      run(['--removeShortcut=' + target + ''], app.quit);
      return true;
    }
    if (cmd === '--squirrel-obsolete') {
      app.quit();
      return true;
    }
  }
  return false;
};

module.exports = check();<commit_msg>We have that in the main menu<commit_after>/*
 * The MIT License (MIT)
 * Copyright (c) 2016 Heat Ledger Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 * */

const electron = require('electron')
const path = require("path")
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const spawn = require('child_process').spawn;
const APP_DIR = path.join(__dirname,'..')

// 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 mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    icon:`${APP_DIR}/electron/icon.png`
  })
  mainWindow.loadURL(`file://${APP_DIR}/index.html`)

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

app.on('ready', createWindow)

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

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

var run = function(args, done) {
  var updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe');
  spawn(updateExe, args, {
    detached: true
  }).on('close', done);
};

var check = function() {
  if (process.platform === 'win32') {
    var cmd = process.argv[1];
    var target = path.basename(process.execPath);
    if (cmd === '--squirrel-install' || cmd === '--squirrel-updated') {
      run(['--createShortcut=' + target + ''], app.quit);
      return true;
    }
    if (cmd === '--squirrel-uninstall') {
      run(['--removeShortcut=' + target + ''], app.quit);
      return true;
    }
    if (cmd === '--squirrel-obsolete') {
      app.quit();
      return true;
    }
  }
  return false;
};

module.exports = check();<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import StatusBar from './components/StatusBar'

function mapStateToProps(state) {
  return {
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({}, dispatch)
}

class HomeScreenPage extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (

      <StatusBar hideBackToHomeLink={true} />

Hello, Blockstack

Forum

To Do List

Guild

Ongaku Ryoho

Casa

OpenBazaar

Beacon

Beacon

    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomeScreenPage)
<commit_msg>update guild url<commit_after>import React, { Component } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import { Link } from 'react-router'
import StatusBar from './components/StatusBar'

function mapStateToProps(state) {
  return {
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({}, dispatch)
}

class HomeScreenPage extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (

      <StatusBar hideBackToHomeLink={true} />

Hello, Blockstack

Forum

To Do List

Guild

Ongaku Ryoho

Casa

OpenBazaar

Beacon

Beacon

    )
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(HomeScreenPage)
<|endoftext|>


<commit_before>var fs = require('fs');
const storage = require('electron-json-storage');
var path = require('path');
var app = require('electron').remote;
var moment = require('moment');
var async = require('async');
var dialog = app.dialog;
var journalDirectory = "";
var dialogAnswer = "";

// TODO: remove the use of the first RegExp

var datePattern = new RegExp(/^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])/);

var datePatternWithExt = new RegExp(/^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])\.txt/);

function checkIfDirectoryIsClean(dir) {
  // TODO: Error if there's no journal here!!!
  var entryList = fs.readdirSync(dir);
  for (var i=0; i < entryList.length; i++) {
    // entryList[i] = entryList[i].replace(/\.[^/.]+$/, "");
    if (datePatternWithExt.test(entryList[i]) === false) {
      return false;
      break;
    }
  }
}

function chooseDirectory() {
  dialogAnswer =  dialog.showOpenDialog(
    { properties: [ 'openDirectory' ] }
  );
  if (typeof dialogAnswer == 'object') {
    if (checkIfDirectoryIsClean(dialogAnswer[0].toString()) === false) {
      document.getElementById("nonEmptyAlertSpace").innerHTML = "
 <button type=button class=close data-dismiss=alert aria-label=Close>×</button>
Are you sure you want that directoy?

It looks like you've chosen a directory,  that already has other kinds of files in it. This can still work, but things could get messy for you because you will have a whole bunch of journal entries mixed in with your other files. We would strongly recommend you create and choose a new, empty directory to hold your journal.

 <button type=button class='btn btn-danger' data-dismiss='alert' onclick='chooseDirectory()'>Choose Another Directory</button> <button type=button class='btn btn-default' data-dismiss='alert' onclick='chooseDirectoryApplied(dialogAnswer)'>I understand, it's ok</button>

"
      document.getElementById("directory1").innerHTML = dialogAnswer[0];
    }
    else {
      chooseDirectoryApplied(dialogAnswer);
    }
  }
}

function chooseDirectoryApplied(directoryChosen) {
  journalDirectory = directoryChosen[0];

  storage.set('journalPath', { journalPath: journalDirectory }, function(error) {
    if (error) throw error;
    document.getElementById("journal-directory").innerHTML = journalDirectory;
    document.getElementById("journal-directory2").innerHTML = journalDirectory;
    refreshCalendar();
  });
}

function initialChooseDirectory() {
  dialogAnswer =  dialog.showOpenDialog(
    { properties: [ 'openDirectory' ] }
  );
  if (typeof dialogAnswer == 'object') {
    if (checkIfDirectoryIsClean(dialogAnswer[0].toString()) === false) {
      document.getElementById("nonEmptyAlertSpaceStart").innerHTML = "
 <button type=button class=close data-dismiss=alert aria-label=Close>×</button>
Are you sure you want that directoy?

It looks like you've chosen a directory,  that already has other kinds of files in it. This will still work, but things could get messy for you because you will have a whole bunch of journal entries mixed in with your other files. We would strongly recommend you create and choose a new, empty directory to hold your journal.

 <button type=button class='btn btn-danger' data-dismiss='alert' onclick='initialChooseDirectory()'>Choose Another Directory</button> <button type=button class='btn btn-default' data-dismiss='alert' onclick='$(\x22#introModal\x22).modal(\x22hide\x22);chooseDirectoryApplied(dialogAnswer);alert(journalDirectory);'>I understand, it's ok</button>

"
      document.getElementById("directory2").innerHTML = dialogAnswer[0];
    }
    else {
      $('#introModal').modal('hide')
      document.getElementById("textArea").focus();
      chooseDirectoryApplied(dialogAnswer);
    }
  }
}

function getEntryList() {
  // TODO: Error if there's no journal here!!!
  var entryList = fs.readdirSync(journalDirectory);
  for (var i=0; i < entryList.length; i++) {
    entryList[i] = entryList[i].replace(/\.[^/.]+$/, "");
    if (datePattern.test(entryList[i]) === false) {
      entryList.splice(i, 1);
      i--;
    }
  }
  return entryList;
}

function readEntry(filename) {
  return fs.readFileSync(path.join(journalDirectory, filename), 'utf8');
}

function saveEntry(filename, content) {
  fs.writeFile(path.join(journalDirectory, filename), content, (err, fd) => {
    if (err) {
      document.getElementById("below-text").innerHTML = "ERROR: Unable to save " + journalDirectory + currentFileOpen;
    }
    else {
      document.getElementById("below-text").innerHTML = journalDirectory + currentFileOpen + " saved";
      setTimeout(restingStatusText, 3500);
      refreshCalendar();
    }
  });
}

function deleteEntry(filename) {
  fs.unlink(path.join(journalDirectory, filename), (err, fd) => {
    if (err) {
      ;
    }
    else {
      document.getElementById("below-text").innerHTML = journalDirectory + currentFileOpen + " deleted";
      setTimeout(restingStatusText, 3500);
      refreshCalendar();
    }
  });
}

function restingStatusText () {
  document.getElementById("below-text").innerHTML = currentFileOpen + ' - CTRL+S to Save';
}

function getIndicesOf(searchStr, str) {
    var searchStrLen = searchStr.length;
    if (searchStrLen == 0) {
        return [];
    }
    var startIndex = 0, index, indices = [];
    str = str.toLowerCase();
    searchStr = searchStr.toLowerCase();
    while ((index = str.indexOf(searchStr, startIndex)) > -1) {
        indices.push(index);
        startIndex = index + searchStrLen;
    }
    return indices;
}

function printSearchResults(file, buffer, indices, searchLength) {
  // var buffer = buffer.toString();
  var snippetLength = 72;
  var halfSnippet = snippetLength / 2;
  var insertedFile = "'" + file + "'";
  var niceDate = moment(file).format('MMMM D[,] YYYY');
  function printOut(html) {
    document.getElementById("searchResultsDisplay").insertAdjacentHTML("beforeend", html);
  }
  printOut("
" + niceDate + "
");
  for (var i=0; i < indices.length; i++) {
    if (indices[i] < snippetLength && indices[i] !== 0) {
      printOut("" + buffer.substr(0, indices[i]) + "" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, snippetLength-indices[i]) + "");
    }
    else if (indices[i] == 0) {
      printOut("" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, snippetLength) + "");
    }
    else {
      printOut("" + buffer.substr(indices[i] - halfSnippet, halfSnippet) + "" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, halfSnippet) + "");
    }
  }
}

function printEmptySearchResults() {
  function printOut(html) {
    document.getElementById("searchResultsBody").insertAdjacentHTML("beforeend", html);
  }
  printOut("
Nothing found...
");
}

function searchJournal() {
  var searchString;
  var matchCount = 0;
  var returnedResult = false
  if (document.getElementById("searchArea").value !== "") {
    document.getElementById("searchResultsBody").innerHTML = "

";
    $('#searchModal').modal('show');
    searchString = document.getElementById("searchArea").value.toLowerCase();
    searchStrLen = searchString.length;
    // Loop through list of files (getEntryList)
    var entryList = getEntryList();
    // TODO: find a way to see if it doesn't return any results using the commented async version.... async issue
    // for (let file of entryList) {
    //   fs.readFile(path.join(journalDirectory, (file + ".txt")), function (err, data) {
    //     if (err) throw err;
    //       var startIndex = 0, index, indices = [];
    //       data = data.toString().toLowerCase();
    //       while ((index = data.indexOf(searchString, startIndex)) > -1) {
    //           indices.push(index);
    //           startIndex = index + searchStrLen;
    //       }
    //       if (indices.length != 0) {
    //         printSearchResults(file, data, indices, searchString.length);
    //         returnedResult = true;
    //       }
    //   });
    for (let file of entryList) {
      var data = fs.readFileSync(path.join(journalDirectory, (file + ".txt")));
      var startIndex = 0, index, indices = [];
      data = data.toString().toLowerCase();
      while ((index = data.indexOf(searchString, startIndex)) > -1) {
          indices.push(index);
          startIndex = index + searchStrLen;
      }
      if (indices.length != 0) {
        printSearchResults(file, data, indices, searchString.length);
        returnedResult = true;
      }
    }
    if (returnedResult === false) {
      printEmptySearchResults();
    }
  }
}

function updateCheck() {
  require('electron').shell.openExternal('http://www.adamdueck.com/journee/');
}

<commit_msg>Update file-interface.js<commit_after>var fs = require('fs');
const storage = require('electron-json-storage');
var path = require('path');
var app = require('electron').remote;
var moment = require('moment');
var async = require('async');
var dialog = app.dialog;
var journalDirectory = "";
var dialogAnswer = "";

// TODO: remove the use of the first RegExp

var datePattern = new RegExp(/^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])/);

var datePatternWithExt = new RegExp(/^(\d{4})\D?(0[1-9]|1[0-2])\D?([12]\d|0[1-9]|3[01])\.txt/);

function checkIfDirectoryIsClean(dir) {
  // TODO: Error if there's no journal here!!!
  var entryList = fs.readdirSync(dir);
  for (var i=0; i < entryList.length; i++) {
    // entryList[i] = entryList[i].replace(/\.[^/.]+$/, "");
    if (datePatternWithExt.test(entryList[i]) === false) {
      return false;
      break;
    }
  }
}

function chooseDirectory() {
  dialogAnswer =  dialog.showOpenDialog(
    { properties: [ 'openDirectory' ] }
  );
  if (typeof dialogAnswer == 'object') {
    if (checkIfDirectoryIsClean(dialogAnswer[0].toString()) === false) {
      document.getElementById("nonEmptyAlertSpace").innerHTML = "
 <button type=button class=close data-dismiss=alert aria-label=Close>×</button>
Are you sure you want that directoy?

It looks like you've chosen a directory,  that already has other kinds of files in it. This can still work, but things could get messy for you because you will have a whole bunch of journal entries mixed in with your other files. We would strongly recommend you create and choose a new, empty directory to hold your journal.

 <button type=button class='btn btn-danger' data-dismiss='alert' onclick='chooseDirectory()'>Choose Another Directory</button> <button type=button class='btn btn-default' data-dismiss='alert' onclick='chooseDirectoryApplied(dialogAnswer)'>I understand, it's ok</button>

"
      document.getElementById("directory1").innerHTML = dialogAnswer[0];
    }
    else {
      chooseDirectoryApplied(dialogAnswer);
    }
  }
}

function chooseDirectoryApplied(directoryChosen) {
  journalDirectory = directoryChosen[0];

  storage.set('journalPath', { journalPath: journalDirectory }, function(error) {
    if (error) throw error;
    document.getElementById("journal-directory").innerHTML = journalDirectory;
    document.getElementById("journal-directory2").innerHTML = journalDirectory;
    refreshCalendar();
  });
}

function initialChooseDirectory() {
  dialogAnswer =  dialog.showOpenDialog(
    { properties: [ 'openDirectory' ] }
  );
  if (typeof dialogAnswer == 'object') {
    if (checkIfDirectoryIsClean(dialogAnswer[0].toString()) === false) {
      document.getElementById("nonEmptyAlertSpaceStart").innerHTML = "
 <button type=button class=close data-dismiss=alert aria-label=Close>×</button>
Are you sure you want that directoy?

It looks like you've chosen a directory,  that already has other kinds of files in it. This will still work, but things could get messy for you because you will have a whole bunch of journal entries mixed in with your other files. We would strongly recommend you create and choose a new, empty directory to hold your journal.

 <button type=button class='btn btn-danger' data-dismiss='alert' onclick='initialChooseDirectory()'>Choose Another Directory</button> <button type=button class='btn btn-default' data-dismiss='alert' onclick='$(\x22#introModal\x22).modal(\x22hide\x22);chooseDirectoryApplied(dialogAnswer);alert(journalDirectory);'>I understand, it's ok</button>

"
      document.getElementById("directory2").innerHTML = dialogAnswer[0];
    }
    else {
      $('#introModal').modal('hide')
      document.getElementById("textArea").focus();
      chooseDirectoryApplied(dialogAnswer);
    }
  }
}

function getEntryList() {
  // TODO: Error if there's no journal here!!!
  var entryList = fs.readdirSync(journalDirectory);
  for (var i=0; i < entryList.length; i++) {
    entryList[i] = entryList[i].replace(/\.[^/.]+$/, "");
    if (datePattern.test(entryList[i]) === false) {
      entryList.splice(i, 1);
      i--;
    }
  }
  return entryList;
}

function readEntry(filename) {
  return fs.readFileSync(path.join(journalDirectory, filename), 'utf8');
}

function saveEntry(filename, content) {
  fs.writeFile(path.join(journalDirectory, filename), content, (err, fd) => {
    if (err) {
      document.getElementById("below-text").innerHTML = "ERROR: Unable to save " + journalDirectory + currentFileOpen;
    }
    else {
      document.getElementById("below-text").innerHTML = journalDirectory + currentFileOpen + " saved";
      setTimeout(restingStatusText, 3500);
      refreshCalendar();
    }
  });
}

function deleteEntry(filename) {
  fs.unlink(path.join(journalDirectory, filename), (err, fd) => {
    if (err) {
      ;
    }
    else {
      document.getElementById("below-text").innerHTML = journalDirectory + currentFileOpen + " deleted";
      setTimeout(restingStatusText, 3500);
      refreshCalendar();
    }
  });
}

function restingStatusText () {
  document.getElementById("below-text").innerHTML = currentFileOpen + ' - CTRL+S to Save';
}

function getIndicesOf(searchStr, str) {
    var searchStrLen = searchStr.length;
    if (searchStrLen == 0) {
        return [];
    }
    var startIndex = 0, index, indices = [];
    str = str.toLowerCase();
    searchStr = searchStr.toLowerCase();
    while ((index = str.indexOf(searchStr, startIndex)) > -1) {
        indices.push(index);
        startIndex = index + searchStrLen;
    }
    return indices;
}

function printSearchResults(file, buffer, indices, searchLength) {
  // var buffer = buffer.toString();
  var snippetLength = 72;
  var halfSnippet = snippetLength / 2;
  var insertedFile = "'" + file + "'";
  var niceDate = moment(file).format('MMMM D[,] YYYY');
  function printOut(html) {
    document.getElementById("searchResultsDisplay").insertAdjacentHTML("beforeend", html);
  }
  printOut("
" + niceDate + "
");
  for (var i=0; i < indices.length; i++) {
    if (indices[i] < snippetLength && indices[i] !== 0) {
      printOut("" + buffer.substr(0, indices[i]) + "" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, snippetLength-indices[i]) + "");
    }
    else if (indices[i] == 0) {
      printOut("" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, snippetLength) + "");
    }
    else {
      printOut("" + buffer.substr(indices[i] - halfSnippet, halfSnippet) + "" + buffer.substr(indices[i], searchLength) + "" + buffer.substr(indices[i] + searchLength, halfSnippet) + "");
    }
  }
}

function printEmptySearchResults() {
  function printOut(html) {
    document.getElementById("searchResultsBody").insertAdjacentHTML("beforeend", html);
  }
  printOut("
Nothing found...
");
}

function searchJournal() {
  var searchString;
  var matchCount = 0;
  var returnedResult = false
  if (document.getElementById("searchArea").value !== "") {
    document.getElementById("searchResultsBody").innerHTML = "

";
    $('#searchModal').modal('show');
    searchString = document.getElementById("searchArea").value.toLowerCase();
    searchStrLen = searchString.length;
    // Loop through list of files (getEntryList)
    var entryList = getEntryList();
    // TODO: find a way to see if it doesn't return any results using the commented async version.... async issue
    // for (let file of entryList) {
    //   fs.readFile(path.join(journalDirectory, (file + ".txt")), function (err, data) {
    //     if (err) throw err;
    //       var startIndex = 0, index, indices = [];
    //       data = data.toString().toLowerCase();
    //       while ((index = data.indexOf(searchString, startIndex)) > -1) {
    //           indices.push(index);
    //           startIndex = index + searchStrLen;
    //       }
    //       if (indices.length != 0) {
    //         printSearchResults(file, data, indices, searchString.length);
    //         returnedResult = true;
    //       }
    //   });
    for (let file of entryList) {
      var data = fs.readFileSync(path.join(journalDirectory, (file + ".txt")));
      var startIndex = 0, index, indices = [];
      data = data.toString().toLowerCase();
      while ((index = data.indexOf(searchString, startIndex)) > -1) {
          indices.push(index);
          startIndex = index + searchStrLen;
      }
      if (indices.length != 0) {
        printSearchResults(file, data, indices, searchString.length);
        returnedResult = true;
      }
    }
    if (returnedResult === false) {
      printEmptySearchResults();
    }
  }
}

function updateCheck() {
  require('electron').shell.openExternal('https://adueck.github.io/journee/');
}

<|endoftext|>


<commit_before>import lodash from 'lodash';
var _ = lodash;

import {
    Table,
    generate_headers,
    create_header_hopper,
    get_cursor_position,
    toggle_header,
    remove_hidden_from_table,
    remove_hidden_from_matrix
} from './utils.js';

export function generate_matrix_headers(table, headers, size) {

    _.forOwn(table.hopper, (hopper, key) => {
        hopper(true);
    });

    let data = [];
    for (var i=0; i < size; i++) {

        let obj = {hop:{}};
        let hop = null;

        headers.map((heading, index) => {

            let hopper = table.hopper[heading];
            let header = hopper();

            if (header) {
                hop = table.meta.hops[heading];
            }
            else {
                header = data[i - 1][heading];
                hop = null;
            }

            obj[heading] = header;
            obj['hop'][heading] = hop;
        });
        data.push(obj);
    }
    return data;
}

export function generate_hidden_check(headers, hiding_queries) {
    /*
    Generates a helper function that tells if a given header
    is hidden.
    hidden argument is a list of query objects, with header
    values set for any necessary headings:
        [{level1: value1}, {level2: value2}]
    Query is all inclusive, all matching objects are removed

    TODO: find out why trying to add to an array during reduce fails
    */

    let hidden_headers = _.flatten(
        hiding_queries.map((hide_query) => {
            return _.where(headers, hide_query);
        })
    );

    return (header) => {
        return _.where(hidden_headers, header).length > 0;
    }
}
<commit_msg>Added a function to get index positions for visible headers in the matrix<commit_after>import lodash from 'lodash';
var _ = lodash;

import {
    Table,
    generate_headers,
    create_header_hopper,
    get_cursor_position,
    toggle_header,
    remove_hidden_from_table,
    remove_hidden_from_matrix
} from './utils.js';

export function generate_matrix_headers(table, headers, size) {

    _.forOwn(table.hopper, (hopper, key) => {
        hopper(true);
    });

    let data = [];
    for (var i=0; i < size; i++) {

        let obj = {hop:{}};
        let hop = null;

        headers.map((heading, index) => {

            let hopper = table.hopper[heading];
            let header = hopper();

            if (header) {
                hop = table.meta.hops[heading];
            }
            else {
                header = data[i - 1][heading];
                hop = null;
            }

            obj[heading] = header;
            obj['hop'][heading] = hop;
        });
        data.push(obj);
    }
    return data;
}

export function generate_hidden_check(headers, hiding_queries) {
    /*
    Generates a helper function that tells if a given header
    is hidden.
    hidden argument is a list of query objects, with header
    values set for any necessary headings:
        [{level1: value1}, {level2: value2}]
    Query is all inclusive, all matching objects are removed

    TODO: find out why trying to add to an array during reduce fails
    */

    let hidden_headers = _.flatten(
        hiding_queries.map((hide_query) => {
            return _.where(headers, hide_query);
        })
    );

    return (header) => {
        return _.where(hidden_headers, header).length > 0;
    }
}

export function generate_hidden_index(original_headers, visible_headers) {
    /*
    Goes through all headers and returns their index position
    if the header is in visible_headers
    This means that for this index all cells on this point of the axis
    can be used in the matrix
     */
    return original_headers.map((header, index) => {
       if (visible_headers.indexOf(header) !== -1) {
           return index;
       }
    });
}<|endoftext|>


<commit_before>import { BrowserWindow, globalShortcut, app } from 'electron'
import EventEmitter from 'events'
import trackEvent from '../lib/trackEvent'

import {
  INPUT_HEIGHT,
  WINDOW_WIDTH
} from './constants/ui'

import buildMenu from './createWindow/buildMenu'
import toggleWindow from './createWindow/toggleWindow'
import handleUrl from './createWindow/handleUrl'
import config from '../lib/config'

export default ({ src, isDev }) => {
  const mainWindow = new BrowserWindow({
    alwaysOnTop: true,
    width: WINDOW_WIDTH,
    minWidth: WINDOW_WIDTH,
    height: INPUT_HEIGHT,
    frame: false,
    resizable: false,
    // Show main window on launch only when application started for the first time
    show: config.get('firstStart')
  })

  mainWindow.loadURL(src)
  mainWindow.settingsChanges = new EventEmitter()

  // Get global shortcut from app settings
  let shortcut = config.get('hotkey')

  // Function to toggle main window
  const toggleMainWindow = () => toggleWindow(mainWindow)
  // Function to show main window
  const showMainWindow = () => {
    mainWindow.show()
    mainWindow.focus()
  }

  // Setup event listeners for main window
  globalShortcut.register(shortcut, toggleMainWindow)

  mainWindow.on('blur', () => {
    if (!isDev()) {
      // Hide window on blur in production
      // In development we usually use developer tools that can blur a window
      mainWindow.hide()
    }
  })

  // Change global hotkey if it is changed in app settings
  mainWindow.settingsChanges.on('hotkey', (value) => {
    globalShortcut.unregister(shortcut)
    shortcut = value
    globalShortcut.register(shortcut, toggleMainWindow)
  })

  // Change theme css file
  mainWindow.settingsChanges.on('theme', (value) => {
    mainWindow.webContents.send('message', {
      message: 'updateTheme',
      payload: value
    })
  })

  // Show main window when user opens application, but it is already opened
  app.on('open-file', (event, path) => handleUrl(mainWindow, path))
  app.on('open-url', (event, path) => handleUrl(mainWindow, path))
  app.on('activate', showMainWindow)

  // Track app start event
  trackEvent({
    category: 'App Start',
    event: config.get('firstStart') ? 'First' : 'Secondary'
  })

  // Save in config information, that application has been started
  config.set('firstStart', false)

  buildMenu(mainWindow)
  return mainWindow
}
<commit_msg>Open links from previews in default browser instead of navigating in cerebro web view<commit_after>import { BrowserWindow, globalShortcut, app, shell } from 'electron'
import EventEmitter from 'events'
import trackEvent from '../lib/trackEvent'

import {
  INPUT_HEIGHT,
  WINDOW_WIDTH
} from './constants/ui'

import buildMenu from './createWindow/buildMenu'
import toggleWindow from './createWindow/toggleWindow'
import handleUrl from './createWindow/handleUrl'
import config from '../lib/config'

export default ({ src, isDev }) => {
  const mainWindow = new BrowserWindow({
    alwaysOnTop: true,
    width: WINDOW_WIDTH,
    minWidth: WINDOW_WIDTH,
    height: INPUT_HEIGHT,
    frame: false,
    resizable: false,
    // Show main window on launch only when application started for the first time
    show: config.get('firstStart')
  })

  mainWindow.loadURL(src)
  mainWindow.settingsChanges = new EventEmitter()

  // Get global shortcut from app settings
  let shortcut = config.get('hotkey')

  // Function to toggle main window
  const toggleMainWindow = () => toggleWindow(mainWindow)
  // Function to show main window
  const showMainWindow = () => {
    mainWindow.show()
    mainWindow.focus()
  }

  // Setup event listeners for main window
  globalShortcut.register(shortcut, toggleMainWindow)

  mainWindow.on('blur', () => {
    if (!isDev()) {
      // Hide window on blur in production
      // In development we usually use developer tools that can blur a window
      mainWindow.hide()
    }
  })

  mainWindow.webContents.on('new-window', (event, url) => {
    shell.openExternal(url)
    event.preventDefault()
  })

  mainWindow.webContents.on('will-navigate', (event, url) => {
    shell.openExternal(url)
    event.preventDefault()
  })

  // Change global hotkey if it is changed in app settings
  mainWindow.settingsChanges.on('hotkey', (value) => {
    globalShortcut.unregister(shortcut)
    shortcut = value
    globalShortcut.register(shortcut, toggleMainWindow)
  })

  // Change theme css file
  mainWindow.settingsChanges.on('theme', (value) => {
    mainWindow.webContents.send('message', {
      message: 'updateTheme',
      payload: value
    })
  })

  // Show main window when user opens application, but it is already opened
  app.on('open-file', (event, path) => handleUrl(mainWindow, path))
  app.on('open-url', (event, path) => handleUrl(mainWindow, path))
  app.on('activate', showMainWindow)

  // Track app start event
  trackEvent({
    category: 'App Start',
    event: config.get('firstStart') ? 'First' : 'Secondary'
  })

  // Save in config information, that application has been started
  config.set('firstStart', false)

  buildMenu(mainWindow)
  return mainWindow
}
<|endoftext|>