<commit_before>const { join, relative, resolve } = require('path')
const R = require('ramda')
const Interface = require('./interface')
const Connector = require('./connector')
const File = require('./util/file')
const PageUtil = require('./util/page')
const { dasherize, replaceTemplateComments } = require('./util/string')
const { error } = require('./util/message')
const { debug2, debug3, debug4 } = require('./util/debug')

const copyPageFile = (targetPath, sourcePath, source) => {
  const filePath = relative(sourcePath, source)
  const target = resolve(targetPath, filePath)

  return File.copy(source, target)
}

async function render (state, template, data, identifier) {
  debug4(state, `Builder.render(${template}):start`)

  const templatePath = join(state.config.source.templates, template)

  let rendered
  try {
    rendered = await Connector.render(state, templatePath, data)
  } catch (err) {
    const message = [error(`${identifier} could not be generated!`), err]

    if (state.config.debug) message.push(JSON.stringify(data, null, 2))

    throw new Error(message.join('\n\n'))
  }

  debug4(state, `Builder.render(${template}):end`)

  return rendered
}

export async function generatePageFiles (state, pageId) {
  debug4(state, `Builder.generatePageFiles(${pageId}):start`)

  const { pages, config } = state
  const page = pages[pageId]

  if (!page.files) return

  const targetPagePath = page.path
  const sourcePagePath = PageUtil.pageIdToPath(page.id)
  const targetPath = join(config.target, targetPagePath)
  const sourcePath = join(config.source.pages, sourcePagePath)
  const copyFile = R.partial(copyPageFile, [targetPath, sourcePath])
  const copyFiles = R.map(copyFile, page.files)

  await Promise.all(copyFiles)

  debug4(state, `Builder.generatePageFiles(${pageId}):start`)
}

export async function generatePageWithTemplate (state, pageId) {
  debug2(state, `Builder.generatePageWithTemplate(${pageId}):start`)

  const { pages, config: { target } } = state
  const identifier = `Page "${pageId}"`
  const page = pages[pageId]
  if (!page) throw new Error(`${identifier} does not exist or has not been fetched yet.`)

  if (page.template) {
    // render template with context
    const { id, context, template } = page
    const { rendered } = await render(state, template, context, identifier)

    // write file
    const filePath = resolve(target, '_pages', `${id}.html`)
    await File.write(filePath, rendered)
  }

  debug2(state, `Builder.generatePageWithTemplate(${pageId}):end`)
}

export async function generatePagesWithTemplate (state, template) {
  debug3(state, `Builder.generatePagesWithTemplate(${template}):start`)

  const affectedPages = R.filter(page => page.template === template, state.pages)
  const pageIds = Object.keys(affectedPages)
  const build = R.partial(generatePageWithTemplate, [state])
  const builds = R.map(build, pageIds)

  await Promise.all(builds)

  debug3(state, `Builder.generatePagesWithTemplate(${template}):end`)
}

export async function generatePageWithTokens (state, pageId) {
  debug2(state, `Builder.generatePageWithTokens(${pageId}):start`)

  const { pages, config } = state
  const { name, target, version } = config
  const identifier = `Page "${pageId}"`
  const page = pages[pageId]
  if (!page) throw new Error(`${identifier} does not exist or has not been fetched yet.`)

  if (PageUtil.isTokensPage(page.type)) {
    // render tokens with context, in preview layout
    const { id, title } = page
    const data = page
    const template = page.template || config.template
    let { rendered } = await render(state, template, data, identifier)
    rendered = replaceTemplateComments(rendered, {
      'content': await Interface.render(state, 'tokens', page),
      'class': `uie-tokens uie-tokens--${dasherize(id)}`,
      'title': `${title} • ${name} (${version})`
    })

    // write file
    const filePath = resolve(target, '_tokens', `${id}.html`)
    await File.write(filePath, rendered)
  }

  debug2(state, `Builder.generatePageWithTokens(${pageId}):end`)
}

export async function generateVariant (state, variant) {
  debug2(state, `Builder.generateVariant(${variant.id}):start`)

  const { config, components } = state
  const identifier = `Variant "${variant.id}"`
  const component = components[variant.componentId]

  // render variant preview, with layout
  const data = { state }
  const template = variant.template || config.template
  let { rendered } = await render(state, template, data, identifier)
  rendered = replaceTemplateComments(rendered, {
    'content': variant.rendered,
    'class': `uie-variant uie-variant--${dasherize(variant.componentId)} uie-variant--${dasherize(variant.id)}`,
    'title': `${component.title}: ${variant.title} • ${config.name} (${config.version})`
  })

  // write file
  const filePath = resolve(config.target, '_variants', `${variant.id}.html`)
  await File.write(filePath, rendered)

  debug2(state, `Builder.generateVariant(${variant.id}):end`)
}

export async function generateComponentVariants (state, componentId) {
  debug3(state, `Builder.generateComponentVariants(${componentId}):start`)

  const component = state.components[componentId]
  const variants = component.variants || []
  const build = R.partial(generateVariant, [state])
  const builds = R.map(build, variants)
  await Promise.all(builds)

  debug3(state, `Builder.generateComponentVariants(${componentId}):end`)
}

export async function generateVariantsWithTemplate (state, template) {
  debug3(state, `Builder.generateVariantsWithTemplate(${template}):start`)

  const components = Object.values(state.components)
  const isPreviewTemplate = template === state.config.template
  const affectedVariants = R.reduce((list, component) => {
    return R.concat(list, R.filter(variant => {
      // the variant must be regenerated in two cases:
      // 1.) the template is the general preview template and
      //     the variant does not use a custom template
      // 2.) the template matches the variant template
      return (isPreviewTemplate && variant.template === undefined) ||
        variant.template === template
    }, component.variants))
  }, [], components)
  const build = R.partial(generateVariant, [state])
  const builds = R.map(build, affectedVariants)

  await Promise.all(builds)

  debug3(state, `Builder.generateVariantsWithTemplate(${template}):end`)
}

export async function generateTokensWithTemplate (state, template) {
  debug3(state, `Builder.generateTokensWithTemplate(${template}):start`)

  const isPreviewTemplate = template === state.config.template
  const affectedPages = R.filter(page => {
    // the page must be regenerated in two cases:
    // 1.) the template is the general preview template and
    //     the variant does not use a custom template
    // 2.) the template matches the page template
    return PageUtil.isTokensPage(page.type) &&
      (
        (isPreviewTemplate && page.template === undefined) ||
        page.template === template
      )
  }, state.pages)
  const pageIds = Object.keys(affectedPages)
  const build = R.partial(generatePageWithTokens, [state])
  const builds = R.map(build, pageIds)

  await Promise.all(builds)

  debug3(state, `Builder.generateTokensWithTemplate(${template}):end`)
}

async function generateStateHTML (state) {
  debug3(state, 'Builder.generateStateHTML():start')

  const data = { state }
  const rendered = await Interface.render(state, 'index', data)
  const filePath = resolve(state.config.target, 'index.html')
  await File.write(filePath, rendered)

  debug3(state, 'Builder.generateStateHTML():end')
}

async function generateStateJSON (state) {
  debug3(state, 'Builder.generateStateJSON():start')

  const json = JSON.stringify(state, null, 2)
  const filePath = resolve(state.config.target, '_state.json')
  await File.write(filePath, json)

  debug3(state, 'Builder.generateStateJSON():end')
}

async function generateState (state) {
  debug2(state, 'Builder.generateState():start')

  const tasks = [generateStateHTML(state)]
  if (state.config.debug) tasks.push(generateStateJSON(state))
  await Promise.all(tasks)

  debug2(state, 'Builder.generateState():end')
}

// generateIncrement is a better name for the public function,
// whereas generateState describes it better internally
export const generateIncrement = generateState

async function generateSketch (state) {
  debug2(state, `Builder.generateSketch():start`)

  const { config: { name, target, template, version } } = state
  const identifier = 'HTML Sketchapp Export'

  // render variant preview, with layout
  const data = { state }
  let { rendered } = await render(state, template, data, identifier)
  rendered = replaceTemplateComments(rendered, {
    'content': await Interface.render(state, 'sketch', data),
    'class': 'uie-html-sketchapp',
    'title': `HTML Sketchapp Export • ${name} (${version})`
  })

  // write file
  const filePath = resolve(target, '_sketch.html')
  await File.write(filePath, rendered)

  debug2(state, `Builder.generateSketch():end`)
}

export async function generate (state) {
  debug2(state, 'Builder.generate():start')

  const pageIds = Object.keys(state.pages)
  const variants = R.reduce((list, component) => {
    return R.concat(list, component.variants)
  }, [], Object.values(state.components))

  const templateBuild = R.partial(generatePageWithTemplate, [state])
  const templateBuilds = R.map(templateBuild, pageIds)

  const tokenBuild = R.partial(generatePageWithTokens, [state])
  const tokenBuilds = R.map(tokenBuild, pageIds)

  const fileBuild = R.partial(generatePageFiles, [state])
  const fileBuilds = R.map(fileBuild, pageIds)

  const variantBuild = R.partial(generateVariant, [state])
  const variantBuilds = R.map(variantBuild, variants)

  await Promise.all([
    ...fileBuilds,
    ...tokenBuilds,
    ...variantBuilds,
    ...templateBuilds,
    generateSketch(state),
    generateState(state)
  ])

  debug2(state, 'Builder.generate():end')
}
<commit_msg>👉 skip generating sketch if template is missing<commit_after>const { join, relative, resolve } = require('path')
const R = require('ramda')
const Interface = require('./interface')
const Connector = require('./connector')
const File = require('./util/file')
const PageUtil = require('./util/page')
const { dasherize, replaceTemplateComments } = require('./util/string')
const { error } = require('./util/message')
const { debug2, debug3, debug4 } = require('./util/debug')

const copyPageFile = (targetPath, sourcePath, source) => {
  const filePath = relative(sourcePath, source)
  const target = resolve(targetPath, filePath)

  return File.copy(source, target)
}

async function render (state, template, data, identifier) {
  debug4(state, `Builder.render(${template}):start`)

  const templatePath = join(state.config.source.templates, template)

  let rendered
  try {
    rendered = await Connector.render(state, templatePath, data)
  } catch (err) {
    const message = [error(`${identifier} could not be generated!`), err]

    if (state.config.debug) message.push(JSON.stringify(data, null, 2))

    throw new Error(message.join('\n\n'))
  }

  debug4(state, `Builder.render(${template}):end`)

  return rendered
}

export async function generatePageFiles (state, pageId) {
  debug4(state, `Builder.generatePageFiles(${pageId}):start`)

  const { pages, config } = state
  const page = pages[pageId]

  if (!page.files) return

  const targetPagePath = page.path
  const sourcePagePath = PageUtil.pageIdToPath(page.id)
  const targetPath = join(config.target, targetPagePath)
  const sourcePath = join(config.source.pages, sourcePagePath)
  const copyFile = R.partial(copyPageFile, [targetPath, sourcePath])
  const copyFiles = R.map(copyFile, page.files)

  await Promise.all(copyFiles)

  debug4(state, `Builder.generatePageFiles(${pageId}):start`)
}

export async function generatePageWithTemplate (state, pageId) {
  debug2(state, `Builder.generatePageWithTemplate(${pageId}):start`)

  const { pages, config: { target } } = state
  const identifier = `Page "${pageId}"`
  const page = pages[pageId]
  if (!page) throw new Error(`${identifier} does not exist or has not been fetched yet.`)

  if (page.template) {
    // render template with context
    const { id, context, template } = page
    const { rendered } = await render(state, template, context, identifier)

    // write file
    const filePath = resolve(target, '_pages', `${id}.html`)
    await File.write(filePath, rendered)
  }

  debug2(state, `Builder.generatePageWithTemplate(${pageId}):end`)
}

export async function generatePagesWithTemplate (state, template) {
  debug3(state, `Builder.generatePagesWithTemplate(${template}):start`)

  const affectedPages = R.filter(page => page.template === template, state.pages)
  const pageIds = Object.keys(affectedPages)
  const build = R.partial(generatePageWithTemplate, [state])
  const builds = R.map(build, pageIds)

  await Promise.all(builds)

  debug3(state, `Builder.generatePagesWithTemplate(${template}):end`)
}

export async function generatePageWithTokens (state, pageId) {
  debug2(state, `Builder.generatePageWithTokens(${pageId}):start`)

  const { pages, config } = state
  const { name, target, version } = config
  const identifier = `Page "${pageId}"`
  const page = pages[pageId]
  if (!page) throw new Error(`${identifier} does not exist or has not been fetched yet.`)

  if (PageUtil.isTokensPage(page.type)) {
    // render tokens with context, in preview layout
    const { id, title } = page
    const data = page
    const template = page.template || config.template
    let { rendered } = await render(state, template, data, identifier)
    rendered = replaceTemplateComments(rendered, {
      'content': await Interface.render(state, 'tokens', page),
      'class': `uie-tokens uie-tokens--${dasherize(id)}`,
      'title': `${title} • ${name} (${version})`
    })

    // write file
    const filePath = resolve(target, '_tokens', `${id}.html`)
    await File.write(filePath, rendered)
  }

  debug2(state, `Builder.generatePageWithTokens(${pageId}):end`)
}

export async function generateVariant (state, variant) {
  debug2(state, `Builder.generateVariant(${variant.id}):start`)

  const { config, components } = state
  const identifier = `Variant "${variant.id}"`
  const component = components[variant.componentId]

  // render variant preview, with layout
  const data = { state }
  const template = variant.template || config.template
  let { rendered } = await render(state, template, data, identifier)
  rendered = replaceTemplateComments(rendered, {
    'content': variant.rendered,
    'class': `uie-variant uie-variant--${dasherize(variant.componentId)} uie-variant--${dasherize(variant.id)}`,
    'title': `${component.title}: ${variant.title} • ${config.name} (${config.version})`
  })

  // write file
  const filePath = resolve(config.target, '_variants', `${variant.id}.html`)
  await File.write(filePath, rendered)

  debug2(state, `Builder.generateVariant(${variant.id}):end`)
}

export async function generateComponentVariants (state, componentId) {
  debug3(state, `Builder.generateComponentVariants(${componentId}):start`)

  const component = state.components[componentId]
  const variants = component.variants || []
  const build = R.partial(generateVariant, [state])
  const builds = R.map(build, variants)
  await Promise.all(builds)

  debug3(state, `Builder.generateComponentVariants(${componentId}):end`)
}

export async function generateVariantsWithTemplate (state, template) {
  debug3(state, `Builder.generateVariantsWithTemplate(${template}):start`)

  const components = Object.values(state.components)
  const isPreviewTemplate = template === state.config.template
  const affectedVariants = R.reduce((list, component) => {
    return R.concat(list, R.filter(variant => {
      // the variant must be regenerated in two cases:
      // 1.) the template is the general preview template and
      //     the variant does not use a custom template
      // 2.) the template matches the variant template
      return (isPreviewTemplate && variant.template === undefined) ||
        variant.template === template
    }, component.variants))
  }, [], components)
  const build = R.partial(generateVariant, [state])
  const builds = R.map(build, affectedVariants)

  await Promise.all(builds)

  debug3(state, `Builder.generateVariantsWithTemplate(${template}):end`)
}

export async function generateTokensWithTemplate (state, template) {
  debug3(state, `Builder.generateTokensWithTemplate(${template}):start`)

  const isPreviewTemplate = template === state.config.template
  const affectedPages = R.filter(page => {
    // the page must be regenerated in two cases:
    // 1.) the template is the general preview template and
    //     the variant does not use a custom template
    // 2.) the template matches the page template
    return PageUtil.isTokensPage(page.type) &&
      (
        (isPreviewTemplate && page.template === undefined) ||
        page.template === template
      )
  }, state.pages)
  const pageIds = Object.keys(affectedPages)
  const build = R.partial(generatePageWithTokens, [state])
  const builds = R.map(build, pageIds)

  await Promise.all(builds)

  debug3(state, `Builder.generateTokensWithTemplate(${template}):end`)
}

async function generateStateHTML (state) {
  debug3(state, 'Builder.generateStateHTML():start')

  const data = { state }
  const rendered = await Interface.render(state, 'index', data)
  const filePath = resolve(state.config.target, 'index.html')
  await File.write(filePath, rendered)

  debug3(state, 'Builder.generateStateHTML():end')
}

async function generateStateJSON (state) {
  debug3(state, 'Builder.generateStateJSON():start')

  const json = JSON.stringify(state, null, 2)
  const filePath = resolve(state.config.target, '_state.json')
  await File.write(filePath, json)

  debug3(state, 'Builder.generateStateJSON():end')
}

async function generateState (state) {
  debug2(state, 'Builder.generateState():start')

  const tasks = [generateStateHTML(state)]
  if (state.config.debug) tasks.push(generateStateJSON(state))
  await Promise.all(tasks)

  debug2(state, 'Builder.generateState():end')
}

// generateIncrement is a better name for the public function,
// whereas generateState describes it better internally
export const generateIncrement = generateState

async function generateSketch (state) {
  debug2(state, `Builder.generateSketch():start`)

  const { config: { name, target, template, version } } = state
  const identifier = 'HTML Sketchapp Export'

  if (template) {
    // render variant preview, with layout
    const data = { state }
    let { rendered } = await render(state, template, data, identifier)
    rendered = replaceTemplateComments(rendered, {
      'content': await Interface.render(state, 'sketch', data),
      'class': 'uie-html-sketchapp',
      'title': `HTML Sketchapp Export • ${name} (${version})`
    })

    // write file
    const filePath = resolve(target, '_sketch.html')
    await File.write(filePath, rendered)
  }

  debug2(state, `Builder.generateSketch():end`)
}

export async function generate (state) {
  debug2(state, 'Builder.generate():start')

  const pageIds = Object.keys(state.pages)
  const variants = R.reduce((list, component) => {
    return R.concat(list, component.variants)
  }, [], Object.values(state.components))

  const templateBuild = R.partial(generatePageWithTemplate, [state])
  const templateBuilds = R.map(templateBuild, pageIds)

  const tokenBuild = R.partial(generatePageWithTokens, [state])
  const tokenBuilds = R.map(tokenBuild, pageIds)

  const fileBuild = R.partial(generatePageFiles, [state])
  const fileBuilds = R.map(fileBuild, pageIds)

  const variantBuild = R.partial(generateVariant, [state])
  const variantBuilds = R.map(variantBuild, variants)

  await Promise.all([
    ...fileBuilds,
    ...tokenBuilds,
    ...variantBuilds,
    ...templateBuilds,
    generateSketch(state),
    generateState(state)
  ])

  debug2(state, 'Builder.generate():end')
}
<|endoftext|>


<commit_before>// Structure: A structure on a Tile.

const Class = require("classe");
const log = require(`${__basedir}/gameplay/log`);
const GameObject = require("./gameObject");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

// any additional requires you want can be required here safely between Creer re-runs

//<<-- /Creer-Merge: requires -->>

// @class Structure: A structure on a Tile.
let Structure = Class(GameObject, {
    /**
     * Initializes Structures.
     *
     * @param {Object} data - a simple mapping passed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        GameObject.init.apply(this, arguments);

        /**
         * The range of this Structure's effect. For example, a radius of 1 means this Structure affects a 3x3 square centered on this Structure.
         *
         * @type {number}
         */
        this.effectRadius = this.effectRadius || 0;

        /**
         * The number of materials in this Structure. Once this number reaches 0, this Structure is destroyed.
         *
         * @type {number}
         */
        this.materials = this.materials || 0;

        /**
         * The owner of this Structure if any, otherwise null.
         *
         * @type {Player}
         */
        this.owner = this.owner || null;

        /**
         * The Tile this Structure is on.
         *
         * @type {Tile}
         */
        this.tile = this.tile || null;

        /**
         * The type of Structure this is ('shelter', 'monument', 'wall', 'road', 'neutral').
         *
         * @type {string}
         */
        this.type = this.type || "";

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        this.owner = data.owner || null;
        this.tile = data.tile || null;
        this.type = data.type || "neutral";
        this.type = this.type.toLowerCase();

        this.materials = this.game.shelterCost(this.type) || 0;
        this.effectRadius = this.game.shelterRange(this.type) || 0;

        this.game.newStructures.push(this);
        //<<-- /Creer-Merge: init -->>
    },

    gameObjectName: "Structure",

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    // You can add additional functions here. These functions will not be directly callable by client AIs

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Structure;
<commit_msg>Fixed game-breaking typo<commit_after>// Structure: A structure on a Tile.

const Class = require("classe");
const log = require(`${__basedir}/gameplay/log`);
const GameObject = require("./gameObject");

//<<-- Creer-Merge: requires -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

// any additional requires you want can be required here safely between Creer re-runs

//<<-- /Creer-Merge: requires -->>

// @class Structure: A structure on a Tile.
let Structure = Class(GameObject, {
    /**
     * Initializes Structures.
     *
     * @param {Object} data - a simple mapping passed in to the constructor with whatever you sent with it. GameSettings are in here by key/value as well.
     */
    init: function(data) {
        GameObject.init.apply(this, arguments);

        /**
         * The range of this Structure's effect. For example, a radius of 1 means this Structure affects a 3x3 square centered on this Structure.
         *
         * @type {number}
         */
        this.effectRadius = this.effectRadius || 0;

        /**
         * The number of materials in this Structure. Once this number reaches 0, this Structure is destroyed.
         *
         * @type {number}
         */
        this.materials = this.materials || 0;

        /**
         * The owner of this Structure if any, otherwise null.
         *
         * @type {Player}
         */
        this.owner = this.owner || null;

        /**
         * The Tile this Structure is on.
         *
         * @type {Tile}
         */
        this.tile = this.tile || null;

        /**
         * The type of Structure this is ('shelter', 'monument', 'wall', 'road', 'neutral').
         *
         * @type {string}
         */
        this.type = this.type || "";

        //<<-- Creer-Merge: init -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.
        this.owner = data.owner || null;
        this.tile = data.tile || null;
        this.type = data.type || "neutral";
        this.type = this.type.toLowerCase();

        this.materials = this.game.structureCost(this.type) || 0;
        this.effectRadius = this.game.structureRange(this.type) || 0;

        this.game.newStructures.push(this);
        //<<-- /Creer-Merge: init -->>
    },

    gameObjectName: "Structure",

    //<<-- Creer-Merge: added-functions -->> - Code you add between this comment and the end comment will be preserved between Creer re-runs.

    // You can add additional functions here. These functions will not be directly callable by client AIs

    //<<-- /Creer-Merge: added-functions -->>

});

module.exports = Structure;
<|endoftext|>


<commit_before>var Simulation = {
    initialize : function() {
        // Init rotors
        this.rotors = [];
        for (var i = 0; i < 3; i++) {
            this.rotors[i] = new Rotor(i);
            this.rotors[i].set_wiring();
        }

        // Create key listener
        document.onkeydown = function(event) {
            Simulation.key_press_handler(String.fromCharCode(event.keyCode));
        };

        // Init display
        Display.initialize();
        Display.update();
    },

    encrypt : function(key) {
        // Perform the encryption
        var current = key;

        // Advance rotors
        for (var i = 0; i < 3; i++) {
            this.rotors[i].advance();
        }

        // Do the substitution through the rotors
        for (var i = 0; i < 3; i++) {
            current = this.rotors[i].sub_letter(current);
        }

        // Find letter after reflector rotor;
        current = rotor_reflector[Letter.to_pin(current) - 1];

        // Do the substitution back through the rotors
        for (var i = 2; i >= 0; i--) {
            current = this.rotors[i].rsub_letter(current);
        }

        return current;
    },

    key_press_handler : function(key) {
        // Check if key is in acceptable range
        if (key.charCodeAt() < 65 || key.charCodeAt() > 90)
            return;

        // Set current key
        this.ckey = key;
        // Set current ciphertext key
        this.cct = Simulation.encrypt(key);

        // Add to text panes
        Display.add_to_text();
        // Update display
        Display.update();
    },

    rotor_set_listener : function(event) {
        // Function to call when rotor up or down clicked
        var rtr = event.target.parentNode.id.charAt(6) - 1;
        Simulation.rotors[rtr].change_init_pos((event.target.className == "up") ? -1 : 1);
        Display.update();
    }
};

var Display = {
    initialize : function() {
        this.rotor_disp = [];

        // Create rotor display
        for (var i = 1; i < 4; i++) {

            // Create rotor div
            var parent_d = document.createElement("div");
            parent_d.className = "rotor";
            parent_d.id = "rotor_" + i;
            document.getElementById("rotor_pane").appendChild(parent_d);

            // Create up arrow
            var up_d = document.createElement("div");
            up_d.innerHTML = '↑';
            up_d.className = "up";
            up_d.onclick = Simulation.rotor_set_listener;
            parent_d.appendChild(up_d);

            // Create rotor disp
            var rotor_d = document.createElement("div");
            this.rotor_disp[i - 1] = rotor_d;
            parent_d.appendChild(rotor_d);

            // Create down arrow
            var down_d = document.createElement("div");
            down_d.innerHTML = '↓';
            down_d.className = "down";
            down_d.onclick = Simulation.rotor_set_listener;
            parent_d.appendChild(down_d);

        }

        // Create keys display
        for (var i = 0; i < 26; i++) {
            var key = document.createElement("div");
            var letter = String.fromCharCode(65 + i);
            key.id = "key_" + letter;
            key.innerHTML = letter;
            key.onclick = function(event) {
                Simulation.key_press_handler(event.target.id.charAt(4));
            }
            document.getElementById("key_pane").appendChild(key);
        }
    },

    add_to_text : function() {
        // Add text to text panes
        document.getElementById("tp_pt").innerHTML =
            document.getElementById("tp_pt").innerHTML + " " + Simulation.ckey;
        document.getElementById("tp_ct").innerHTML =
            document.getElementById("tp_ct").innerHTML + " " + Simulation.cct;
    },

    update : function() {
        // Update rotor letters
        for (var i = 0; i < 3; i++)
            this.rotor_disp[i].innerHTML = Simulation.rotors[i].pos;

        // Remove old highlights
        var prevpt = document.getElementsByClassName("pthighlight");
        if (prevpt[0] != undefined)
            prevpt[0].classList.remove("pthighlight");
        var prevct = document.getElementsByClassName("cthighlight");
        if (prevct[0] != undefined)
            prevct[0].classList.remove("cthighlight");

        // Highlight plaintext
        document.getElementById("key_" + Simulation.ckey).classList.add("pthighlight");

        // Highlight ciphertext
        document.getElementById("key_" + Simulation.cct).classList.add("cthighlight");
    }
};

var Letter = {
    add : function(letter, change_by) {
        // Add to letter
        var changed = letter + change_by;
        if (changed > 26)
            changed = 0 + (changed - 26);
        else if (changed < 1)
            changed = 26 - (0 - changed);
        return changed;
    },

    to_pin : function(letter) {
        return letter.charCodeAt() - 64;
    },

    from_pin : function(pin) {
        return String.fromCharCode(pin + 64);
    }
};

function Rotor(id) {
    // Rotor id
    this.id = id;

    // Rotor position
    this.pos = 'A';

    // Rotor advancement, from rotor_wiring.js
    this.advance_cycle = rotor_advancements[this.id];
    this.advance_count = 0;

    this.set_wiring = function() {
        this.wiring = [];
        for (var i = 0; i < 26; i++) {
            this.wiring[i] = Letter.to_pin(rotor_wirings[this.id][i]);
        }
    };

    this.change_init_pos = function(change_by) {
        // Set rotor starting position
        this.pos = Letter.from_pin(Letter.add(Letter.to_pin(this.pos), change_by));
    };

    this.advance = function() {
        // Advance the rotor
        if (++this.advance_count >= this.advance_cycle) {
            this.advance_count = 0;
            this.change_init_pos(1);
        }
    };

    this.sub_letter = function(letter) {
        // Do the substitution cipher for this rotor

        // Pin on side 1 of rotor
        var in_pin = Letter.add(Letter.to_pin(letter), Letter.to_pin(this.pos));
        // Pin on side 2 of rotor
        var out_pin = this.wiring[in_pin - 1];
        // Accounting for rotor position
        return Letter.from_pin(Letter.add(out_pin, (-1 * Letter.to_pin(this.pos))));
    };

    this.rsub_letter = function(letter) {
        // Do the substitution cipher for this rotor

        // Pin on side 2 of rotor
        var in_pin = Letter.add(Letter.to_pin(letter), Letter.to_pin(this.pos));
        // Pin on side 1 of rotor
        var out_pin = this.wiring.indexOf(in_pin) + 1;
        // Accounting for rotor position
        return Letter.from_pin(Letter.add(out_pin, (-1 * Letter.to_pin(this.pos))));
    };
};
<commit_msg>Fixed comment placement<commit_after>var Simulation = {
    // Set up for simulation
    initialize : function() {
        // Init rotors
        this.rotors = [];
        for (var i = 0; i < 3; i++) {
            this.rotors[i] = new Rotor(i);
            this.rotors[i].set_wiring();
        }

        // Create key listener
        document.onkeydown = function(event) {
            Simulation.key_press_handler(String.fromCharCode(event.keyCode));
        };

        // Init display
        Display.initialize();
        Display.update();
    },

    // Perform the encryption
    encrypt : function(key) {
        var current = key;

        // Advance rotors
        for (var i = 0; i < 3; i++) {
            this.rotors[i].advance();
        }

        // Do the substitution through the rotors
        for (var i = 0; i < 3; i++) {
            current = this.rotors[i].sub_letter(current);
        }

        // Find letter after reflector rotor;
        current = rotor_reflector[Letter.to_pin(current) - 1];

        // Do the substitution back through the rotors
        for (var i = 2; i >= 0; i--) {
            current = this.rotors[i].rsub_letter(current);
        }

        return current;
    },

    // Called when keys are pressed or clicked
    key_press_handler : function(key) {
        // Check if key is in acceptable range
        if (key.charCodeAt() < 65 || key.charCodeAt() > 90)
            return;

        // Set current key
        this.ckey = key;
        // Set current ciphertext key
        this.cct = Simulation.encrypt(key);

        // Add to text panes
        Display.add_to_text();
        // Update display
        Display.update();
    },

    // Function to call when rotor up or down clicked
    rotor_set_listener : function(event) {
        var rtr = event.target.parentNode.id.charAt(6) - 1;
        Simulation.rotors[rtr].change_init_pos((event.target.className == "up") ? -1 : 1);
        Display.update();
    }
};

var Display = {
    initialize : function() {
        this.rotor_disp = [];

        // Create rotor display
        for (var i = 1; i < 4; i++) {

            // Create rotor div
            var parent_d = document.createElement("div");
            parent_d.className = "rotor";
            parent_d.id = "rotor_" + i;
            document.getElementById("rotor_pane").appendChild(parent_d);

            // Create up arrow
            var up_d = document.createElement("div");
            up_d.innerHTML = '↑';
            up_d.className = "up";
            up_d.onclick = Simulation.rotor_set_listener;
            parent_d.appendChild(up_d);

            // Create rotor disp
            var rotor_d = document.createElement("div");
            this.rotor_disp[i - 1] = rotor_d;
            parent_d.appendChild(rotor_d);

            // Create down arrow
            var down_d = document.createElement("div");
            down_d.innerHTML = '↓';
            down_d.className = "down";
            down_d.onclick = Simulation.rotor_set_listener;
            parent_d.appendChild(down_d);

        }

        // Create keys display
        for (var i = 0; i < 26; i++) {
            var key = document.createElement("div");
            var letter = String.fromCharCode(65 + i);
            key.id = "key_" + letter;
            key.innerHTML = letter;
            key.onclick = function(event) {
                Simulation.key_press_handler(event.target.id.charAt(4));
            }
            document.getElementById("key_pane").appendChild(key);
        }
    },

    // Add text to text panes
    add_to_text : function() {
        document.getElementById("tp_pt").innerHTML =
            document.getElementById("tp_pt").innerHTML + " " + Simulation.ckey;
        document.getElementById("tp_ct").innerHTML =
            document.getElementById("tp_ct").innerHTML + " " + Simulation.cct;
    },

    // Update rotor letters
    update : function() {
        for (var i = 0; i < 3; i++)
            this.rotor_disp[i].innerHTML = Simulation.rotors[i].pos;

        // Remove old highlights
        var prevpt = document.getElementsByClassName("pthighlight");
        if (prevpt[0] != undefined)
            prevpt[0].classList.remove("pthighlight");
        var prevct = document.getElementsByClassName("cthighlight");
        if (prevct[0] != undefined)
            prevct[0].classList.remove("cthighlight");

        // Highlight plaintext
        document.getElementById("key_" + Simulation.ckey).classList.add("pthighlight");

        // Highlight ciphertext
        document.getElementById("key_" + Simulation.cct).classList.add("cthighlight");
    }
};

var Letter = {
    // Add together pin and adjustment
    add : function(letter, change_by) {
        // Add to letter
        var changed = letter + change_by;
        if (changed > 26)
            changed = 0 + (changed - 26);
        else if (changed < 1)
            changed = 26 - (0 - changed);
        return changed;
    },

    // Convert ASCII char to pin
    to_pin : function(letter) {
        return letter.charCodeAt() - 64;
    },

    // Convert pin to ASCII char
    from_pin : function(pin) {
        return String.fromCharCode(pin + 64);
    }
};

function Rotor(id) {
    // Rotor id
    this.id = id;

    // Rotor position
    this.pos = 'A';

    // Rotor advancement, from rotor_wiring.js
    this.advance_cycle = rotor_advancements[this.id];
    this.advance_count = 0;

    // Set wiring for rotor
    this.set_wiring = function() {
        this.wiring = [];
        for (var i = 0; i < 26; i++) {
            this.wiring[i] = Letter.to_pin(rotor_wirings[this.id][i]);
        }
    };

    // Set rotor starting position
    this.change_init_pos = function(change_by) {
        this.pos = Letter.from_pin(Letter.add(Letter.to_pin(this.pos), change_by));
    };

    // Advance the rotor
    this.advance = function() {
        if (++this.advance_count >= this.advance_cycle) {
            this.advance_count = 0;
            this.change_init_pos(1);
        }
    };

    // Do the substitution cipher for this rotor
    this.sub_letter = function(letter) {
        // Pin on side 1 of rotor
        var in_pin = Letter.add(Letter.to_pin(letter), Letter.to_pin(this.pos));
        // Pin on side 2 of rotor
        var out_pin = this.wiring[in_pin - 1];
        // Accounting for rotor position
        return Letter.from_pin(Letter.add(out_pin, (-1 * Letter.to_pin(this.pos))));
    };

    // Do the reverse of the substitution cipher for this rotor
    this.rsub_letter = function(letter) {
        // Pin on side 2 of rotor
        var in_pin = Letter.add(Letter.to_pin(letter), Letter.to_pin(this.pos));
        // Pin on side 1 of rotor
        var out_pin = this.wiring.indexOf(in_pin) + 1;
        // Accounting for rotor position
        return Letter.from_pin(Letter.add(out_pin, (-1 * Letter.to_pin(this.pos))));
    };
};
<|endoftext|>


<commit_before>var Sequelize = require('Sequelize');
var Moment = require('moment');
var Promise = require("bluebird");

var sequelize = new Sequelize("mssql://szy0syz0yngf2017:xQnWdw3u4BOgwTuU@192.168.97.199:1433/YNNZ2011001");
var SaleIssueEntry = sequelize.import('./models/SaleIssueEntry');
var PurInEntry = sequelize.import('./models/PurInEntry');

var sqlCommand = require('./db/sqlCommand');
var sqlConditions = require('./db/sqlConditions');

//{ type: sequelize.QueryTypes.SELECT} 只返回Sequelize查询到结果，不返回数据库的元数据。
var query = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: sqlConditions
});

var queryCurtAcc = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).month(0).date(1).format('YYYY-MM-DD'), //set month=1
      FBizDateEnd: sqlConditions.FBizDateEnd
    }
});

var queryLastAcc = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).add(-1,'year').month(0).date(1).format('YYYY-MM-DD'), //set year-1, month=1
      FBizDateEnd: Moment(sqlConditions.FBizDateEnd).add(-1,'year').format('YYYY-MM-DD')
    }
});

var queryPurIn = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: sqlConditions
});

var queryPurInCurtAcc = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).month(0).date(1).format('YYYY-MM-DD'), //set month=1
      FBizDateEnd: sqlConditions.FBizDateEnd
    }
});

var queryPurInLastAcc = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).add(-1,'year').month(0).date(1).format('YYYY-MM-DD'), //set year-1, month=1
      FBizDateEnd: Moment(sqlConditions.FBizDateEnd).add(-1,'year').format('YYYY-MM-DD')
    }
});

/////////////
Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    var map = {};
    this.map(e => ({k: key(e), d: e})).forEach(e => {
      map[e.k] = map[e.k] || [];
      map[e.k].push(e.d);
    });
    return Object.keys(map).map(k => ({key: k, data: map[k]}));
  }
});
/////////////

function statFert(arrData) {
  //以后这里从数据库取，不要写死！
  var brandFString = ['尿素','碳铵','硫铵','氯化铵','硝磷铵','普钙','重钙','钙镁','磷铵','富钙','硫酸钾','氯化钾','硫酸钾肥','高含量','低含量']
  var brandF = [];
  //根据顺序初始化
  brandFString.forEach(function(s,i) {
    var o = {
      name: s,
      sumQty: 0,
      sumAmount: 0
    };
    brandF.push(o);
  })
  //根据数据分析
  arrData.forEach(function(item, index) {
    brandF.forEach(function(brand,i) {
      if(item.FBrandFertilizer == brand.name) {
        brand.sumQty += item.FBaseQty;
        brand.sumAmount += item.FAmount;
      }
    });
  });
  brandF.push({
    name: '复合肥',
    sumQty: brandF[13].sumQty+ brandF[14].sumQty,
    sumAmount: brandF[13].sumAmount+ brandF[14].sumAmount,
  });
  //返回结果
  return brandF;
}

function statUrea(arrData) {
  var brandC = accObj = [];
  arrData
    .filter(function(v) { return v.FBrandCarbaMind != '非尿素'})
    .group(ii => ii.FMaterialType3)
    .forEach(function(v1, i1) {
      var o = {
        name: v1.key,
        sumQty: 0,
        sumAmount: 0,
        data: []
      }
      // 一次循环求两个字段的和
      accObj = v1.data.reduce((acc, val) => {
        acc.sumQty += val.FBaseQty;
        acc.sumAmount += val.FAmount;
        acc.data.push({
          materialNumber: val.FMaterialNumber,
          materialName: val.FMaterial,
          materialModel: val.FMaterialModel,
          qty: val.FBaseQty,
          amount: val.FAmount
        })
        return acc;
      }, {sumQty:0, sumAmount:0, data:[]});//初始化acc对象！
      o.sumQty = accObj.sumQty;
      o.sumAmount = accObj.sumAmount;
      o.data = accObj.data;
      brandC.push(o);
    });
    //返回处理结果
    return brandC;
}

function sumByColumnName(arrData, cn) {
  var sum = arrData.reduce((acc, val) => {
    acc += val[cn];
    return acc;
  }, 0);
  return sum;
}

function printSaleSummary(statRes, startDate) {
  var sumUrea = statRes.statFertRes.filter((item) => {return item.name == '尿素'})[0].sumQty.toFixed(2) || 0;
  var sumUreaDetails = statRes.statUreaRes
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + "吨,单价" + (val.sumAmount/val.sumQty).toFixed(0));
      return acc;
    }, [])
    .join(',');
  var sumFert = statRes
    .statFertRes.filter((item) => {return item.name != '尿素' && item.sumQty != 0})
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + '吨');
      return acc;
    }, [])
    .join(',');
  var strSaleSummary = "销售：化肥总售出"+ statRes.sumCurtQty.toFixed(2) +"吨，"+ (statRes.sumCurtAmount/10000).toFixed(2) +"万元。其中尿素"+
    sumUrea +"吨\（"+ sumUreaDetails +"），"
    + sumFert +"。"+ startDate.split('-')[0] +"年累计销售"+ statRes.sumAccCurtQty.toFixed(2) +"吨，同比增长"+ ((statRes.sumAccCurtQty/statRes.sumAccLastQty)*100).toFixed() +"%；累计销额"+ (statRes.sumAccCurtAmount/10000).toFixed(2) +"万元，同比增长"+ ((statRes.sumAccCurtAmount/statRes.sumAccLastAmount)*100).toFixed() +"%（以销售出库单统计）。";
  return strSaleSummary;
}

function printPurSummary(statRes, startDate) {
  var sumUrea = statRes.statFertRes.filter((item) => {return item.name == '尿素'})[0].sumQty.toFixed(2) || 0;
  var sumUreaDetails = statRes.statUreaRes
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + "吨,单价" + (val.sumAmount/val.sumQty).toFixed(0));
      return acc;
    }, [])
    .join(',');
  var sumFert = statRes
    .statFertRes.filter((item) => {return item.name != '尿素' && item.sumQty != 0})
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + '吨');
      return acc;
    }, [])
    .join(',');
  var strSaleSummary = "购进：化肥总购进"+ statRes.sumCurtQty.toFixed(2) +"吨，"+ (statRes.sumCurtAmount/10000).toFixed(2) +"万元。其中尿素"+
    sumUrea +"吨\（"+ sumUreaDetails +"），"
    + sumFert +"。"+ startDate.split('-')[0] +"年累计总购"+ statRes.sumAccCurtQty.toFixed(2) +"吨，同比增长"+ ((statRes.sumAccCurtQty/statRes.sumAccLastQty)*100).toFixed() +"%；累计购额"+ (statRes.sumAccCurtAmount/10000).toFixed(2) +"万元，同比增长"+ ((statRes.sumAccCurtAmount/statRes.sumAccLastAmount)*100).toFixed() +"%（以采购入库单统计）。";
  return strSaleSummary;
}

Promise.join(query, queryCurtAcc ,queryLastAcc, queryPurIn, queryPurInCurtAcc, queryPurInLastAcc, function(curtData, curtAccData, lastAccData, curtPurData, curtPurAccData, lastPurAccData){
  var statSaleRes = {
    sumCurtQty: sumByColumnName(curtData, 'FBaseQty'),
    sumCurtAmount: sumByColumnName(curtData, 'FAmount'),
    statFertRes: statFert(curtData),
    statUreaRes: statUrea(curtData),
    sumAccCurtQty: sumByColumnName(curtAccData,'FBaseQty'),
    sumAccCurtAmount: sumByColumnName(curtAccData,'FAmount'),
    sumAccLastQty: sumByColumnName(lastAccData,'FBaseQty'),
    sumAccLastAmount: sumByColumnName(lastAccData,'FAmount')
  };
  var statPurRes = {
    sumCurtQty: sumByColumnName(curtPurData, 'FBaseQty'),
    sumCurtAmount: sumByColumnName(curtPurData, 'FTaxAmount'),
    statFertRes: statFert(curtPurData),
    statUreaRes: statUrea(curtPurData),
    sumAccCurtQty: sumByColumnName(curtPurAccData,'FBaseQty'),
    sumAccCurtAmount: sumByColumnName(curtPurAccData,'FTaxAmount'),
    sumAccLastQty: sumByColumnName(lastPurAccData,'FBaseQty'),
    sumAccLastAmount: sumByColumnName(lastPurAccData,'FTaxAmount')
  };

  var saleRes = printSaleSummary(statSaleRes, sqlConditions.FBizDateStart);
  var purRes = printPurSummary(statPurRes, sqlConditions.FBizDateStart);
  console.log(saleRes);
  console.log(purRes);
});
<commit_msg>修复算法中也为含税价格<commit_after>var Sequelize = require('Sequelize');
var Moment = require('moment');
var Promise = require("bluebird");

var sequelize = new Sequelize("mssql://szy0syz0yngf2017:xQnWdw3u4BOgwTuU@192.168.97.199:1433/YNNZ2011001");
var SaleIssueEntry = sequelize.import('./models/SaleIssueEntry');
var PurInEntry = sequelize.import('./models/PurInEntry');

var sqlCommand = require('./db/sqlCommand');
var sqlConditions = require('./db/sqlConditions');

//{ type: sequelize.QueryTypes.SELECT} 只返回Sequelize查询到结果，不返回数据库的元数据。
var query = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: sqlConditions
});

var queryCurtAcc = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).month(0).date(1).format('YYYY-MM-DD'), //set month=1
      FBizDateEnd: sqlConditions.FBizDateEnd
    }
});

var queryLastAcc = sequelize.query(sqlCommand.saleOut, {
    type: sequelize.QueryTypes.SELECT,
    model: SaleIssueEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).add(-1,'year').month(0).date(1).format('YYYY-MM-DD'), //set year-1, month=1
      FBizDateEnd: Moment(sqlConditions.FBizDateEnd).add(-1,'year').format('YYYY-MM-DD')
    }
});

var queryPurIn = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: sqlConditions
});

var queryPurInCurtAcc = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).month(0).date(1).format('YYYY-MM-DD'), //set month=1
      FBizDateEnd: sqlConditions.FBizDateEnd
    }
});

var queryPurInLastAcc = sequelize.query(sqlCommand.PruIn, {
    type: sequelize.QueryTypes.SELECT,
    model: PurInEntry,
    replacements: {
      FBizDateStart: Moment(sqlConditions.FBizDateStart).add(-1,'year').month(0).date(1).format('YYYY-MM-DD'), //set year-1, month=1
      FBizDateEnd: Moment(sqlConditions.FBizDateEnd).add(-1,'year').format('YYYY-MM-DD')
    }
});

/////////////
Object.defineProperty(Array.prototype, 'group', {
  enumerable: false,
  value: function (key) {
    var map = {};
    this.map(e => ({k: key(e), d: e})).forEach(e => {
      map[e.k] = map[e.k] || [];
      map[e.k].push(e.d);
    });
    return Object.keys(map).map(k => ({key: k, data: map[k]}));
  }
});
/////////////

function statFert(arrData) {
  //以后这里从数据库取，不要写死！
  var brandFString = ['尿素','碳铵','硫铵','氯化铵','硝磷铵','普钙','重钙','钙镁','磷铵','富钙','硫酸钾','氯化钾','硫酸钾肥','高含量','低含量']
  var brandF = [];
  //根据顺序初始化
  brandFString.forEach(function(s,i) {
    var o = {
      name: s,
      sumQty: 0,
      sumAmount: 0
    };
    brandF.push(o);
  })
  //根据数据分析
  arrData.forEach(function(item, index) {
    brandF.forEach(function(brand,i) {
      if(item.FBrandFertilizer == brand.name) {
        brand.sumQty += item.FBaseQty;
        brand.sumAmount += item.FTaxAmount;  //修改为含税的
      }
    });
  });
  brandF.push({
    name: '复合肥',
    sumQty: brandF[13].sumQty+ brandF[14].sumQty,
    sumAmount: brandF[13].sumAmount+ brandF[14].sumAmount,
  });
  //返回结果
  return brandF;
}

function statUrea(arrData) {
  var brandC = accObj = [];
  arrData
    .filter(function(v) { return v.FBrandCarbaMind != '非尿素'})
    .group(ii => ii.FMaterialType3)
    .forEach(function(v1, i1) {
      var o = {
        name: v1.key,
        sumQty: 0,
        sumAmount: 0,
        data: []
      }
      // 一次循环求两个字段的和
      accObj = v1.data.reduce((acc, val) => {
        acc.sumQty += val.FBaseQty;
        acc.sumAmount += val.FTaxAmount;   //修改为含税的
        acc.data.push({
          materialNumber: val.FMaterialNumber,
          materialName: val.FMaterial,
          materialModel: val.FMaterialModel,
          qty: val.FBaseQty,
          amount: val.FTaxAmount   //修改为含税的
        })
        return acc;
      }, {sumQty:0, sumAmount:0, data:[]});//初始化acc对象！
      o.sumQty = accObj.sumQty;
      o.sumAmount = accObj.sumAmount;
      o.data = accObj.data;
      brandC.push(o);
    });
    //返回处理结果
    return brandC;
}

function sumByColumnName(arrData, cn) {
  var sum = arrData.reduce((acc, val) => {
    acc += val[cn];
    return acc;
  }, 0);
  return sum;
}

function printSaleSummary(statRes, startDate) {
  var sumUrea = statRes.statFertRes.filter((item) => {return item.name == '尿素'})[0].sumQty.toFixed(2) || 0;
  var sumUreaDetails = statRes.statUreaRes
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + "吨,单价" + (val.sumAmount/val.sumQty).toFixed(0));
      return acc;
    }, [])
    .join(',');
  var sumFert = statRes
    .statFertRes.filter((item) => {return item.name != '尿素' && item.sumQty != 0})
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + '吨');
      return acc;
    }, [])
    .join(',');
  var strSaleSummary = "销售：化肥总售出"+ statRes.sumCurtQty.toFixed(2) +"吨，"+ (statRes.sumCurtAmount/10000).toFixed(2) +"万元。其中尿素"+
    sumUrea +"吨\（"+ sumUreaDetails +"），"
    + sumFert +"。"+ startDate.split('-')[0] +"年累计销售"+ statRes.sumAccCurtQty.toFixed(2) +"吨，同比增长"+ ((statRes.sumAccCurtQty/statRes.sumAccLastQty)*100).toFixed() +"%；累计销额"+ (statRes.sumAccCurtAmount/10000).toFixed(2) +"万元，同比增长"+ ((statRes.sumAccCurtAmount/statRes.sumAccLastAmount)*100).toFixed() +"%（以销售出库单统计）。";
  return strSaleSummary;
}

function printPurSummary(statRes, startDate) {
  var sumUrea = statRes.statFertRes.filter((item) => {return item.name == '尿素'})[0].sumQty.toFixed(2) || 0;
  var sumUreaDetails = statRes.statUreaRes
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + "吨,单价" + (val.sumAmount/val.sumQty).toFixed(0));
      return acc;
    }, [])
    .join(',');
  var sumFert = statRes
    .statFertRes.filter((item) => {return item.name != '尿素' && item.sumQty != 0})
    .reduce((acc, val) => {
      acc.push(val.name + val.sumQty.toFixed(2) + '吨');
      return acc;
    }, [])
    .join(',');
  var strSaleSummary = "购进：化肥总购进"+ statRes.sumCurtQty.toFixed(2) +"吨，"+ (statRes.sumCurtAmount/10000).toFixed(2) +"万元。其中尿素"+
    sumUrea +"吨\（"+ sumUreaDetails +"），"
    + sumFert +"。"+ startDate.split('-')[0] +"年累计总购"+ statRes.sumAccCurtQty.toFixed(2) +"吨，同比增长"+ ((statRes.sumAccCurtQty/statRes.sumAccLastQty)*100).toFixed() +"%；累计购额"+ (statRes.sumAccCurtAmount/10000).toFixed(2) +"万元，同比增长"+ ((statRes.sumAccCurtAmount/statRes.sumAccLastAmount)*100).toFixed() +"%（以采购入库单统计）。";
  return strSaleSummary;
}

Promise.join(query, queryCurtAcc ,queryLastAcc, queryPurIn, queryPurInCurtAcc, queryPurInLastAcc, function(curtData, curtAccData, lastAccData, curtPurData, curtPurAccData, lastPurAccData){
  var statSaleRes = {
    sumCurtQty: sumByColumnName(curtData, 'FBaseQty'),
    sumCurtAmount: sumByColumnName(curtData, 'FAmount'),
    statFertRes: statFert(curtData),
    statUreaRes: statUrea(curtData),
    sumAccCurtQty: sumByColumnName(curtAccData,'FBaseQty'),
    sumAccCurtAmount: sumByColumnName(curtAccData,'FAmount'),
    sumAccLastQty: sumByColumnName(lastAccData,'FBaseQty'),
    sumAccLastAmount: sumByColumnName(lastAccData,'FAmount')
  };
  var statPurRes = {
    sumCurtQty: sumByColumnName(curtPurData, 'FBaseQty'),
    sumCurtAmount: sumByColumnName(curtPurData, 'FTaxAmount'),
    statFertRes: statFert(curtPurData),
    statUreaRes: statUrea(curtPurData),
    sumAccCurtQty: sumByColumnName(curtPurAccData,'FBaseQty'),
    sumAccCurtAmount: sumByColumnName(curtPurAccData,'FTaxAmount'),
    sumAccLastQty: sumByColumnName(lastPurAccData,'FBaseQty'),
    sumAccLastAmount: sumByColumnName(lastPurAccData,'FTaxAmount')
  };

  var saleRes = printSaleSummary(statSaleRes, sqlConditions.FBizDateStart);
  var purRes = printPurSummary(statPurRes, sqlConditions.FBizDateStart);
  console.log(saleRes);
  console.log(purRes);
});
<|endoftext|>


<commit_before>// ebef.user.js
//
// Copyright 2017, Ola Rinta-Koski
//
// Based on abef.user.js v8.1, Copyright 2007-2015, Michael Devore
//
// This file is licensed under the terms of the Artistic License 2.0.
// See http://opensource.org/licenses/artistic-license-2.0 for the license itself.
//
// This is a Greasemonkey script.
// See http://www.greasespot.net/ for more information on Greasemonkey.
//
// ==UserScript==
// @name          Even Better eBay Filter
// @namespace     https://github.com/rockola/Even-Better-eBay-Filter
// @description	Allow filtering eBay searches based on additional criteria, global site support
// @version       1.0
// @include       http://*listings*.ebay.*/*
// @include       https://*listings*.ebay.*/*
// @include       http://*search*.ebay.*/*
// @include       https://*search*.ebay.*/*
// @include       http://*shop*.ebay.*/*
// @include       https://*shop*.ebay.*/*
// @include       http://www.ebay.*/sch/*
// @include       https://www.ebay.*/sch/*
// @include       http://www.ebay.*/dsc/*
// @include       https://www.ebay.*/dsc/*
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-end
// ==/UserScript==
//
// Version 1.0, released April 2017

var defaultScoreThreshold = 50;
var defaultPercentThreshold = 98.5;
var defaultScoreMax = "";
var FILTER_SCORE_FLAG = 1;
var FILTER_PERCENT_FLAG = FILTER_SCORE_FLAG << 1;
var FILTER_SCOREMAX_FLAG = FILTER_PERCENT_FLAG << 1;
var filteredCount = 0;
var buttonText = "button";
var divText = "DIV";
var spanText = "SPAN";
var inputText = "INPUT";
var imgText = "IMG";
var brText = "BR";
var ulText = "UL";
var liText = "LI";
var noneText = "none";
var blockText = "block";
var xText = "x";
var changedText = "\u00a0 (Values changed.)";
var changeNode = null;
var configNode = null;
var filteredNode = null;
var scoreThreshold = defaultScoreThreshold;
var percentThreshold = defaultPercentThreshold;
var scoreMax = defaultScoreMax;
var noTrsChecked = false;

var abefActiveControl = null;

function getAbefConfiguration()
{
	var abefParam = GM_getValue("scoreThreshold", xText);
	if (abefParam != xText)
	{
		scoreThreshold = abefParam - 0;
	}
	else
	{
		GM_setValue("scoreThreshold", scoreThreshold + "");
	}
	abefParam = GM_getValue("percentThreshold", xText);
	if (abefParam != xText)
	{
		percentThreshold = abefParam - 0;
	}
	else
	{
		GM_setValue("percentThreshold", percentThreshold + "");
	}
	abefParam = GM_getValue("scoreMax", xText);
	if (abefParam != xText)
	{
		if (isNaN(parseInt(abefParam)))
		{
			scoreMax = "";
		}
		else
		{
			scoreMax = abefParam - 0;
		}
	}
	else
	{
		GM_setValue("scoreMax", scoreMax + "");
	}

	abefParam = GM_getValue("noTrsChecked", xText);
	if (abefParam != xText)
	{
		noTrsChecked = abefParam;
	}
	else
	{
		GM_setValue("noTrsChecked", noTrsChecked);
	}
}

function updateAbefConfiguration()
{
	GM_setValue("scoreThreshold", scoreThreshold + "");
	GM_setValue("percentThreshold", percentThreshold + "");
	GM_setValue("scoreMax", scoreMax + "");
	GM_setValue("noTrsChecked",noTrsChecked);
}

function showFilteredCount()
{
	filteredNode.nodeValue =
		"\u00a0 " + filteredCount + " entr"+ (filteredCount != 1 ? "ies" : "y") + " filtered from view";
	configNode.parentNode.appendChild(filteredNode);
}

function newFilterValues(event)
{
	if (event != null)
	{
		event.preventDefault();
		event.stopPropagation();
	}
	if (configNode.nextSibling == changeNode)
	{
		configNode.parentNode.removeChild(changeNode);
	}

	var fNode = document.getElementById("fbScore");
	scoreThreshold = fNode.value - 0;	// force numeric
	fNode = document.getElementById("fbPercent");
	percentThreshold = fNode.value - 0;
	fNode = document.getElementById("fbMax");
	if (isNaN(parseInt(fNode.value)))
	{
		scoreMax = "";
	}
	else
	{
		scoreMax = fNode.value - 0;
	}
	fNode = document.getElementById("fbTrs");
	noTrsChecked = fNode.checked;
	updateAbefConfiguration();
	walkTheListings();
}

function changedFilter(event)
{
	if (event != null)
	{
		event.preventDefault();
		event.stopPropagation();
	}
	if (configNode.nextSibling == changeNode)
	{
		return;
	}
	if (configNode.nextSibling == filteredNode)
	{
		configNode.parentNode.removeChild(filteredNode);
	}
	configNode.parentNode.appendChild(changeNode);
}

function getFeedbackScore(textNode)
{
	if (!textNode.nodeValue)
	{
		return xText;
	}
	var s = textNode.nodeValue;
	var result;
	if ((result = s.match(/([\.,0-9]+)/)) != null)
	{
		// remove comma and period
		result[1] = result[1].replace(/[\.,]/g, "");
		return result[1] - 0;	// force numeric
	}
	return xText;
}

function getFeedbackPercent(textNode)
{
	if (!textNode.nodeValue)
	{
		return xText;
	}
	var s = textNode.nodeValue;
	var result;
	// globalize by accepting comma as a period
	if ((result = s.match(/([\.,0-9]+)%/)) != null)
	{
		// change comma, if any, to period
		result[1] = result[1].replace(/,/, ".");
		return result[1] - 0;	// force numeric
	}
	return xText;
}

function shouldFilterOut(spanNode)
{
	var cNode = spanNode.firstChild;
	var validScore = false;
	var validPercent = false;
	var feedbackScore = 0;
	var feedbackPercent = 0;

	var parsed;
	while (!validScore || !validPercent)
	{
		parsed = false;
		if (cNode && spanNode.nodeName.toUpperCase() === spanText && cNode.nodeName === "#text")
		{
			if (!validPercent)
			{
				if ((feedbackPercent = getFeedbackPercent(cNode)) != xText)
				{
					validPercent = true;
					parsed = true;
				}
			}
			if (!validScore && !parsed)
			{
				if ((feedbackScore = getFeedbackScore(cNode)) != xText)
				{
					validScore = true;
				}
			}
		}
		spanNode = spanNode.nextSibling;
		if (!spanNode)
		{
			break;
		}
		cNode = spanNode.firstChild;
	}

	var retValue = 0;
	if (validScore && feedbackScore < scoreThreshold)
	{
		retValue |= FILTER_SCORE_FLAG;
	}
	if (validPercent && feedbackPercent < percentThreshold)
	{
		retValue |= FILTER_PERCENT_FLAG;
	}
	if (validScore && !isNaN(parseInt(scoreMax)) && feedbackScore > scoreMax)
	{
		retValue |= FILTER_SCOREMAX_FLAG;
	}
	if (retValue)
	{
		filteredCount++;
	}

	return retValue;
}

function walkTheListings()
{
	filteredCount = 0;

	var xpath = "//div[@id='ResultSetItems']//li[contains(@id, 'item') and contains(@class, 'lvresult')]";

	var liNodes = document.evaluate(
		xpath,
		document,
		null,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
		null
	);

	var entryCounter = 0;
	for (var loopVar = 0; loopVar < liNodes.snapshotLength; loopVar++)
	{
		var liNode = liNodes.snapshotItem(loopVar);
		var detailNode = liNode.firstChild;
		while (detailNode && (!detailNode.className || detailNode.nodeName.toUpperCase() !== ulText || detailNode.className.indexOf('lvdetails') < 0))
		{
			detailNode = detailNode.nextSibling;
		}
		if (!detailNode)
		{
			continue;
		}

		var subLiNode = detailNode.firstChild;
		var spanNode = null;
		var found = false;
		while (subLiNode)
		{
			spanNode = subLiNode.firstChild;
			while (spanNode)
			{
				if (spanNode.nodeName.toUpperCase() === spanText && spanNode.className === 'selrat')
				{
					found = true;
					break;
				}
				spanNode = spanNode.nextSibling;
			}
			if (!found)
			{
				subLiNode = subLiNode.nextSibling;
			}
			else
			{
				break;
			}
		}
		if (!spanNode)
		{
			continue;
		}

		var topSellerFilter = false;
		if (noTrsChecked)
		{
			var checkLiNode = subLiNode.nextSibling;
			while (checkLiNode)
			{
				if (checkLiNode.nodeName.toUpperCase() === liText)
				{
					var checkNode = checkLiNode.firstChild;
					while (checkNode && checkNode.nodeName.toUpperCase() !== spanText)
					{
						checkNode = checkNode.nextSibling;
					}
					if (checkNode)
					{
						// got SPAN
						checkNode = checkNode.firstChild;
					}
					while (checkNode && (checkNode.nodeName.toUpperCase() !== imgText || checkNode.className.indexOf("iconETRS") < 0))
					{
						checkNode = checkNode.nextSibling;
					}
					if (checkNode)
					{
						// found top seller IMG
						topSellerFilter = true;
						filteredCount++;
						break;
					}
				}
				checkLiNode = checkLiNode.nextSibling;
			}
		}

		if (topSellerFilter || shouldFilterOut(spanNode))
		{
			if (liNode.style.display != noneText)
			{
				liNode.style.display = noneText;
			}
		}
		else
		{
			liNode.style.display = blockText;
		}
	}
	showFilteredCount();
}

function buildControls()
{
	var xpath;
	var divNodes;

	xpath = "//div[@id='RelatedSearchesDF']";

	divNodes = document.evaluate(
		xpath,
		document,
		null,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
		null
	);

	var isRelatedSearch = true;
	if (divNodes.snapshotLength <= 0)
	{
//		return false;
		isRelatedSearch = false;
		xpath = "//div[@id='TopPanelDF']";
		divNodes = document.evaluate(
			xpath,
			document,
			null,
			XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
			null
		);
		if (divNodes.snapshotLength <= 0)
		{
			return false;
		}
	}

	changeNode = document.createTextNode(changedText);
	filteredNode = document.createTextNode("\u00a0 0 entries filtered from view");

	var divSibling;

	divSibling = divNodes.snapshotItem(0);

	var newDivNode = document.createElement(divText);

	if (!isRelatedSearch)
	{
		newDivNode.appendChild(document.createElement(brText));
	}

	var span1Node = document.createElement(spanText);
	span1Node.style.fontWeight = "bold";
	var input1Node = document.createElement(inputText);
	input1Node.type = "text";
	input1Node.size = 4;
	input1Node.style.textAlign = "right";
	input1Node.setAttribute("id", "fbScore");
	input1Node.defaultValue = scoreThreshold;
	input1Node.addEventListener('change', changedFilter, false);
	span1Node.appendChild(input1Node);
	span1Node.appendChild(document.createTextNode(" Minimum feedback score\u00a0\u00a0\u00a0\u00a0"));

	newDivNode.appendChild(span1Node);
	var span2Node = document.createElement(spanText);
	span2Node.style.fontWeight = "bold";
	var input2Node = document.createElement(inputText);
	input2Node.type = "text";
	input2Node.size = 4;
	input2Node.style.textAlign = "right";
	input2Node.setAttribute("id", "fbPercent");
	input2Node.defaultValue = percentThreshold;
	input2Node.addEventListener('change', changedFilter, false);
	span2Node.appendChild(input2Node);
	span2Node.appendChild(document.createTextNode("% Minimum positive feedback\u00a0\u00a0\u00a0\u00a0"));
	newDivNode.appendChild(span2Node);

	var spanMaxNode = document.createElement(spanText);
	spanMaxNode.style.fontWeight = "bold";
	var inputMaxNode = document.createElement(inputText);
	inputMaxNode.type = "text";
	inputMaxNode.size = 4;
	inputMaxNode.style.textAlign = "right";
	inputMaxNode.setAttribute("id", "fbMax");
	inputMaxNode.defaultValue = scoreMax;
	inputMaxNode.addEventListener('change', changedFilter, false);
	spanMaxNode.appendChild(inputMaxNode);
	spanMaxNode.appendChild(document.createTextNode(" Maximum feedback score (blank to disable)\u00a0\u00a0\u00a0\u00a0"));
	newDivNode.appendChild(spanMaxNode);

	var spanTrsNode = document.createElement(spanText);
	spanTrsNode.style.fontWeight = "bold";
	var inputTrsNode = document.createElement(inputText);
	inputTrsNode.type = "checkbox";
	inputTrsNode.size = 4;
	inputTrsNode.style.textAlign = "right";
	inputTrsNode.setAttribute("id", "fbTrs");
	inputTrsNode.checked = noTrsChecked;
	inputTrsNode.defaultChecked = noTrsChecked;
	inputTrsNode.addEventListener('change', changedFilter, false);
	spanTrsNode.appendChild(inputTrsNode);
	spanTrsNode.appendChild(document.createTextNode(" No Top-rated Sellers"));
	newDivNode.appendChild(spanTrsNode);

	// second line
	newDivNode.appendChild(document.createElement(brText));
	configNode = document.createElement(buttonText);
	configNode.appendChild(document.createTextNode("Update Filter"));
	configNode.addEventListener('click', newFilterValues, false);
	newDivNode.appendChild(configNode);

	divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);
	divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);

	divSibling.parentNode.insertBefore(newDivNode, divSibling.nextSibling);

	return true;
}

function init()
{
	getAbefConfiguration();
	if (!buildControls())
	{
		return;
	}
	walkTheListings();
}

function main()
{
	if (!GM_setValue)
	{
		return;
	}
	init();
}

main();

<commit_msg>untabify; indent 4<commit_after>// ebef.user.js
//
// Copyright 2017, Ola Rinta-Koski
//
// Based on abef.user.js v8.1, Copyright 2007-2015, Michael Devore
//
// This file is licensed under the terms of the Artistic License 2.0.
// See http://opensource.org/licenses/artistic-license-2.0 for the license itself.
//
// This is a Greasemonkey script.
// See http://www.greasespot.net/ for more information on Greasemonkey.
//
// ==UserScript==
// @name          Even Better eBay Filter
// @namespace     https://github.com/rockola/Even-Better-eBay-Filter
// @description Allow filtering eBay searches based on additional criteria, global site support
// @version       1.0
// @include       http://*listings*.ebay.*/*
// @include       https://*listings*.ebay.*/*
// @include       http://*search*.ebay.*/*
// @include       https://*search*.ebay.*/*
// @include       http://*shop*.ebay.*/*
// @include       https://*shop*.ebay.*/*
// @include       http://www.ebay.*/sch/*
// @include       https://www.ebay.*/sch/*
// @include       http://www.ebay.*/dsc/*
// @include       https://www.ebay.*/dsc/*
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-end
// ==/UserScript==
//
// Version 1.0, released April 2017
// -- Added vendor blacklist

var defaultScoreThreshold = 50;
var defaultPercentThreshold = 98.5;
var defaultScoreMax = "";
var FILTER_SCORE_FLAG = 1;
var FILTER_PERCENT_FLAG = FILTER_SCORE_FLAG << 1;
var FILTER_SCOREMAX_FLAG = FILTER_PERCENT_FLAG << 1;
var filteredCount = 0;
var buttonText = "button";
var divText = "DIV";
var spanText = "SPAN";
var inputText = "INPUT";
var imgText = "IMG";
var brText = "BR";
var ulText = "UL";
var liText = "LI";
var noneText = "none";
var blockText = "block";
var xText = "x";
var changedText = "\u00a0 (Values changed.)";
var changeNode = null;
var configNode = null;
var filteredNode = null;
var scoreThreshold = defaultScoreThreshold;
var percentThreshold = defaultPercentThreshold;
var scoreMax = defaultScoreMax;
var noTrsChecked = false;

var abefActiveControl = null;

function getAbefConfiguration()
{
    var abefParam = GM_getValue("scoreThreshold", xText);
    if (abefParam != xText)
    {
        scoreThreshold = abefParam - 0;
    }
    else
    {
        GM_setValue("scoreThreshold", scoreThreshold + "");
    }
    abefParam = GM_getValue("percentThreshold", xText);
    if (abefParam != xText)
    {
        percentThreshold = abefParam - 0;
    }
    else
    {
        GM_setValue("percentThreshold", percentThreshold + "");
    }
    abefParam = GM_getValue("scoreMax", xText);
    if (abefParam != xText)
    {
        if (isNaN(parseInt(abefParam)))
        {
            scoreMax = "";
        }
        else
        {
            scoreMax = abefParam - 0;
        }
    }
    else
    {
        GM_setValue("scoreMax", scoreMax + "");
    }

    abefParam = GM_getValue("noTrsChecked", xText);
    if (abefParam != xText)
    {
        noTrsChecked = abefParam;
    }
    else
    {
        GM_setValue("noTrsChecked", noTrsChecked);
    }
}

function updateAbefConfiguration()
{
    GM_setValue("scoreThreshold", scoreThreshold + "");
    GM_setValue("percentThreshold", percentThreshold + "");
    GM_setValue("scoreMax", scoreMax + "");
    GM_setValue("noTrsChecked",noTrsChecked);
}

function showFilteredCount()
{
    filteredNode.nodeValue =
        "\u00a0 " + filteredCount + " entr"+ (filteredCount != 1 ? "ies" : "y") + " filtered from view";
    configNode.parentNode.appendChild(filteredNode);
}

function newFilterValues(event)
{
    if (event != null)
    {
        event.preventDefault();
        event.stopPropagation();
    }
    if (configNode.nextSibling == changeNode)
    {
        configNode.parentNode.removeChild(changeNode);
    }

    var fNode = document.getElementById("fbScore");
    scoreThreshold = fNode.value - 0;       // force numeric
    fNode = document.getElementById("fbPercent");
    percentThreshold = fNode.value - 0;
    fNode = document.getElementById("fbMax");
    if (isNaN(parseInt(fNode.value)))
    {
        scoreMax = "";
    }
    else
    {
        scoreMax = fNode.value - 0;
    }
    fNode = document.getElementById("fbTrs");
    noTrsChecked = fNode.checked;
    updateAbefConfiguration();
    walkTheListings();
}

function changedFilter(event)
{
    if (event != null)
    {
        event.preventDefault();
        event.stopPropagation();
    }
    if (configNode.nextSibling == changeNode)
    {
        return;
    }
    if (configNode.nextSibling == filteredNode)
    {
        configNode.parentNode.removeChild(filteredNode);
    }
    configNode.parentNode.appendChild(changeNode);
}

function getFeedbackScore(textNode)
{
    if (!textNode.nodeValue)
    {
        return xText;
    }
    var s = textNode.nodeValue;
    var result;
    if ((result = s.match(/([\.,0-9]+)/)) != null)
    {
        // remove comma and period
        result[1] = result[1].replace(/[\.,]/g, "");
        return result[1] - 0;   // force numeric
    }
    return xText;
}

function getFeedbackPercent(textNode)
{
    if (!textNode.nodeValue)
    {
        return xText;
    }
    var s = textNode.nodeValue;
    var result;
    // globalize by accepting comma as a period
    if ((result = s.match(/([\.,0-9]+)%/)) != null)
    {
        // change comma, if any, to period
        result[1] = result[1].replace(/,/, ".");
        return result[1] - 0;   // force numeric
    }
    return xText;
}

function shouldFilterOut(spanNode)
{
    var cNode = spanNode.firstChild;
    var validScore = false;
    var validPercent = false;
    var feedbackScore = 0;
    var feedbackPercent = 0;

    var parsed;
    while (!validScore || !validPercent)
    {
        parsed = false;
        if (cNode && spanNode.nodeName.toUpperCase() === spanText && cNode.nodeName === "#text")
        {
            if (!validPercent)
            {
                if ((feedbackPercent = getFeedbackPercent(cNode)) != xText)
                {
                    validPercent = true;
                    parsed = true;
                }
            }
            if (!validScore && !parsed)
            {
                if ((feedbackScore = getFeedbackScore(cNode)) != xText)
                {
                    validScore = true;
                }
            }
        }
        spanNode = spanNode.nextSibling;
        if (!spanNode)
        {
            break;
        }
        cNode = spanNode.firstChild;
    }

    var retValue = 0;
    if (validScore && feedbackScore < scoreThreshold)
    {
        retValue |= FILTER_SCORE_FLAG;
    }
    if (validPercent && feedbackPercent < percentThreshold)
    {
        retValue |= FILTER_PERCENT_FLAG;
    }
    if (validScore && !isNaN(parseInt(scoreMax)) && feedbackScore > scoreMax)
    {
        retValue |= FILTER_SCOREMAX_FLAG;
    }
    if (retValue)
    {
        filteredCount++;
    }

    return retValue;
}

function walkTheListings()
{
    filteredCount = 0;

    var xpath = "//div[@id='ResultSetItems']//li[contains(@id, 'item') and contains(@class, 'lvresult')]";

    var liNodes = document.evaluate(
        xpath,
        document,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
    );

    var entryCounter = 0;
    for (var loopVar = 0; loopVar < liNodes.snapshotLength; loopVar++)
    {
        var liNode = liNodes.snapshotItem(loopVar);
        var detailNode = liNode.firstChild;
        while (detailNode && (!detailNode.className || detailNode.nodeName.toUpperCase() !== ulText || detailNode.className.indexOf('lvdetails') < 0))
        {
            detailNode = detailNode.nextSibling;
        }
        if (!detailNode)
        {
            continue;
        }

        var subLiNode = detailNode.firstChild;
        var spanNode = null;
        var found = false;
        while (subLiNode)
        {
            spanNode = subLiNode.firstChild;
            while (spanNode)
            {
                if (spanNode.nodeName.toUpperCase() === spanText && spanNode.className === 'selrat')
                {
                    found = true;
                    break;
                }
                spanNode = spanNode.nextSibling;
            }
            if (!found)
            {
                subLiNode = subLiNode.nextSibling;
            }
            else
            {
                break;
            }
        }
        if (!spanNode)
        {
            continue;
        }

        var topSellerFilter = false;
        if (noTrsChecked)
        {
            var checkLiNode = subLiNode.nextSibling;
            while (checkLiNode)
            {
                if (checkLiNode.nodeName.toUpperCase() === liText)
                {
                    var checkNode = checkLiNode.firstChild;
                    while (checkNode && checkNode.nodeName.toUpperCase() !== spanText)
                    {
                        checkNode = checkNode.nextSibling;
                    }
                    if (checkNode)
                    {
                        // got SPAN
                        checkNode = checkNode.firstChild;
                    }
                    while (checkNode && (checkNode.nodeName.toUpperCase() !== imgText || checkNode.className.indexOf("iconETRS") < 0))
                    {
                        checkNode = checkNode.nextSibling;
                    }
                    if (checkNode)
                    {
                        // found top seller IMG
                        topSellerFilter = true;
                        filteredCount++;
                        break;
                    }
                }
                checkLiNode = checkLiNode.nextSibling;
            }
        }

        if (topSellerFilter || shouldFilterOut(spanNode))
        {
            if (liNode.style.display != noneText)
            {
                liNode.style.display = noneText;
            }
        }
        else
        {
            liNode.style.display = blockText;
        }
    }
    showFilteredCount();
}

function buildControls()
{
    var xpath;
    var divNodes;

    xpath = "//div[@id='RelatedSearchesDF']";

    divNodes = document.evaluate(
        xpath,
        document,
        null,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
        null
    );

    var isRelatedSearch = true;
    if (divNodes.snapshotLength <= 0)
    {
	//              return false;
        isRelatedSearch = false;
        xpath = "//div[@id='TopPanelDF']";
        divNodes = document.evaluate(
            xpath,
            document,
            null,
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
            null
        );
        if (divNodes.snapshotLength <= 0)
        {
            return false;
        }
    }

    changeNode = document.createTextNode(changedText);
    filteredNode = document.createTextNode("\u00a0 0 entries filtered from view");

    var divSibling;

    divSibling = divNodes.snapshotItem(0);

    var newDivNode = document.createElement(divText);

    if (!isRelatedSearch)
    {
        newDivNode.appendChild(document.createElement(brText));
    }

    var span1Node = document.createElement(spanText);
    span1Node.style.fontWeight = "bold";
    var input1Node = document.createElement(inputText);
    input1Node.type = "text";
    input1Node.size = 4;
    input1Node.style.textAlign = "right";
    input1Node.setAttribute("id", "fbScore");
    input1Node.defaultValue = scoreThreshold;
    input1Node.addEventListener('change', changedFilter, false);
    span1Node.appendChild(input1Node);
    span1Node.appendChild(document.createTextNode(" Minimum feedback score\u00a0\u00a0\u00a0\u00a0"));

    newDivNode.appendChild(span1Node);
    var span2Node = document.createElement(spanText);
    span2Node.style.fontWeight = "bold";
    var input2Node = document.createElement(inputText);
    input2Node.type = "text";
    input2Node.size = 4;
    input2Node.style.textAlign = "right";
    input2Node.setAttribute("id", "fbPercent");
    input2Node.defaultValue = percentThreshold;
    input2Node.addEventListener('change', changedFilter, false);
    span2Node.appendChild(input2Node);
    span2Node.appendChild(document.createTextNode("% Minimum positive feedback\u00a0\u00a0\u00a0\u00a0"));
    newDivNode.appendChild(span2Node);

    var spanMaxNode = document.createElement(spanText);
    spanMaxNode.style.fontWeight = "bold";
    var inputMaxNode = document.createElement(inputText);
    inputMaxNode.type = "text";
    inputMaxNode.size = 4;
    inputMaxNode.style.textAlign = "right";
    inputMaxNode.setAttribute("id", "fbMax");
    inputMaxNode.defaultValue = scoreMax;
    inputMaxNode.addEventListener('change', changedFilter, false);
    spanMaxNode.appendChild(inputMaxNode);
    spanMaxNode.appendChild(document.createTextNode(" Maximum feedback score (blank to disable)\u00a0\u00a0\u00a0\u00a0"));
    newDivNode.appendChild(spanMaxNode);

    var spanTrsNode = document.createElement(spanText);
    spanTrsNode.style.fontWeight = "bold";
    var inputTrsNode = document.createElement(inputText);
    inputTrsNode.type = "checkbox";
    inputTrsNode.size = 4;
    inputTrsNode.style.textAlign = "right";
    inputTrsNode.setAttribute("id", "fbTrs");
    inputTrsNode.checked = noTrsChecked;
    inputTrsNode.defaultChecked = noTrsChecked;
    inputTrsNode.addEventListener('change', changedFilter, false);
    spanTrsNode.appendChild(inputTrsNode);
    spanTrsNode.appendChild(document.createTextNode(" No Top-rated Sellers"));
    newDivNode.appendChild(spanTrsNode);

    // second line
    newDivNode.appendChild(document.createElement(brText));
    configNode = document.createElement(buttonText);
    configNode.appendChild(document.createTextNode("Update Filter"));
    configNode.addEventListener('click', newFilterValues, false);
    newDivNode.appendChild(configNode);

    divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);
    divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);

    divSibling.parentNode.insertBefore(newDivNode, divSibling.nextSibling);

    return true;
}

function init()
{
    getAbefConfiguration();
    if (!buildControls())
    {
        return;
    }
    walkTheListings();
}

function main()
{
    if (!GM_setValue)
    {
        return;
    }
    init();
}

main();

<|endoftext|>


<commit_before>var buffer = 20; //scroll bar buffer

function pageY(elem) {
    return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop;
}

function resizeIframe() {
    var height = document.documentElement.clientHeight;
    height -= pageY(document.getElementById('roundcubeFrame'))+ buffer ;
    height = (height < 0) ? 0 : height;
    document.getElementById('roundcubeFrame').style.height = '100%';
	// fix scrollbar issue
	$('#content').css('overflow','hidden');
	$('#content').css('height','+ height+');
	width=$('#content').css('width');
	$('#content').css('width','+ width +');

}

$('#roundcubeFrame').load(function() {
	resizeIframe();

	var mainscreen = $('#roundcubeFrame').contents().find('#mainscreen');
    // remove header line, includes about line and
    var top_line = $('#roundcubeFrame').contents().find('#topline');
    // correct top padding
    var top_margin=10;
    try{
            var top_nav = $('#roundcubeFrame').contents().find('#topnav');
            // check if the above element exits (only in new larry theme, if null use rc 0.7 default theme
            // TODO refactor and move theme check
            if(top_nav.height()!=null){
                    top_margin= 10;
                    //In larry theme with accounts plugin, we have to move the account selection
                    var acc_select = top_line.find('.username');
                    if (acc_select) {
                          mainscreen.find('div#messagetoolbar').attr('id',''); //Quick n dirty for space
                          acc_select.appendTo(mainscreen.find('div#searchfilter')).css({position: 'absolute', top:'0px', left:'-170px'});
                    }
            } else {
                    top_margin= parseInt(mainscreen.css('top'),10)-top_line.height();
            }
    } catch (e) {}
    top_line.remove();

    // fix topbar, issue https://github.com/hypery2k/owncloud/issues/54
    $('#roundcubeFrame').contents().find('.toolbar').css('z-index','80');
    $('#roundcubeFrame').contents().find('.toolbar').css('position','absolute');

    // remove logout button
    $('#roundcubeFrame').contents().find('.button-logout').remove();

    // check if the header menu from roundcube was disabled
    if($('#disable_header_nav').val() === 'true') {

            var top_nav = $('#roundcubeFrame').contents().find('#header');
            // check if the above element exits (only in new larry theme, if null use rc 0.7 default theme
            if(top_nav.height()==null){
                    top_nav = $('#roundcubeFrame').contents().find('#taskbar');
            } else {
                    //top_in= top_margin-top_nav.height();
            }
            top_nav.remove();
		$('#roundcubeFrame').contents().find('#mainscreen').css('top',top_margin);
    } else {
		// correct top padding
		$('#roundcubeFrame').contents().find('#mainscreen').css('top','50px');
	}
	// slide in roundcube nice
	$('#loader').fadeOut('slow');
	$('#roundcubeFrame').slideDown('slow');
	// remove email adresse
	$('#roundcubeFrame').contents().find('.username').remove();

});
<commit_msg>Update apps/roundcube/src/js/mailFrameScripts.js<commit_after>$(document).ready(function() {
        $(window).resize(function () {
                fillWindow($('#roundcube_container'));
        });
        $(window).resize();
        $('#roundcube_container').scroll(updateOnBottom).empty().width($('#content').width());
});

$('#roundcubeFrame').load(function() {
	var mainscreen = $('#roundcubeFrame').contents().find('#mainscreen');
    // remove header line, includes about line and
    var top_line = $('#roundcubeFrame').contents().find('#topline');
    // correct top padding
    var top_margin=10;
    try{
            var top_nav = $('#roundcubeFrame').contents().find('#topnav');
            // check if the above element exits (only in new larry theme, if null use rc 0.7 default theme
            // TODO refactor and move theme check
            if(top_nav.height()!=null){
                    top_margin= 10;
                    //In larry theme with accounts plugin, we have to move the account selection
                    var acc_select = top_line.find('.username');
                    if (acc_select) {
                          mainscreen.find('div#messagetoolbar').attr('id',''); //Quick n dirty for space
                          acc_select.appendTo(mainscreen.find('div#searchfilter')).css({position: 'absolute', top:'0px', left:'-170px'});
                    }
            } else {
                    top_margin= parseInt(mainscreen.css('top'),10)-top_line.height();
            }
    } catch (e) {}
    top_line.remove();

    // fix topbar, issue https://github.com/hypery2k/owncloud/issues/54
    $('#roundcubeFrame').contents().find('.toolbar').css('z-index','80');
    $('#roundcubeFrame').contents().find('.toolbar').css('position','absolute');

    // remove logout button
    $('#roundcubeFrame').contents().find('.button-logout').remove();

    // check if the header menu from roundcube was disabled
    if($('#disable_header_nav').val() === 'true') {

            var top_nav = $('#roundcubeFrame').contents().find('#header');
            // check if the above element exits (only in new larry theme, if null use rc 0.7 default theme
            if(top_nav.height()==null){
                    top_nav = $('#roundcubeFrame').contents().find('#taskbar');
            } else {
                    //top_in= top_margin-top_nav.height();
            }
            top_nav.remove();
		$('#roundcubeFrame').contents().find('#mainscreen').css('top',top_margin);
    } else {
		// correct top padding
		$('#roundcubeFrame').contents().find('#mainscreen').css('top','50px');
	}
	// slide in roundcube nice
	$('#loader').fadeOut('slow');
	$('#roundcubeFrame').slideDown('slow');
	// remove email adresse
	$('#roundcubeFrame').contents().find('.username').remove();

});
<|endoftext|>


<commit_before>define(['underscore', 'knockout', 'models/abstract', 'widgets'], function(_, ko, AbstractModel, widgets) {
    return AbstractModel.extend({
        /**
        * A backbone model to manage cards_x_nodes_x_widgets records
        * @augments AbstractModel
        * @constructor
        * @name CardWidgetModel
        */
        constructor: function(attributes, options){
            var defaults = {
                'id': null,
                'node_id': '',
                'card_id': '',
                'widget_id': '',
                'config': {},
                'label': '',
                'sortorder': null,
                'disabled': false
            };
            var self = this;
            this.widgetLookup = widgets;
            this.widgetList = function() {
                var widgets = _.map(self.widgetLookup, function(widget, id) {
                    widget.id = id;
                    return widget;
                });
                return _.filter(widgets, function(widget) {
                    return widget.datatype === self.datatype.datatype;
                });
            };
            options = options ? options : {};
            attributes = attributes ? attributes : {};
            options.parse = true;
            this.node = (options.node || null);
            this.card = (options.card || null);
            this.datatype = (options.datatype || null);
            this.disabled = (options.disabled || false);
            this.icon = 'ion-ios-paper';
            if (this.datatype) {
                this.icon = this.datatype.iconclass;
            }
            if (this.datatype && this.datatype.defaultwidget_id) {
                defaults.widget_id = this.datatype.defaultwidget_id;
                defaults.config = JSON.parse(widgets[defaults.widget_id].defaultconfig);
            }
            if (this.node) {
                defaults.node_id = this.node.nodeid;
                defaults.label = ko.unwrap(this.node.name);
            }
            if (this.card) {
                defaults.card_id = this.card.get('id');
            }

            attributes = _.defaults(attributes, defaults);

            AbstractModel.prototype.constructor.call(this, attributes, options);

            this.configJSON = ko.computed({
                read: function() {
                    var configJSON = {};
                    var config = this.get('config');
                    _.each(this.configKeys(), function(key) {
                        configJSON[key] = config[key]();
                    });
                    configJSON.label = this.get('label')();
                    return configJSON;
                },
                write: function(value) {
                    var config = this.get('config');
                    for (var key in value) {
                        if (key === 'label') {
                            this.get('label')(value[key]);
                        }
                        if (config[key] && config[key]() !== value[key]) {
                            config[key](value[key]);
                        }
                    }
                },
                owner: this
            });
            this.configJSON.extend({ rateLimit: { timeout: 100, method: "notifyWhenChangesStop" } });

            return this;
        },

        /**
         * parse - parses the passed in attributes into a {@link CardWidgetModel}
         * @memberof CardWidgetModel.prototype
         * @param  {object} attributes - the properties to seed a {@link CardWidgetModel} with
         */
        parse: function(attributes){
            var self = this;

            _.each(attributes, function(value, key){
                if (key === 'config') {
                    if (typeof value === 'string') {
                        value = JSON.parse(value);
                    }
                    var configKeys = [];
                    _.each(value, function(configVal, configKey) {
                        value[configKey] = ko.observable(configVal);
                        configKeys.push(configKey);
                    });
                    this.set(key, value);
                    this.configKeys = ko.observableArray(configKeys);
                } else if (key==='widget_id') {
                    var widgetId = ko.observable(value);
                    this.set(key, ko.computed({
                        read: function() {
                            return widgetId();
                        },
                        write: function(value) {
                            var key;
                            var defaultConfig = JSON.parse(widgets[value].defaultconfig);
                            for (key in defaultConfig) {
                                defaultConfig[key] = ko.observable(defaultConfig[key]);
                            }
                            var currentConfig = this.get('config');
                            this.set('config', _.defaults(currentConfig, defaultConfig));
                            for (key in defaultConfig) {
                                self.configKeys.push(key);
                            }
                            widgetId(value);
                        },
                        owner: this
                    }));
                } else {
                    this.set(key, ko.observable(value));
                }
                this[key] = this.get(key);
            }, this);
        },

        /**
         * toJSON - casts the model as a JSON object
         * @return {object} a JSON object representation of the model
         */
        toJSON: function() {
            var ret = {};
            for (var key in this.attributes){
                if (key !== 'config') {
                    ret[key] = this.attributes[key]();
                } else {
                    ret[key] = this.configJSON();
                }
            }
            return ret;
        }
    });
});
<commit_msg>fixes issue w/ widget configs<commit_after>define(['underscore', 'knockout', 'models/abstract', 'widgets'], function(_, ko, AbstractModel, widgets) {
    return AbstractModel.extend({
        /**
        * A backbone model to manage cards_x_nodes_x_widgets records
        * @augments AbstractModel
        * @constructor
        * @name CardWidgetModel
        */
        constructor: function(attributes, options){
            var defaults = {
                'id': null,
                'node_id': '',
                'card_id': '',
                'widget_id': '',
                'config': {},
                'label': '',
                'sortorder': null,
                'disabled': false
            };
            var self = this;
            this.widgetLookup = widgets;
            this.widgetList = function() {
                var widgets = _.map(self.widgetLookup, function(widget, id) {
                    widget.id = id;
                    return widget;
                });
                return _.filter(widgets, function(widget) {
                    return widget.datatype === self.datatype.datatype;
                });
            };
            options = options ? options : {};
            attributes = attributes ? attributes : {};
            options.parse = true;
            this.node = (options.node || null);
            this.card = (options.card || null);
            this.datatype = (options.datatype || null);
            this.disabled = (options.disabled || false);
            this.icon = 'ion-ios-paper';
            if (this.datatype) {
                this.icon = this.datatype.iconclass;
            }
            if (this.datatype && this.datatype.defaultwidget_id) {
                defaults.widget_id = this.datatype.defaultwidget_id;
                defaults.config = JSON.parse(widgets[defaults.widget_id].defaultconfig);
            }
            if (this.node) {
                defaults.node_id = this.node.nodeid;
                defaults.label = ko.unwrap(this.node.name);
            }
            if (this.card) {
                defaults.card_id = this.card.get('id');
            }

            attributes = _.defaults(attributes, defaults);

            AbstractModel.prototype.constructor.call(this, attributes, options);

            this.configJSON = ko.computed({
                read: function() {
                    var configJSON = {};
                    var config = this.get('config');
                    _.each(this.configKeys(), function(key) {
                        configJSON[key] = config[key]();
                    });
                    configJSON.label = this.get('label')();
                    return configJSON;
                },
                write: function(value) {
                    var config = this.get('config');
                    for (var key in value) {
                        if (key === 'label') {
                            this.get('label')(value[key]);
                        }
                        if (config[key] && config[key]() !== value[key]) {
                            config[key](value[key]);
                        }
                    }
                },
                owner: this
            });
            this.configJSON.extend({ rateLimit: { timeout: 100, method: "notifyWhenChangesStop" } });

            return this;
        },

        /**
         * parse - parses the passed in attributes into a {@link CardWidgetModel}
         * @memberof CardWidgetModel.prototype
         * @param  {object} attributes - the properties to seed a {@link CardWidgetModel} with
         */
        parse: function(attributes){
            var self = this;

            _.each(attributes, function(value, key){
                if (key === 'config') {
                    if (typeof value === 'string') {
                        value = JSON.parse(value);
                    }
                    var configKeys = [];
                    _.each(value, function(configVal, configKey) {
                        if (!ko.isObservable(configVal)) {
                            value[configKey] = ko.observable(configVal);
                        } else {
                            value[configKey] = configVal;
                        }
                        configKeys.push(configKey);
                    });
                    this.set(key, value);
                    this.configKeys = ko.observableArray(configKeys);
                } else if (key==='widget_id') {
                    var widgetId = ko.observable(value);
                    this.set(key, ko.computed({
                        read: function() {
                            return widgetId();
                        },
                        write: function(value) {
                            var key;
                            var defaultConfig = JSON.parse(widgets[value].defaultconfig);
                            for (key in defaultConfig) {
                                defaultConfig[key] = ko.observable(defaultConfig[key]);
                            }
                            var currentConfig = this.get('config');
                            this.set('config', _.defaults(currentConfig, defaultConfig));
                            for (key in defaultConfig) {
                                self.configKeys.push(key);
                            }
                            widgetId(value);
                        },
                        owner: this
                    }));
                } else {
                    this.set(key, ko.observable(value));
                }
                this[key] = this.get(key);
            }, this);
        },

        /**
         * toJSON - casts the model as a JSON object
         * @return {object} a JSON object representation of the model
         */
        toJSON: function() {
            var ret = {};
            for (var key in this.attributes){
                if (key !== 'config') {
                    ret[key] = this.attributes[key]();
                } else {
                    ret[key] = this.configJSON();
                }
            }
            return ret;
        }
    });
});
<|endoftext|>