<commit_before>/* globals process */

import fs from 'fs'
import path from 'path'
import globAll from 'glob-all'
import chokidar from 'chokidar'
import Promise, { promisify } from 'bluebird'

import resolveTarget from './lib/resolve-target'
import sourcesBases from './lib/sources-bases'
import isGlob from './lib/is-glob'
import trimQuotes from './lib/trim-quotes'
import { copyDir, copyFile, remove } from './lib/fs'

Promise.config({ cancellation: true })

const defaults = {
  watch: false,
  delete: true,
  depth: Infinity,
}

/**
 * Synchronise files, directories and/or glob patterns, optionally watching for changes.
 *
 * @param {string|Array.<string>} sources - A list of files, directories and/or glob patterns.
 * @param {string} target - The destination directory.
 * @param {Object} [options] - An optional configuration object.
 * @param {bool} [options.watch=false] - Enable or disable watch mode.
 * @param {bool} [options.delete=true] - Whether to delete the `target`'s content initially.
 * @param {bool} [options.depth=Infinity] - Chokidars `depth` (If set, limits how many levels of subdirectories will be traversed).
 * @param {string} [options.transform=false] - A module path resolved by node's `require`.
 * @param {NotifyCallback} [notify] - An optional notification callback.
 * @returns {CloseFunc} - Returns a close function which cancels active promises and watch mode.
 */
// eslint-disable-next-line consistent-return
const syncGlob = (sources, target, options, notify = () => {}) => {
  if (!Array.isArray(sources)) {
    // eslint-disable-next-line no-param-reassign
    sources = [sources]
  }
  // eslint-disable-next-line no-param-reassign
  sources = sources.map(trimQuotes)
  // eslint-disable-next-line no-param-reassign
  options = {
    ...defaults,
    ...options,
  }

  const notifyError = (err) => { notify('error', err) }
  const bases = sourcesBases(sources)
  const resolveTargetFromBases = resolveTarget(bases)
  const { depth, watch } = options
  let { transform } = options

  if (typeof depth !== 'number' || isNaN(depth)) {
    notifyError('Expected valid number for option "depth"')
    return false
  }

  if (transform) {
    let transformPath = transform

    try {
      require.resolve(transformPath)
    } catch (err) {
      transformPath = path.join(process.cwd(), transformPath)

      try {
        require.resolve(transformPath)
      } catch (err2) {
        notifyError(err2)
      }
    }

    // eslint-disable-next-line
    transform = require(transformPath)
  }

  // Initial mirror
  const mirrorInit = [
    promisify(globAll)(sources.map(source => (isGlob(source) === -1
      && fs.statSync(source).isDirectory() ? `${source}/**` : source))),
  ]

  if (options.delete) {
    mirrorInit.push(remove(target)
      .then(() => {
        notify('remove', target)
      })
      .catch(notifyError)
    )
  } else {
    notify('no-delete', target)
  }

  let mirrorPromiseAll = Promise.all(mirrorInit)
    .then(([files]) => Promise.all(files.map((file) => {
      const resolvedTarget = resolveTargetFromBases(file, target)

      return copyFile(file, resolvedTarget, transform)
        .then(() => {
          notify('copy', [file, resolvedTarget])
        })
        .catch(notifyError)
    })))
    .then(() => {
      notify('mirror', [sources, target])
    })
    .catch(notifyError)
    .finally(() => {
      mirrorPromiseAll = null
    })

  let watcher
  let activePromises = []
  const close = () => {
    if (watcher) {
      watcher.close()
      watcher = null
    }

    if (mirrorPromiseAll) {
      mirrorPromiseAll.cancel()
      mirrorPromiseAll = null
    }

    activePromises.forEach((promise) => {
      promise.cancel()
    })

    activePromises = null
  }

  // Watcher to keep in sync from that
  if (watch) {
    watcher = chokidar.watch(sources, {
      persistent: true,
      depth,
      ignoreInitial: true,
      awaitWriteFinish: true,
    })

    watcher.on('ready', notify.bind(undefined, 'watch', sources))
      .on('all', (event, source) => {
        const resolvedTarget = resolveTargetFromBases(source, target)
        let promise

        switch (event) {
          case 'add':
          case 'change':
            promise = copyFile(source, resolvedTarget, transform)
            break

          case 'addDir':
            promise = copyDir(source, resolvedTarget)
            break

          case 'unlink':
          case 'unlinkDir':
            promise = remove(resolvedTarget)
            break

          default:
            return
        }

        activePromises.push(promise
          .then(() => {
            const eventMap = {
              add: 'copy',
              addDir: 'copy',
              change: 'copy',
              unlink: 'remove',
              unlinkDir: 'remove',
            }

            notify(eventMap[event] || event, [source, resolvedTarget])
          })
          .catch(notifyError)
          .finally(() => {
            if (activePromises) {
              const index = activePromises.indexOf(promise)

              if (index !== -1) {
                activePromises.slice(index, 1)
              }
            }

            promise = null
          })
        )
      })
      .on('error', notifyError)

    process.on('SIGINT', close)
    process.on('SIGQUIT', close)
    process.on('SIGTERM', close)
  }

  return close
}

export default syncGlob

/**
 * This callback notifies you about various steps, like:
 * - **copy:** File or directory has been copied to `target`.
 * - **remove:** File or directory has been removed from `target`.
 * - **no-delete:** No initial deletion of `target`s contents.
 * - **mirror:** Initial copy of all `sources` to `target` done.
 * - **watch:** Watch mode has started.
 * - **error:** Any error which may occurred during program execution.
 *
 * @callback NotifyCallback
 * @param {string} type - The type of notification.
 * @param {...any} args - Event specific variadic arguments.
 */

/**
 * A cleanup function which cancels all active promises and closes watch mode if enabled.
 *
 * @typedef {function} CloseFunc
 */
<commit_msg>made options optional<commit_after>/* globals process */

import fs from 'fs'
import path from 'path'
import globAll from 'glob-all'
import chokidar from 'chokidar'
import Promise, { promisify } from 'bluebird'

import resolveTarget from './lib/resolve-target'
import sourcesBases from './lib/sources-bases'
import isGlob from './lib/is-glob'
import trimQuotes from './lib/trim-quotes'
import { copyDir, copyFile, remove } from './lib/fs'

Promise.config({ cancellation: true })

const defaults = {
  watch: false,
  delete: true,
  depth: Infinity,
}

/**
 * Synchronise files, directories and/or glob patterns, optionally watching for changes.
 *
 * @param {string|Array.<string>} sources - A list of files, directories and/or glob patterns.
 * @param {string} target - The destination directory.
 * @param {Object} [options] - An optional configuration object.
 * @param {bool} [options.watch=false] - Enable or disable watch mode.
 * @param {bool} [options.delete=true] - Whether to delete the `target`'s content initially.
 * @param {bool} [options.depth=Infinity] - Chokidars `depth` (If set, limits how many levels of subdirectories will be traversed).
 * @param {string} [options.transform=false] - A module path resolved by node's `require`.
 * @param {NotifyCallback} [notify] - An optional notification callback.
 * @returns {CloseFunc} - Returns a close function which cancels active promises and watch mode.
 */
// eslint-disable-next-line consistent-return
const syncGlob = (sources, target, options = {}, notify = () => {}) => {
  if (!Array.isArray(sources)) {
    // eslint-disable-next-line no-param-reassign
    sources = [sources]
  }
  // eslint-disable-next-line no-param-reassign
  sources = sources.map(trimQuotes)

  if (typeof options === 'function') {
    // eslint-disable-next-line no-param-reassign
    notify = options
    // eslint-disable-next-line no-param-reassign
    options = {}
  }

  // eslint-disable-next-line no-param-reassign
  options = {
    ...defaults,
    ...options,
  }

  const notifyError = (err) => { notify('error', err) }
  const bases = sourcesBases(sources)
  const resolveTargetFromBases = resolveTarget(bases)
  const { depth, watch } = options
  let { transform } = options

  if (typeof depth !== 'number' || isNaN(depth)) {
    notifyError('Expected valid number for option "depth"')
    return false
  }

  if (transform) {
    let transformPath = transform

    try {
      require.resolve(transformPath)
    } catch (err) {
      transformPath = path.join(process.cwd(), transformPath)

      try {
        require.resolve(transformPath)
      } catch (err2) {
        notifyError(err2)
      }
    }

    // eslint-disable-next-line
    transform = require(transformPath)
  }

  // Initial mirror
  const mirrorInit = [
    promisify(globAll)(sources.map(source => (isGlob(source) === -1
      && fs.statSync(source).isDirectory() ? `${source}/**` : source))),
  ]

  if (options.delete) {
    mirrorInit.push(remove(target)
      .then(() => {
        notify('remove', target)
      })
      .catch(notifyError)
    )
  } else {
    notify('no-delete', target)
  }

  let mirrorPromiseAll = Promise.all(mirrorInit)
    .then(([files]) => Promise.all(files.map((file) => {
      const resolvedTarget = resolveTargetFromBases(file, target)

      return copyFile(file, resolvedTarget, transform)
        .then(() => {
          notify('copy', [file, resolvedTarget])
        })
        .catch(notifyError)
    })))
    .then(() => {
      notify('mirror', [sources, target])
    })
    .catch(notifyError)
    .finally(() => {
      mirrorPromiseAll = null
    })

  let watcher
  let activePromises = []
  const close = () => {
    if (watcher) {
      watcher.close()
      watcher = null
    }

    if (mirrorPromiseAll) {
      mirrorPromiseAll.cancel()
      mirrorPromiseAll = null
    }

    activePromises.forEach((promise) => {
      promise.cancel()
    })

    activePromises = null
  }

  // Watcher to keep in sync from that
  if (watch) {
    watcher = chokidar.watch(sources, {
      persistent: true,
      depth,
      ignoreInitial: true,
      awaitWriteFinish: true,
    })

    watcher.on('ready', notify.bind(undefined, 'watch', sources))
      .on('all', (event, source) => {
        const resolvedTarget = resolveTargetFromBases(source, target)
        let promise

        switch (event) {
          case 'add':
          case 'change':
            promise = copyFile(source, resolvedTarget, transform)
            break

          case 'addDir':
            promise = copyDir(source, resolvedTarget)
            break

          case 'unlink':
          case 'unlinkDir':
            promise = remove(resolvedTarget)
            break

          default:
            return
        }

        activePromises.push(promise
          .then(() => {
            const eventMap = {
              add: 'copy',
              addDir: 'copy',
              change: 'copy',
              unlink: 'remove',
              unlinkDir: 'remove',
            }

            notify(eventMap[event] || event, [source, resolvedTarget])
          })
          .catch(notifyError)
          .finally(() => {
            if (activePromises) {
              const index = activePromises.indexOf(promise)

              if (index !== -1) {
                activePromises.slice(index, 1)
              }
            }

            promise = null
          })
        )
      })
      .on('error', notifyError)

    process.on('SIGINT', close)
    process.on('SIGQUIT', close)
    process.on('SIGTERM', close)
  }

  return close
}

export default syncGlob

/**
 * This callback notifies you about various steps, like:
 * - **copy:** File or directory has been copied to `target`.
 * - **remove:** File or directory has been removed from `target`.
 * - **no-delete:** No initial deletion of `target`s contents.
 * - **mirror:** Initial copy of all `sources` to `target` done.
 * - **watch:** Watch mode has started.
 * - **error:** Any error which may occurred during program execution.
 *
 * @callback NotifyCallback
 * @param {string} type - The type of notification.
 * @param {...any} args - Event specific variadic arguments.
 */

/**
 * A cleanup function which cancels all active promises and closes watch mode if enabled.
 *
 * @typedef {function} CloseFunc
 */
<|endoftext|>


<commit_before>const Promise = require('bluebird');

const { scheduleNextPush } = require('./campaign');
const { getScheduledPushes, getActiveCampaigns } = require('./query');
const { createPushWorkItems } = require('./schedule');
const { addOffsetCounts, markAsComplete } = require('./statusHandler');
const { flatten, logger } = require('./util');
const { queryResults, sender } = require('./batch');

const BATCH_SIZE = parseInt(process.env.BATCH_SIZE) || 300;

module.exports = {
  async sendScheduledPushes(publisher, channel, applicationId, now = new Date()) {
    const pushWorkItems = await Promise.resolve(getScheduledPushes())
      .filter((pushStatus) => markAsComplete(pushStatus, now)
        // Pick only the incomplete pushes
        .then((res) => res === false))
      .tap((pushStatuses) => {
        logger.debug('Incomplete pushes', { pushStatuses: pushStatuses.map((p) => p.toJSON()) });
      })

      .map((pushStatus) => createPushWorkItems(pushStatus, applicationId, now))
      .then(flatten)
      .tap((pushWorkItems) => {
        if (pushWorkItems && pushWorkItems.length > 0) {
          logger.debug('Generated push work items', { pushWorkItems });
        }
      })

      // We set the offsets to prevent resending in the next iteration
      .map((pwi) => addOffsetCounts(pwi.pushStatus, pwi.UTCOffset, now)
        .then(() => Promise.resolve(pwi)));

    const publish = (msg) => publisher.publish(channel, msg);
    for (const pwi of pushWorkItems) {
      const sendPushWorkItems = sender(publish, pwi, BATCH_SIZE);
      await (new Promise((resolve, reject) => {
        const stream = queryResults(pwi.query.where);
        stream.pipe(sendPushWorkItems, { end: true });
        stream.on('end', resolve);
        stream.on('error', reject);
      }));
    }

    return pushWorkItems;
  },

  runCampaigns(now = new Date()) {
    return Promise.resolve(getActiveCampaigns())
      .tap((activeCampaigns) => logger.info(`Found ${activeCampaigns.length} active campaigns`, { activeCampaigns }))
      .map((campaign) => scheduleNextPush(campaign, now));
  },
};
<commit_msg>Unnecessary ES6 Promise to Bluebird conversion<commit_after>const Promise = require('bluebird');

const { scheduleNextPush } = require('./campaign');
const { getScheduledPushes, getActiveCampaigns } = require('./query');
const { createPushWorkItems } = require('./schedule');
const { addOffsetCounts, markAsComplete } = require('./statusHandler');
const { flatten, logger } = require('./util');
const { queryResults, sender } = require('./batch');

const BATCH_SIZE = parseInt(process.env.BATCH_SIZE) || 300;

module.exports = {
  async sendScheduledPushes(publisher, channel, applicationId, now = new Date()) {
    const pushWorkItems = await Promise.resolve(getScheduledPushes())
      .filter((pushStatus) => markAsComplete(pushStatus, now)
        // Pick only the incomplete pushes
        .then((res) => res === false))
      .tap((pushStatuses) => {
        logger.debug('Incomplete pushes', { pushStatuses: pushStatuses.map((p) => p.toJSON()) });
      })

      .map((pushStatus) => createPushWorkItems(pushStatus, applicationId, now))
      .then(flatten)
      .tap((pushWorkItems) => {
        if (pushWorkItems && pushWorkItems.length > 0) {
          logger.debug('Generated push work items', { pushWorkItems });
        }
      })

      // We set the offsets to prevent resending in the next iteration
      .map((pwi) => addOffsetCounts(pwi.pushStatus, pwi.UTCOffset, now));

    const publish = (msg) => publisher.publish(channel, msg);
    for (const pwi of pushWorkItems) {
      const sendPushWorkItems = sender(publish, pwi, BATCH_SIZE);
      await (new Promise((resolve, reject) => {
        const stream = queryResults(pwi.query.where);
        stream.pipe(sendPushWorkItems, { end: true });
        stream.on('end', resolve);
        stream.on('error', reject);
      }));
    }

    return pushWorkItems;
  },

  runCampaigns(now = new Date()) {
    return Promise.resolve(getActiveCampaigns())
      .tap((activeCampaigns) => logger.info(`Found ${activeCampaigns.length} active campaigns`, { activeCampaigns }))
      .map((campaign) => scheduleNextPush(campaign, now));
  },
};
<|endoftext|>


<commit_before>let fs = require('fs')
let crypto = require('crypto')

module.exports = exports = function(files, method) {
  return new Promise(async function(resolve, reject) {
    let hash = crypto.createHash(method).setEncoding('hex')

    if (!Array.isArray(files)) files = [files]

    for (let file of files) {
      let fd = fs.createReadStream(file)
      await doPipe(fd, hash)
    }

    hash.end()
    return resolve(hash.read())
  })
}

function doPipe(fd, hash){
  return new Promise(function(resolve, reject) {
    fd.on('end', function(){
      return resolve()
    })

    fd.on('error', function(err){
      return reject(err)
    })

    fd.on('data', function(data){
      hash.update(data)
    })
  })
}
<commit_msg>More error handling<commit_after>let fs = require('fs')
let crypto = require('crypto')

module.exports = exports = function(files, method) {
  return new Promise(async function(resolve, reject) {
    let hash = crypto.createHash(method).setEncoding('hex')

    if (!Array.isArray(files)) files = [files]

    for (let file of files) {
      let fd = fs.createReadStream(file)

      try {
        await doPipe(fd, hash)
      } catch(err) {
        return reject(err)
      }
    }

    hash.end()
    return resolve(hash.read())
  })
}

function doPipe(fd, hash){
  return new Promise(function(resolve, reject) {
    fd.on('end', function(){
      return resolve()
    })

    fd.on('error', function(err){
      return reject(err)
    })

    fd.on('data', function(data){
      hash.update(data)
    })
  })
}
<|endoftext|>


<commit_before>/* global kuromoji */
import d3 from "d3"
import "kuromoji"

if(1){
  document.write("JavaScriptはONです。");
}

var i,j,k,l,m,n,bunsuu;  //mは段落

var width = 1320,
    height = 660;

var color = d3.scale.category20c();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(120)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

document.getElementById('load-button').addEventListener('click', function () {
        var file = document.getElementById('file-input').files[0];
        var reader = new FileReader();
        reader.onload = function(event) {
            var data = JSON.parse(event.target.result);
            console.log(data);

            kuromoji.builder({dicPath: 'dict/'}).build((err, tokenizer) => {
              const path = tokenizer.tokenize(data[0].a); //1集計単位ごとにこの関数を用いよう
              console.log(path);

              var keitaisokaiseki = new Array; //このlengthは段落数
              var reasonMatrix = new Array;

              var buntou;
              var tegakari;
              var toutencount;
              var toutenbasho=0;
              n=0; //nは全データ内で何文字目か
              bunsuu=0; //全段落内で何分目か
              m=0; //何個目の発言か
              var karabasho;
              var reason;
              var kara="から"
              while(n<path.length){//発言ごとのループ
                keitaisokaiseki[m] = new Array; //一発言
                i=0; //段落内の何文目か。
                while(n<path.length){//文ごとのループ
                  keitaisokaiseki[m][i] = new Array; //文
                  keitaisokaiseki[m][i].length = 0;
                  reasonMatrix[i] = new Array;
                  k=0; //集計単位内で何単語目か
                  while(n<path.length){//単語ごとのループ
                      if(path[n].basic_form=="。"||path[n].basic_form=="？"||path[n].basic_form=="?"||path[n].word_id=="2613630"){
                        bunsuu++;
                        toutencount=0;
                        break;//3区間終了
                      }
                      keitaisokaiseki[m][i][k] = path[n].basic_form;
                      reasonMatrix[i][k] = reason;
                      n++;
                      k++;
                  }
                  if(n==path.length){//確認
                    break;
                  }
                  if(path[n].word_id=="2613630"){
                    n++;
                    break;
                  }//1段落作成完了
                  n++;
                  i++;//段落内の何文目か
                }
                m++;
              }

            //以下、想定していた形態素解析後の結果から、共起ネットワークのノード・エッジ作成へ

            console.log(keitaisokaiseki);

            var tangoset = new Set();

            var tmp=new Array;

            for(m=0;m<keitaisokaiseki.length;++m){
              for(i=0;i<keitaisokaiseki[m].length;++i){
                tmp = keitaisokaiseki[m][i];
                console.log("m=%d,i=%d",m,i);
                console.log(tmp);
                if(keitaisokaiseki[m][i] ==false||keitaisokaiseki[m][i]==new Array){
                  keitaisokaiseki[m][i].length=0;
                  continue;
                }
                for(j=0;j<tmp.length;++j){
                  tangoset.add({name:keitaisokaiseki[m][i][j],
                                group:reasonMatrix[i][j]

                              });

                  }
                }
            }

          var tangosett = new Array;;

          tangosett = Array.from(tangoset).map(function(t) {
            return {t};
            // body...
          });

            var miserables={"nodes":new Array,"links":new Array};

            for(i=0;i<tangosett.length;i++){
              miserables.nodes[i]=tangosett[i].t;
            }

            console.log(miserables.nodes);
            console.log(miserables.nodes.length);

            /*あとはlinksの作成だけ
            まずはlistをつくる*/
            var list = new Array(keitaisokaiseki.length);
            //list作成

            var RGBlist  = new Array(keitaisokaiseki.length);

            //keitaisokaisekiとnodesを照らしあわせる
            for(m=0;m<keitaisokaiseki.length;++m){
              list[m] = new Array(keitaisokaiseki[m].length);
              for(i=0;i<keitaisokaiseki[m].length;++i){
                list[m][i] = new Array(miserables.nodes.length);
                for(j=0;j<keitaisokaiseki[m][i].length;++j){
                  for(k=0;k<miserables.nodes.length;++k){
                    if(keitaisokaiseki[m][i][j]==miserables.nodes[k].name){
                      list[m][i][k]=1;
                      if(keitaisokaiseki[m][i][j]=="母"){
                        console.log("%d発言目の%d文目の%d語目に「母」が出ています",m,i,j);
                      }

                    }
                  }
                }
              }
            }

            console.log(list);
            //listはi*k

            //listからmiserables.linksとlist3をつくる

            var x,y,z;

            var edge=-1;

                for(k=0;k<miserables.nodes.length;++k){

                  for(l=k+1;l<miserables.nodes.length;++l){//別の単語を見る
                    //現在単語2個の組み合わせを選択中

                    //ここから段落を指定して縦になめる

                    for(m=0;i<keitaisokaiseki.length;++m){
                      for(i=0;i<keitaisokaiseki[m].length;++i){
                        x=list[m][i][k];
                        y=list[m][i][l];

                        if(x==1 && y==1){

                          miserables.links.push({source:l,target:k,value:0});
                          edge++;//最初のedgeが0
                          break;
                        }
                      }
                    }
                      //とりあえずエッジつくってbreak
                      //こっからvalueを与える
                    for(m=0;i<keitaisokaiseki.length;++m){
                      for(i=0;i<keitaisokaiseki[m].length;++i){
                        x=list[m][i][k];
                        y=list[m][i][l];
                        if(x==1 && y==1){
                          z = miserables.links[edge].value;
                          z++;
                          miserables.links[edge].value = z;
                        }
                      }
                    }

                    }

                  }

                console.log(miserables.links);

            //以上計算中

            var graph = miserables;

                force
                    .nodes(graph.nodes)
                    .links(graph.links)
                    .start();

                var link = svg.selectAll(".link")
                    .data(graph.links)
                  .enter().append("line")
                    .attr("class", "link")
                    .style("stroke-width", function(d) { return Math.sqrt(d.value); });

              var node = svg.selectAll(".node")
                  .data(graph.nodes)
                .enter().append("text")
                  .attr("class", "node")
                  .style("fill", function(d) {
                    if(d.group==1){
                      return "red";
                    } else{
                      return "blue";
                    }

                  })
                  .call(force.drag)
                  .text(function(d) { return d.name; });

                force.on("tick", function() {
                  link.attr("x1", function(d) { return d.source.x; })
                      .attr("y1", function(d) { return d.source.y; })
                      .attr("x2", function(d) { return d.target.x; })
                      .attr("y2", function(d) { return d.target.y; });

                  node.attr("x", function(d) { return d.x; })
                      .attr("y", function(d) { return d.y; });
                });
                });
        };
        reader.readAsText(file);

        /*clustering*/

  });

  var cluster = require('hierarchical-clustering');
  var colors = [
  [20, 20, 80],
  [22, 22, 90],
  [250, 255, 253],
  [100, 54, 255]
  ];

  // Euclidean distance
  function distance(a, b) {
  var d = 0;
  for (var i = 0; i < a.length; i++) {
  d += Math.pow(a[i] - b[i], 2);
  }
  return Math.sqrt(d);
  }

  // Single-linkage clustering
  function linkage(distances) {
  return Math.min.apply(null, distances);
  }

  var levels = cluster({
  input: colors,
  distance: distance,
  linkage: linkage,
  minClusters: 2, // only want two clusters
  });

  var clusters = levels[levels.length - 1].clusters;
  console.log(clusters);
  // => [ [ 2 ], [ 3, 1, 0 ] ]
  clusters = clusters.map(function (cluster) {
  return cluster.map(function (index) {
  return colors[index];
  });
  });
  console.log(clusters);
  // => [ [ [ 250, 255, 253 ] ],
  // => [ [ 100, 54, 255 ], [ 22, 22, 90 ], [ 20, 20, 80 ] ] ]

//ドットで区切る
//単語グループ分け -> 形態素解析時点で。

//クライアントの各文の得点換算
//stackdataArr完成

          var stackdataArr = [
              [{x:1,y:3},{x:2,y:5},{x:3,y:7},{x:4,y:3},{x:5,y:5},{x:6,y:7}],
              [{x:1,y:2},{x:2,y:4},{x:3,y:9},{x:4,y:7},{x:5,y:6},{x:6,y:4}],
              [{x:1,y:1},{x:2,y:8},{x:3,y:5},{x:4,y:3},{x:5,y:5},{x:6,y:7}],
              [{x:1,y:18},{x:2,y:7},{x:3,y:3},{x:4,y:11},{x:5,y:8},{x:6,y:6}]
          ];
          var stack = d3.layout.stack()
              .x(function(d){return 1;})
              .y(function(d){return d.y;})
              .values(function(d){return d;});
          var stackdata = stack(stackdataArr);
          var max = d3.max(stackdata[stackdata.length - 1], function(d){return d.y + d.y0;});
          var scaleX = d3.scale.linear().domain([0,6]).range([0,width/2]);
          var scaleY = d3.scale.linear().domain([0,max]).range([0,200]);
          var colors = ["lightgray","#77ff77","#ff7777","#7777ff","yellow","#0f0","green"];
          var colors2 = ["gray","#d4d","#d4d","gray","#d4d","#d4d","gray"];
          var area = d3.svg.area()
              .x(function(d,i){return i * width/10})
              .y0(function(d){return 200})
              .y1(function(d){return 200 - scaleY(d.y+d.y0)});
          svg.selectAll("path")
              .data(stackdata.reverse())
              .enter()
              .append("path")
              .attr("d", area)
              .attr("fill",function(d,i){return colors[i]});

              //grid line
              //引数はstart,stop,stepの順
              //[190,170,150,130,110,90,70,50,30,10]と同等
            var range = d3.range((width/2)-(width/20),4,-width/10);
            svg.selectAll("line.v")
              .data(range).enter().append("line")
              .attr("x1", function(d,i){return d;}).attr("y1", 0)
              .attr("x2", function(d,i){return d;}).attr("y2", 200);
            svg.selectAll("line")
              .attr("stroke", function(d,i){return colors2[i]})
              .attr("stroke-width", 10)

//チェックボックス

              function boxCheck(){

                //チェックされた項目を記録する変数
                var str="";

                //for文でチェックボックスを１つずつ確認
                for( i=0; i<6; i++ )
                {
                  //チェックされているか確認する
                  if( document.chbox.elements[i].checked )
                  {
                    //変数strが空でない時、区切りのコンマを入れる
                    if( str != "" ) str=str+",";

                    //チェックボックスのvalue値を変数strに入れる
                    str=str+document.chbox.elements[i].value;
                  }
                }

                //strが空の時、警告を出す
                if( str=="" ){
                   alert( "どれか選択してください。" );
                }else{
                  alert( str + "が選択されました。" );
                }
              }
<commit_msg>co-occurence map ignored<commit_after>/* global kuromoji */
import d3 from "d3"
import "kuromoji"

if(1){
  document.write("JavaScriptはONです。");
}

var i,j,k,l,m,n,bunsuu;  //mは段落

var width = 1320,
    height = 660;

var color = d3.scale.category20c();

var force = d3.layout.force()
    .charge(-120)
    .linkDistance(120)
    .size([width, height]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

document.getElementById('load-button').addEventListener('click', function () {
        var file = document.getElementById('file-input').files[0];
        var reader = new FileReader();
        reader.onload = function(event) {
            var data = JSON.parse(event.target.result);
            console.log(data);

            kuromoji.builder({dicPath: 'dict/'}).build((err, tokenizer) => {
              const path = tokenizer.tokenize(data[0].a); //1集計単位ごとにこの関数を用いよう
              console.log(path);

              var keitaisokaiseki = new Array; //このlengthは段落数
              var reasonMatrix = new Array;

              var buntou;
              var tegakari;
              var toutencount;
              var toutenbasho=0;
              n=0; //nは全データ内で何文字目か
              bunsuu=0; //全段落内で何分目か
              m=0; //何個目の発言か
              var karabasho;
              var reason;
              var kara="から"
              while(n<path.length){//発言ごとのループ
                keitaisokaiseki[m] = new Array; //一発言
                i=0; //段落内の何文目か。
                while(n<path.length){//文ごとのループ
                  keitaisokaiseki[m][i] = new Array; //文
                  keitaisokaiseki[m][i].length = 0;
                  reasonMatrix[i] = new Array;
                  k=0; //集計単位内で何単語目か
                  while(n<path.length){//単語ごとのループ
                      if(path[n].basic_form=="。"||path[n].basic_form=="？"||path[n].basic_form=="?"||path[n].word_id=="2613630"){
                        bunsuu++;
                        toutencount=0;
                        break;//3区間終了
                      }
                      keitaisokaiseki[m][i][k] = path[n].basic_form;
                      reasonMatrix[i][k] = reason;
                      n++;
                      k++;
                  }
                  if(n==path.length){//確認
                    break;
                  }
                  if(path[n].word_id=="2613630"){
                    n++;
                    break;
                  }//1段落作成完了
                  n++;
                  i++;//段落内の何文目か
                }
                m++;
              }

            //以下、想定していた形態素解析後の結果から、共起ネットワークのノード・エッジ作成へ

            console.log(keitaisokaiseki);

            var tangoset = new Set();

            var tmp=new Array;

            for(m=0;m
              <keitaisokaiseki.length;++m){
              for(i=0;i<keitaisokaiseki[m].length;++i){
                tmp = keitaisokaiseki[m][i];
                console.log("m=%d,i=%d",m,i);
                console.log(tmp);
                if(keitaisokaiseki[m][i] ==false||keitaisokaiseki[m][i]==new Array){
                  keitaisokaiseki[m][i].length=0;
                  continue;
                }
                for(j=0;j<tmp.length;++j){
                  tangoset.add({name:keitaisokaiseki[m][i][j],
                                group:reasonMatrix[i][j]

                              });

                  }
                }
            }

          var tangosett = new Array;;

          tangosett = Array.from(tangoset).map(function(t) {
            return {t};
            // body...
          });

            var miserables={"nodes":new Array,"links":new Array};

            for(i=0;i<tangosett.length;i++){
              miserables.nodes[i]=tangosett[i].t;
            }

            console.log(miserables.nodes);
            console.log(miserables.nodes.length);

            /*あとはlinksの作成だけ
            まずはlistをつくる*/
            var list = new Array(keitaisokaiseki.length);
            //list作成

            var RGBlist  = new Array(keitaisokaiseki.length);

            //keitaisokaisekiとnodesを照らしあわせる
            for(m=0;m<keitaisokaiseki.length;++m){
              list[m] = new Array(keitaisokaiseki[m].length);
              for(i=0;i<keitaisokaiseki[m].length;++i){
                list[m][i] = new Array(miserables.nodes.length);
                for(j=0;j<keitaisokaiseki[m][i].length;++j){
                  for(k=0;k<miserables.nodes.length;++k){
                    if(keitaisokaiseki[m][i][j]==miserables.nodes[k].name){
                      list[m][i][k]=1;
                      if(keitaisokaiseki[m][i][j]=="母"){
                        console.log("%d発言目の%d文目の%d語目に「母」が出ています",m,i,j);
                      }

                    }
                  }
                }
              }
            }

            console.log(list);
            //listはi*k

            //listからmiserables.linksとlist3をつくる

            var x,y,z;

            var edge=-1;

                for(k=0;k<miserables.nodes.length;++k){

                  for(l=k+1;l<miserables.nodes.length;++l){//別の単語を見る
                    //現在単語2個の組み合わせを選択中

                    //ここから段落を指定して縦になめる

                    for(m=0;i<keitaisokaiseki.length;++m){
                      for(i=0;i<keitaisokaiseki[m].length;++i){
                        x=list[m][i][k];
                        y=list[m][i][l];

                        if(x==1 && y==1){

                          miserables.links.push({source:l,target:k,value:0});
                          edge++;//最初のedgeが0
                          break;
                        }
                      }
                    }
                      //とりあえずエッジつくってbreak
                      //こっからvalueを与える
                    for(m=0;i<keitaisokaiseki.length;++m){
                      for(i=0;i<keitaisokaiseki[m].length;++i){
                        x=list[m][i][k];
                        y=list[m][i][l];
                        if(x==1 && y==1){
                          z = miserables.links[edge].value;
                          z++;
                          miserables.links[edge].value = z;
                        }
                      }
                    }

                    }

                  }

                console.log(miserables.links);

            //以上計算中

            var graph = miserables;

//共起マップ
/*
                force
                    .nodes(graph.nodes)
                    .links(graph.links)
                    .start();

*/
/*
                var link = svg.selectAll(".link")
                    .data(graph.links)
                  .enter().append("line")
                    .attr("class", "link")
                    .style("stroke-width", function(d) { return Math.sqrt(d.value); });
*/
/*              var node = svg.selectAll(".node")
                  .data(graph.nodes)
                .enter().append("text")
                  .attr("class", "node")
                  .style("fill", function(d) {
                    if(d.group==1){
                      return "red";
                    } else{
                      return "blue";
                    }

                  })
                  .call(force.drag)
                  .text(function(d) { return d.name; });
*/

/*
                force.on("tick", function() {
                  link.attr("x1", function(d) { return d.source.x; })
                      .attr("y1", function(d) { return d.source.y; })
                      .attr("x2", function(d) { return d.target.x; })
                      .attr("y2", function(d) { return d.target.y; });

                  node.attr("x", function(d) { return d.x; })
                      .attr("y", function(d) { return d.y; });
                });

*/

                });
        };
        reader.readAsText(file);

        /*clustering*/

  });

  var cluster = require('hierarchical-clustering');
  var colors = [
  [20, 20, 80],
  [22, 22, 90],
  [250, 255, 253],
  [100, 54, 255]
  ];

  // Euclidean distance
  function distance(a, b) {
  var d = 0;
  for (var i = 0; i < a.length; i++) {
  d += Math.pow(a[i] - b[i], 2);
  }
  return Math.sqrt(d);
  }

  // Single-linkage clustering
  function linkage(distances) {
  return Math.min.apply(null, distances);
  }

  var levels = cluster({
  input: colors,
  distance: distance,
  linkage: linkage,
  minClusters: 2, // only want two clusters
  });

  var clusters = levels[levels.length - 1].clusters;
  console.log(clusters);
  // => [ [ 2 ], [ 3, 1, 0 ] ]
  clusters = clusters.map(function (cluster) {
  return cluster.map(function (index) {
  return colors[index];
  });
  });
  console.log(clusters);
  // => [ [ [ 250, 255, 253 ] ],
  // => [ [ 100, 54, 255 ], [ 22, 22, 90 ], [ 20, 20, 80 ] ] ]

//ドットで区切る
//単語グループ分け -> 形態素解析時点で。

//クライアントの各文の得点換算
//stackdataArr完成

          var stackdataArr = [
              [{x:1,y:3},{x:2,y:5},{x:3,y:7},{x:4,y:3},{x:5,y:5},{x:6,y:7}],
              [{x:1,y:2},{x:2,y:4},{x:3,y:9},{x:4,y:7},{x:5,y:6},{x:6,y:4}],
              [{x:1,y:1},{x:2,y:8},{x:3,y:5},{x:4,y:3},{x:5,y:5},{x:6,y:7}],
              [{x:1,y:18},{x:2,y:7},{x:3,y:3},{x:4,y:11},{x:5,y:8},{x:6,y:6}]
          ];
          var stack = d3.layout.stack()
              .x(function(d){return 1;})
              .y(function(d){return d.y;})
              .values(function(d){return d;});
          var stackdata = stack(stackdataArr);
          var max = d3.max(stackdata[stackdata.length - 1], function(d){return d.y + d.y0;});
          var scaleX = d3.scale.linear().domain([0,6]).range([0,width/2]);
          var scaleY = d3.scale.linear().domain([0,max]).range([0,200]);
          var colors = ["lightgray","#77ff77","#ff7777","#7777ff","yellow","#0f0","green"];
          var colors2 = ["gray","#d4d","#d4d","gray","#d4d","#d4d","gray"];
          var area = d3.svg.area()
              .x(function(d,i){return i * width/10})
              .y0(function(d){return 200})
              .y1(function(d){return 200 - scaleY(d.y+d.y0)});
          svg.selectAll("path")
              .data(stackdata.reverse())
              .enter()
              .append("path")
              .attr("d", area)
              .attr("fill",function(d,i){return colors[i]});

              //grid line
              //引数はstart,stop,stepの順
              //[190,170,150,130,110,90,70,50,30,10]と同等
            var range = d3.range((width/2)-(width/20),4,-width/10);
            svg.selectAll("line.v")
              .data(range).enter().append("line")
              .attr("x1", function(d,i){return d;}).attr("y1", 0)
              .attr("x2", function(d,i){return d;}).attr("y2", 200);
            svg.selectAll("line")
              .attr("stroke", function(d,i){return colors2[i]})
              .attr("stroke-width", 10)

//チェックボックス

              function boxCheck(){

                //チェックされた項目を記録する変数
                var str="";

                //for文でチェックボックスを１つずつ確認
                for( i=0; i<6; i++ )
                {
                  //チェックされているか確認する
                  if( document.chbox.elements[i].checked )
                  {
                    //変数strが空でない時、区切りのコンマを入れる
                    if( str != "" ) str=str+",";

                    //チェックボックスのvalue値を変数strに入れる
                    str=str+document.chbox.elements[i].value;
                  }
                }

                //strが空の時、警告を出す
                if( str=="" ){
                   alert( "どれか選択してください。" );
                }else{
                  alert( str + "が選択されました。" );
                }
              }
<|endoftext|>


<commit_before>/* @flow */

import assert from 'assert'
import { getByteLength, getClass, getIncompleteClass } from './helpers'

type Options = {
  strict: boolean,
}

function getClassNamespace(item: any, scope: Object) {
  for (const key in scope) {
    if ({}.hasOwnProperty.call(scope, key) && scope[key] === item.constructor) {
      return key
    }
  }
  return item.constructor.name
}

function serialize(item: any, scope: Object = {}): string {
  const type = typeof item
  if (item === null) {
    return 'N;'
  }
  if (type === 'number') {
    if (item === parseInt(item, 10)) {
      return `i:${item};`
    }
    return `d:${item.toString().toUpperCase()};`
  }
  if (type === 'string') {
    return `s:${getByteLength(item)}:"${item}";`
  }
  if (type === 'boolean') {
    return `b:${item ? '1' : '0'};`
  }
  if (type !== 'object') {
    throw new TypeError()
  }

  const isArray = Array.isArray(item)
  if (isArray || item.constructor.name === 'Object') {
    // Array or raw object
    const toReturn = []
    let size = 0
    for (const key in item) {
      if ({}.hasOwnProperty.call(item, key) && (key !== 'length' || isArray)) {
        size++
        const value = item[key]
        const saneKey = isArray ? parseInt(key, 10) : key
        toReturn.push(serialize(saneKey), serialize(value))
      }
    }
    toReturn.unshift(`a:${size}:{`)
    toReturn.push('}')
    return toReturn.join('')
  }
  if (typeof item.serialize === 'function') {
    const serialized = item.serialize()
    const constructorName = item.__PHP_Incomplete_Class_Name || getClassNamespace(item, scope)
    assert(typeof serialized === 'string', `${item.constructor.name}.serialize should return a string`)
    return `C:${constructorName.length}:"${constructorName}":${serialized.length}:{${serialized}}`
  }
  const items = []
  const constructorName = item.__PHP_Incomplete_Class_Name || getClassNamespace(item, scope)
  for (const key in item) {
    if ({}.hasOwnProperty.call(item, key) && typeof item[key] !== 'function') {
      const value = item[key]
      items.push(serialize(key))
      items.push(serialize(value))
    }
  }
  return `O:${constructorName.length}:"${constructorName}":${items.length / 2}:{${items.join('')}}`
}

function unserializeItem(item: Buffer, startIndex: number, scope: Object, options: Options): { index: number, value: any } {
  let currentIndex = startIndex
  const type = item.toString('utf8', currentIndex, currentIndex + 1)
  // Increment for the type and colon (or semi-colon for null) characters
  currentIndex += 2

  if (type === 'N') {
    // Null
    return { index: currentIndex, value: null }
  }
  if (type === 'i' || type === 'd') {
    // Integer or Double (aka float)
    const valueEnd = item.indexOf(';', currentIndex)
    const value = item.toString('utf8', currentIndex, valueEnd)
    // +1 because of extra semi-colon at the end
    currentIndex += value.length + 1
    return { index: currentIndex, value: type === 'i' ? parseInt(value, 10) : parseFloat(value) }
  }
  if (type === 'b') {
    // Boolean
    const value = item.toString('utf8', currentIndex, currentIndex + 1)
    // +2 for 1 digital value and a semi-colon
    currentIndex += 2
    return { index: currentIndex, value: value === '1' }
  }
  if (type === 's') {
    // String
    const lengthEnd = item.indexOf(':', currentIndex)
    const length = parseInt(item.slice(currentIndex, lengthEnd), 10) || 0
    // +2 because of colon and starting of inverted commas at start of string
    currentIndex = lengthEnd + 2
    const value = item.toString('utf8', currentIndex, currentIndex + length)
    // +2 because of closing of inverted commas at end of string, and extra semi-colon
    currentIndex += length + 2

    return { index: currentIndex, value }
  }
  if (type === 'C') {
    // Serializable class
    const classNameLengthEnd = item.indexOf(':', currentIndex)
    const classNameLength = parseInt(item.toString('utf8', currentIndex, classNameLengthEnd), 10) || 0

    // +2 for : and start of inverted commas for class name
    currentIndex = classNameLengthEnd + 2
    const className = item.toString('utf8', currentIndex, currentIndex + classNameLength)
    // +2 for end of inverted commas and colon before inner content length
    currentIndex += classNameLength + 2

    const contentLengthEnd = item.indexOf(':', currentIndex)
    const contentLength = parseInt(item.toString('utf8', currentIndex, contentLengthEnd), 10) || 0
    // +2 for : and { at start of inner content
    currentIndex = contentLengthEnd + 2

    const classContent = item.toString('utf8', currentIndex, currentIndex + contentLength)
    // +1 for the } at end of inner content
    currentIndex += contentLength + 1

    const container = getClassReference(className, scope, options.strict)
    if (container.constructor.name !== '__PHP_Incomplete_Class') {
      assert(
        typeof container.unserialize === 'function',
        `${container.constructor.name.toLowerCase()}.unserialize is not a function`,
      )
      // console.log('classContent', classContent)
      container.unserialize(classContent)
    }
    return { index: currentIndex, value: container }
  }
  if (type === 'a') {
    // Array or Object
    let first = true
    let container = []
    const lengthEnd = item.indexOf(':', currentIndex)
    const length = parseInt(item.toString('utf8', currentIndex, lengthEnd), 10) || 0

    // +2 for ":{" before the start of object
    currentIndex = lengthEnd + 2
    currentIndex = unserializeObject(
      item,
      currentIndex,
      length,
      scope,
      function(key, value) {
        if (first) {
          container = parseInt(key, 10) === 0 ? [] : {}
          first = false
        }
        container[key] = value
      },
      options,
    )

    // +1 for the last } at the end of object
    currentIndex++
    return { index: currentIndex, value: container }
  }
  if (type === 'O') {
    // Non-Serializable Class
    const classNameLengthEnd = item.indexOf(':', currentIndex)
    const classNameLength = parseInt(item.toString('utf8', currentIndex, classNameLengthEnd), 10) || 0

    // +2 for : and start of inverted commas for class name
    currentIndex = classNameLengthEnd + 2
    const className = item.toString('utf8', currentIndex, currentIndex + classNameLength)
    // +2 for end of inverted commas and colon before inner content length
    currentIndex += classNameLength + 2

    const contentLengthEnd = item.indexOf(':', currentIndex)
    const contentLength = parseInt(item.toString('utf8', currentIndex, contentLengthEnd), 10) || 0
    // +2 for : and { at start of object
    currentIndex = contentLengthEnd + 2

    const container = getClassReference(className, scope, options.strict)
    currentIndex = unserializeObject(
      item,
      currentIndex,
      contentLength,
      scope,
      function(key, value) {
        container[key] = value
      },
      options,
    )
    // +1 for the last } at the end of object
    currentIndex += 1
    return { index: currentIndex, value: container }
  }
  throw new SyntaxError()
}

function getClassReference(className: string, scope: Object, strict: boolean): Object {
  let container
  const classReference = scope[className]
  if (!classReference) {
    if (strict) {
      assert(false, `Class ${className} not found in given scope`)
    }
    container = getIncompleteClass(className)
  } else {
    container = new (getClass(scope[className].prototype))()
  }
  return container
}

function unserializeObject(
  item: Buffer,
  startIndex: number,
  length: number,
  scope: Object,
  valueCallback: Function,
  options: Options,
): number {
  let key = null
  let currentIndex = startIndex

  for (let i = 0; i < length * 2; ++i) {
    const entry = unserializeItem(item, currentIndex, scope, options)
    if (key !== null) {
      valueCallback(key, entry.value)
      key = null
    } else {
      key = entry.value
    }
    currentIndex = entry.index
  }

  return currentIndex
}

function unserialize(item: string, scope: Object = {}, givenOptions: Object = {}): any {
  const options = Object.assign({}, givenOptions)
  if (typeof options.strict === 'undefined') {
    options.strict = true
  }
  return unserializeItem(Buffer.from(item), 0, scope, options).value
}

module.exports = { serialize, unserialize }
<commit_msg>Pass scope for serialize nested objects<commit_after>/* @flow */

import assert from 'assert'
import { getByteLength, getClass, getIncompleteClass } from './helpers'

type Options = {
  strict: boolean,
}

function getClassNamespace(item: any, scope: Object) {
  for (const key in scope) {
    if ({}.hasOwnProperty.call(scope, key) && scope[key] === item.constructor) {
      return key
    }
  }
  return item.constructor.name
}

function serialize(item: any, scope: Object = {}): string {
  const type = typeof item
  if (item === null) {
    return 'N;'
  }
  if (type === 'number') {
    if (item === parseInt(item, 10)) {
      return `i:${item};`
    }
    return `d:${item.toString().toUpperCase()};`
  }
  if (type === 'string') {
    return `s:${getByteLength(item)}:"${item}";`
  }
  if (type === 'boolean') {
    return `b:${item ? '1' : '0'};`
  }
  if (type !== 'object') {
    throw new TypeError()
  }

  const isArray = Array.isArray(item)
  if (isArray || item.constructor.name === 'Object') {
    // Array or raw object
    const toReturn = []
    let size = 0
    for (const key in item) {
      if ({}.hasOwnProperty.call(item, key) && (key !== 'length' || isArray)) {
        size++
        const value = item[key]
        const saneKey = isArray ? parseInt(key, 10) : key
        toReturn.push(serialize(saneKey), serialize(value))
      }
    }
    toReturn.unshift(`a:${size}:{`)
    toReturn.push('}')
    return toReturn.join('')
  }
  if (typeof item.serialize === 'function') {
    const serialized = item.serialize()
    const constructorName = item.__PHP_Incomplete_Class_Name || getClassNamespace(item, scope)
    assert(typeof serialized === 'string', `${item.constructor.name}.serialize should return a string`)
    return `C:${constructorName.length}:"${constructorName}":${serialized.length}:{${serialized}}`
  }
  const items = []
  const constructorName = item.__PHP_Incomplete_Class_Name || getClassNamespace(item, scope)
  for (const key in item) {
    if ({}.hasOwnProperty.call(item, key) && typeof item[key] !== 'function') {
      const value = item[key]
      items.push(serialize(key, scope))
      items.push(serialize(value, scope))
    }
  }
  return `O:${constructorName.length}:"${constructorName}":${items.length / 2}:{${items.join('')}}`
}

function unserializeItem(item: Buffer, startIndex: number, scope: Object, options: Options): { index: number, value: any } {
  let currentIndex = startIndex
  const type = item.toString('utf8', currentIndex, currentIndex + 1)
  // Increment for the type and colon (or semi-colon for null) characters
  currentIndex += 2

  if (type === 'N') {
    // Null
    return { index: currentIndex, value: null }
  }
  if (type === 'i' || type === 'd') {
    // Integer or Double (aka float)
    const valueEnd = item.indexOf(';', currentIndex)
    const value = item.toString('utf8', currentIndex, valueEnd)
    // +1 because of extra semi-colon at the end
    currentIndex += value.length + 1
    return { index: currentIndex, value: type === 'i' ? parseInt(value, 10) : parseFloat(value) }
  }
  if (type === 'b') {
    // Boolean
    const value = item.toString('utf8', currentIndex, currentIndex + 1)
    // +2 for 1 digital value and a semi-colon
    currentIndex += 2
    return { index: currentIndex, value: value === '1' }
  }
  if (type === 's') {
    // String
    const lengthEnd = item.indexOf(':', currentIndex)
    const length = parseInt(item.slice(currentIndex, lengthEnd), 10) || 0
    // +2 because of colon and starting of inverted commas at start of string
    currentIndex = lengthEnd + 2
    const value = item.toString('utf8', currentIndex, currentIndex + length)
    // +2 because of closing of inverted commas at end of string, and extra semi-colon
    currentIndex += length + 2

    return { index: currentIndex, value }
  }
  if (type === 'C') {
    // Serializable class
    const classNameLengthEnd = item.indexOf(':', currentIndex)
    const classNameLength = parseInt(item.toString('utf8', currentIndex, classNameLengthEnd), 10) || 0

    // +2 for : and start of inverted commas for class name
    currentIndex = classNameLengthEnd + 2
    const className = item.toString('utf8', currentIndex, currentIndex + classNameLength)
    // +2 for end of inverted commas and colon before inner content length
    currentIndex += classNameLength + 2

    const contentLengthEnd = item.indexOf(':', currentIndex)
    const contentLength = parseInt(item.toString('utf8', currentIndex, contentLengthEnd), 10) || 0
    // +2 for : and { at start of inner content
    currentIndex = contentLengthEnd + 2

    const classContent = item.toString('utf8', currentIndex, currentIndex + contentLength)
    // +1 for the } at end of inner content
    currentIndex += contentLength + 1

    const container = getClassReference(className, scope, options.strict)
    if (container.constructor.name !== '__PHP_Incomplete_Class') {
      assert(
        typeof container.unserialize === 'function',
        `${container.constructor.name.toLowerCase()}.unserialize is not a function`,
      )
      // console.log('classContent', classContent)
      container.unserialize(classContent)
    }
    return { index: currentIndex, value: container }
  }
  if (type === 'a') {
    // Array or Object
    let first = true
    let container = []
    const lengthEnd = item.indexOf(':', currentIndex)
    const length = parseInt(item.toString('utf8', currentIndex, lengthEnd), 10) || 0

    // +2 for ":{" before the start of object
    currentIndex = lengthEnd + 2
    currentIndex = unserializeObject(
      item,
      currentIndex,
      length,
      scope,
      function(key, value) {
        if (first) {
          container = parseInt(key, 10) === 0 ? [] : {}
          first = false
        }
        container[key] = value
      },
      options,
    )

    // +1 for the last } at the end of object
    currentIndex++
    return { index: currentIndex, value: container }
  }
  if (type === 'O') {
    // Non-Serializable Class
    const classNameLengthEnd = item.indexOf(':', currentIndex)
    const classNameLength = parseInt(item.toString('utf8', currentIndex, classNameLengthEnd), 10) || 0

    // +2 for : and start of inverted commas for class name
    currentIndex = classNameLengthEnd + 2
    const className = item.toString('utf8', currentIndex, currentIndex + classNameLength)
    // +2 for end of inverted commas and colon before inner content length
    currentIndex += classNameLength + 2

    const contentLengthEnd = item.indexOf(':', currentIndex)
    const contentLength = parseInt(item.toString('utf8', currentIndex, contentLengthEnd), 10) || 0
    // +2 for : and { at start of object
    currentIndex = contentLengthEnd + 2

    const container = getClassReference(className, scope, options.strict)
    currentIndex = unserializeObject(
      item,
      currentIndex,
      contentLength,
      scope,
      function(key, value) {
        container[key] = value
      },
      options,
    )
    // +1 for the last } at the end of object
    currentIndex += 1
    return { index: currentIndex, value: container }
  }
  throw new SyntaxError()
}

function getClassReference(className: string, scope: Object, strict: boolean): Object {
  let container
  const classReference = scope[className]
  if (!classReference) {
    if (strict) {
      assert(false, `Class ${className} not found in given scope`)
    }
    container = getIncompleteClass(className)
  } else {
    container = new (getClass(scope[className].prototype))()
  }
  return container
}

function unserializeObject(
  item: Buffer,
  startIndex: number,
  length: number,
  scope: Object,
  valueCallback: Function,
  options: Options,
): number {
  let key = null
  let currentIndex = startIndex

  for (let i = 0; i < length * 2; ++i) {
    const entry = unserializeItem(item, currentIndex, scope, options)
    if (key !== null) {
      valueCallback(key, entry.value)
      key = null
    } else {
      key = entry.value
    }
    currentIndex = entry.index
  }

  return currentIndex
}

function unserialize(item: string, scope: Object = {}, givenOptions: Object = {}): any {
  const options = Object.assign({}, givenOptions)
  if (typeof options.strict === 'undefined') {
    options.strict = true
  }
  return unserializeItem(Buffer.from(item), 0, scope, options).value
}

module.exports = { serialize, unserialize }
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Ads DOM Remover
// @namespace    sagiegurari
// @version      0.82
// @author       Sagie Gur-Ari
// @description  Removes Ad Containers from DOM (doesn't replace adblocker extension, but blocks dynamic content which the adblocker fails to block by removing whole sections from the HTML DOM.)
// @homepage     https://github.com/sagiegurari/userscripts-ads-dom-remover
// @supportURL   https://github.com/sagiegurari/userscripts-ads-dom-remover/issues
// @match        http://www.ynet.co.il/home/*
// @match        http://www.ynet.co.il/articles/*
// @match        http://www.mynet.co.il/articles/*
// @match        http://www.calcalist.co.il/*
// @match        http://www.globes.co.il/*
// @match        https://sourceforge.net/projects/*/download*
// @match        http://subscenter.cinemast.com/*
// @match        https://*.wikipedia.org/*
// @require      https://code.jquery.com/jquery-2.2.2.min.js
// @require      https://greasyfork.org/scripts/18490-ads-dom-remover-runner/code/Ads%20DOM%20Remover%20Runner.js?version=123541
// @grant        none
// @license      MIT License
// ==/UserScript==

(function run($, runner) {
    'use strict';

    var selectorDefinitions = {
        ynet: [
            '#colorbox',
            '#cboxOverlay',
            '#ads.premium',
            '#articleLayoutrightsidtable',
            'img[src*="dynamicyield"]',
            'div.MSCmainContent',
            '[id*="arketingCarouse"]',
            '[id*="arketingRecommended"]',
            '.mainVerticalArticleSharingLinks',
            '.OUTBRAIN',
            '.topBannerWrap',
            '.block.B3 .B3.ghcite.dyother.dyMonitor div',
            '.bigdealhomepage',
            '#ww6s_Main',
            '.buyandsavedy',
            '.area.footer.ghcite',
            '.hdr_set_homepage',
            '#c1_Hor',
            '#c2_Hor',
            '#c3_Hor',
            '#c4_Hor',
            '#c5_Hor',
            '#c6_Hor',
            '.homepagevideo-x6',
            '.buyandsave',
            '.general-image',
            '.PhotoArticlesTalkbacks',
            '[name="ExternalWebpageIframe"]',
            '#PROCOIL_SearchForm',
            '#magazines1024',
            '[id^="promo_"]',
            '.main_search_radio',
            'tr td [id^="ads."]',
            {
                selector: '.homepagelitevideo',
                fineTuneSelector: function ($element) {
                    return $element.parent().parent();
                }
            },
            {
                selector: 'iframe',
                filter: function ($element) {
                    return !$element.parent().hasClass('news_ticker_iframe');
                }
            },
            {
                selector: 'div.B2b.block div',
                pre: function ($element) {
                    $element.parent().css({
                        height: '1px'
                    });
                }
            }
        ],
        globes: [
            '#chromeWindow',
            {
                selector: 'iframe',
                filter: function ($element) {
                    var id = $element.attr('id');
                    var src = $element.attr('src') || '';

                    return (id !== 'GlobalFinanceData_home') && (src.indexOf('/news/') !== -1);
                }
            }
        ],
        sourceforge: [
            '#content-for-adblock'
        ],
        subscenter: [
            '.weekBottom',
            '.reviewsWindow',
            '#banner_cinemast',
            '.bottomMenu',
            'footer',
            '#paypal',
            '#aboveSite'
        ],
        wikipedia: [
            '#frbanner',
            '.frbanner'
        ]
    };

    [
        '#dcPremiumRightImg',
        '.boulevard',
        '#multiarticles-9',
        '#multiarticles-12',
        '#multiarticles-13',
        '#multiarticles-14',
        '#multiarticles-15',
        '#multiarticles-16'
    ].forEach(function addSelector(selector) {
        selectorDefinitions.ynet.push({
            selector: selector,
            fineTuneSelector: function ($element) {
                return $element.parent();
            }
        });
    });

    Object.keys(selectorDefinitions).forEach(function (id) {
        selectorDefinitions[id] = {
            id: id,
            selectors: selectorDefinitions[id]
        };
    });

    runner($, {
        getSelectors: function (hostName) {
            var selectors;
            if (hostName.indexOf('globes') !== -1) {
                selectors = selectorDefinitions.globes;
            } else if (hostName.indexOf('sourceforge.net') !== -1) {
                selectors = selectorDefinitions.sourceforge;
            } else if (hostName.indexOf('subscenter.cinemast.com') !== -1) {
                selectors = selectorDefinitions.subscenter;
            } else if (hostName.indexOf('wikipedia.org') !== -1) {
                selectors = selectorDefinitions.wikipedia;
            } else { //ynet/calcalist
                selectors = selectorDefinitions.ynet;
            }

            return selectors;
        }
    });
}(window.jQuery.noConflict(true), window.adrRunner));
<commit_msg>new subscnter url<commit_after>// ==UserScript==
// @name         Ads DOM Remover
// @namespace    sagiegurari
// @version      0.83
// @author       Sagie Gur-Ari
// @description  Removes Ad Containers from DOM (doesn't replace adblocker extension, but blocks dynamic content which the adblocker fails to block by removing whole sections from the HTML DOM.)
// @homepage     https://github.com/sagiegurari/userscripts-ads-dom-remover
// @supportURL   https://github.com/sagiegurari/userscripts-ads-dom-remover/issues
// @match        http://www.ynet.co.il/home/*
// @match        http://www.ynet.co.il/articles/*
// @match        http://www.mynet.co.il/articles/*
// @match        http://www.calcalist.co.il/*
// @match        http://www.globes.co.il/*
// @match        https://sourceforge.net/projects/*/download*
// @match        http://subscenter.cinemast.com/*
// @match        http://www.subscenter.co/*
// @match        https://*.wikipedia.org/*
// @require      https://code.jquery.com/jquery-2.2.2.min.js
// @require      https://greasyfork.org/scripts/18490-ads-dom-remover-runner/code/Ads%20DOM%20Remover%20Runner.js?version=123541
// @grant        none
// @license      MIT License
// ==/UserScript==

(function run($, runner) {
    'use strict';

    var selectorDefinitions = {
        ynet: [
            '#colorbox',
            '#cboxOverlay',
            '#ads.premium',
            '#articleLayoutrightsidtable',
            'img[src*="dynamicyield"]',
            'div.MSCmainContent',
            '[id*="arketingCarouse"]',
            '[id*="arketingRecommended"]',
            '.mainVerticalArticleSharingLinks',
            '.OUTBRAIN',
            '.topBannerWrap',
            '.block.B3 .B3.ghcite.dyother.dyMonitor div',
            '.bigdealhomepage',
            '#ww6s_Main',
            '.buyandsavedy',
            '.area.footer.ghcite',
            '.hdr_set_homepage',
            '#c1_Hor',
            '#c2_Hor',
            '#c3_Hor',
            '#c4_Hor',
            '#c5_Hor',
            '#c6_Hor',
            '.homepagevideo-x6',
            '.buyandsave',
            '.general-image',
            '.PhotoArticlesTalkbacks',
            '[name="ExternalWebpageIframe"]',
            '#PROCOIL_SearchForm',
            '#magazines1024',
            '[id^="promo_"]',
            '.main_search_radio',
            'tr td [id^="ads."]',
            {
                selector: '.homepagelitevideo',
                fineTuneSelector: function ($element) {
                    return $element.parent().parent();
                }
            },
            {
                selector: 'iframe',
                filter: function ($element) {
                    return !$element.parent().hasClass('news_ticker_iframe');
                }
            },
            {
                selector: 'div.B2b.block div',
                pre: function ($element) {
                    $element.parent().css({
                        height: '1px'
                    });
                }
            }
        ],
        globes: [
            '#chromeWindow',
            {
                selector: 'iframe',
                filter: function ($element) {
                    var id = $element.attr('id');
                    var src = $element.attr('src') || '';

                    return (id !== 'GlobalFinanceData_home') && (src.indexOf('/news/') !== -1);
                }
            }
        ],
        sourceforge: [
            '#content-for-adblock'
        ],
        subscenter: [
            '.weekBottom',
            '.reviewsWindow',
            '#banner_cinemast',
            '.bottomMenu',
            'footer',
            '#paypal',
            '#aboveSite'
        ],
        wikipedia: [
            '#frbanner',
            '.frbanner'
        ]
    };

    [
        '#dcPremiumRightImg',
        '.boulevard',
        '#multiarticles-9',
        '#multiarticles-12',
        '#multiarticles-13',
        '#multiarticles-14',
        '#multiarticles-15',
        '#multiarticles-16'
    ].forEach(function addSelector(selector) {
        selectorDefinitions.ynet.push({
            selector: selector,
            fineTuneSelector: function ($element) {
                return $element.parent();
            }
        });
    });

    Object.keys(selectorDefinitions).forEach(function (id) {
        selectorDefinitions[id] = {
            id: id,
            selectors: selectorDefinitions[id]
        };
    });

    runner($, {
        getSelectors: function (hostName) {
            var selectors;
            if (hostName.indexOf('globes') !== -1) {
                selectors = selectorDefinitions.globes;
            } else if (hostName.indexOf('sourceforge.net') !== -1) {
                selectors = selectorDefinitions.sourceforge;
            } else if (hostName.indexOf('subscenter') !== -1) {
                selectors = selectorDefinitions.subscenter;
            } else if (hostName.indexOf('wikipedia.org') !== -1) {
                selectors = selectorDefinitions.wikipedia;
            } else { //ynet/calcalist
                selectors = selectorDefinitions.ynet;
            }

            return selectors;
        }
    });
}(window.jQuery.noConflict(true), window.adrRunner));
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import Element from 'common/components/Element';
import H3 from 'common/components/H3';
import A from 'common/components/A';

Notice.propTypes = {
  ids: PropTypes.arrayOf(PropTypes.number),
  isLocked: PropTypes.bool,
};

function Notice({ ids, isLocked }) {
  let notice = null;
  if (isLocked === true) {
    notice = (
      <Element>

          Level is locked! Unlock it in Vocabulary Levels

      </Element>
    );
  } else if (isLocked === false && !ids.length) {
    notice = (
      <Element>

          All entries hidden. Check your WaniKani SRS filtering in Settings

      </Element>
    );
  }
  return notice;
}

export default Notice;
<commit_msg>feat: update hidden level message to include vocabulary check info<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import Element from 'common/components/Element';
import H3 from 'common/components/H3';
import A from 'common/components/A';

Notice.propTypes = {
  ids: PropTypes.arrayOf(PropTypes.number),
  isLocked: PropTypes.bool,
};

function Notice({ ids, isLocked }) {
  let notice = null;
  if (isLocked === true) {
    notice = (
      <Element>

          Level is locked! Unlock it in Vocabulary Levels

      </Element>
    );
  } else if (isLocked === false && !ids.length) {
    notice = (
      <Element>

          All entries hidden. Ensure you have unlocked vocabulary on WaniKani for
          this level and check your WaniKani SRS filtering in Settings

      </Element>
    );
  }
  return notice;
}

export default Notice;
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';

import App from './containers/App';

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component />
    </AppContainer>,
    document.getElementById('root'),
  );
};

render(App);

if (module.hot) {
  module.hot.accept('./containers/App', () => {
    const NextApp = require('./containers/App').default; // eslint-disable-line global-require
    render(NextApp);
  });
}
<commit_msg>tree-shaking<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';

import App from './containers/App';

const render = (Component) => {
  ReactDOM.render(
    <AppContainer>
      <Component />
    </AppContainer>,
    document.getElementById('root'),
  );
};

render(App);

if (module.hot) {
  module.hot.accept('./containers/App', () => { render(App); });
}
<|endoftext|>


<commit_before>var WebdriverIOPrototype = require('./webdriverio'),
    EventHandler = require('./utils/EventHandler'),
    path = require('path'),
    chainIt = require('chainit'),
    cp = require('child_process'),
    nonSeleniumCommands = ['on','once','removeListener','removeAllListeners','emit'],
    commandState = [],
    runState = 0,
    commandLoop = [];

/**
 * Constructor
 */
function Multibrowser(options) {
    var self = this,
        instances = {},
        flag = 0.5;

    /**
     * create processes per instance
     */
    Object.keys(options).forEach(function(instanceName) {
        var instance = cp.fork(__dirname + path.sep + 'runner.js');
        instance.on('message', onMessage.bind(self, instance));
        instance.on('disconnect', onDisconnect.bind(self, instance));

        /**
         * assign bitmask
         */
        flag *= 2;
        instance.flag = flag;
        instance.name = instanceName;

        /**
         * initialise process instance
         */
        instance.send({
            action: 'initiate',
            capabilities: options[instanceName]
        });

        /**
         * set run state to running
         */
        runState |= instance.flag;

        instances[instanceName] = instance;
    });

    this.eventHandler = new EventHandler(this);
    this.instances = instances;

    /**
     * addCommand added here because it's synchronous and thus does not need
     * to be added to asynchronous chain.
     */
    this.addCommand = function(name, fn) {
        chainIt.add(this, name, fn);

        /**
         * promisify new commands
         */
        this[name] = PromiseHandler(name, this.register(name, this[name]));

        /**
         * we still returns the instance so that we can continue chaining
         */
        return this;
    };
};

Multibrowser.register = function(fnName, chainedCommand) {
    /**
     * skip non selenium commands so they still get handled like
     * normal chained commands
     */
    if(nonSeleniumCommands.indexOf(fnName) === 0) {
        return chainedCommand;
    }

    /**
     * custom multibrowser call command
     * gets called onced given instances reach to that point (if no instances are given it will wait on all
     * created instances)
     *
     * @param  {Object[]}   instances  instances to wait
     * @param  {Function}   callback   function to be called
     * @type utility
     */
    if(fnName === 'call') {
        return function(instances, callback) {
            var self = this,
                commandId = commandState.length - 1;

            if(typeof instances === 'function') {
                callback = instances;
                instances = Object.keys(this.instances);
            }

            /**
             * stop instances
             */
            instances.forEach(function(instance) {
                runState &= ~self.instances[instance].flag;
            });

            this.eventHandler.on('command_' + commandId, function(finishedInstance, message) {
                var instancesAreSynced = true;

                instances.forEach(function(instance) {
                    instancesAreSynced = instancesAreSynced && (commandState[commandId] & self.instances[instance].flag);
                });

                if(instancesAreSynced || (instances.length !== Object.keys(self.instances).length)) {
                    return chainedCommand.call(this, callback);
                }
            });

            return this;
        }
    }

    return function() {
        var self = this,
            args = Array.prototype.slice.call(arguments);

        commandState.push(0);
        chainedCommand.apply(this, [commandState.length - 1, args.pop()]);

        var processMessage = {
            action: 'command',
            fnName: fnName,
            args: args,
            commandId: commandState.length - 1
        };

        missingInstances = 0;
        Object.keys(this.instances).forEach(function(instanceName) {
            var instance = self.instances[instanceName];

            /**
             * only send message if instance is running
             */
            if(runState & instance.flag) {
                return instance.send(processMessage);
            }

            missingInstances |= instance.flag;
        });

        /**
         * add commands to command loop if some instances aren't running
         */
        if(missingInstances) {
            commandLoop.push({
                bitmask: missingInstances,
                message: processMessage
            });
        }

        return this;
    };
};

/**
 * assign custom multibrowser methods to each WebdriverIO command to make sure they'll get chained
 */
Object.keys(WebdriverIOPrototype.prototype).forEach(function(fnName) {
    /**
     * skip non selenium commands so they still get handled like
     * normal chained commands
     */
    if(!Multibrowser.prototype[fnName] && nonSeleniumCommands.indexOf(fnName) === 0) {
        return Multibrowser.prototype[fnName] = WebdriverIOPrototype.prototype[fnName];
    }

    /**
     * handle Selenium methods as real chained methods but instead of doing
     * HTTP requests we listen on the result from all instances
     */
    Multibrowser.prototype[fnName] = function(commandId, cb){
        this.eventHandler.on('command_' + commandId, function(instance, message) {
            /**
             * make sure that we call the callback method of
             * the correct commandId
             */
            if(commandId !== message.commandId) {
                return;
            }

            cb.apply(this, message.args);
        });
    };
});

/**
 * process message listener
 * gets called once an instance finishes a command
 */
var onMessage = function(instance, message) {
    commandState[message.commandId] |= instance.flag;
    this.eventHandler.emit('command_' + message.commandId, instance, message);
};

/**
 * process disconnect listener
 * gets called once an instance called the `end` method
 */
var onDisconnect = function(instance, e) {
    instance.kill();
    delete this.instances[instance.name];
};

/**
 * TODO
 * implement ticker that checks command loop
 */

Multibrowser.prototype.call = WebdriverIOPrototype.prototype.__call;
module.exports = Multibrowser;<commit_msg>finished sync (call) method<commit_after>var WebdriverIOPrototype = require('./webdriverio'),
    EventHandler = require('./utils/EventHandler'),
    path = require('path'),
    chainIt = require('chainit'),
    cp = require('child_process'),
    nonSeleniumCommands = ['on','once','removeListener','removeAllListeners','emit'];

/**
 * Constructor
 */
function Multibrowser(options) {
    var self = this,
        instances = {},
        flag = 0.5;

    this.commandState = [];
    this.commandLoop = [];
    this.runState = 0;

    /**
     * create processes per instance
     */
    Object.keys(options).forEach(function(instanceName) {
        var instance = cp.fork(__dirname + path.sep + 'runner.js');
        instance.on('message', onMessage.bind(self, instance));
        instance.on('disconnect', onDisconnect.bind(self, instance));

        /**
         * assign bitmask
         */
        flag *= 2;
        instance.flag = flag;
        instance.name = instanceName;

        /**
         * initialise process instance
         */
        instance.send({
            action: 'initiate',
            capabilities: options[instanceName]
        });

        /**
         * set run state to running
         */
        self.runState |= instance.flag;

        instances[instanceName] = instance;
    });

    this.eventHandler = new EventHandler(this);
    this.instances = instances;

    /**
     * addCommand added here because it's synchronous and thus does not need
     * to be added to asynchronous chain.
     */
    this.addCommand = function(name, fn) {
        chainIt.add(this, name, fn);

        /**
         * promisify new commands
         */
        this[name] = PromiseHandler(name, this.register(name, this[name]));

        /**
         * we still returns the instance so that we can continue chaining
         */
        return this;
    };

    this.startInstances = function(instances) {
        var self = this;

        if(!Array.isArray(instances)) {
            return this.runState;
        }

        instances.forEach(function(instanceName) {
            self.runState |= self.instances[instanceName].flag;
        });

        this.flushCommandLoop();

        return this.runState;
    };

    this.stopInstances = function(instances) {
        var self = this;

        if(!Array.isArray(instances)) {
            return this.runState;
        }

        instances.forEach(function(instanceName) {
            self.runState &= ~self.instances[instanceName].flag;
        });

        return this.runState;
    };

    this.flushCommandLoop = function() {
        var self = this;

        this.commandLoop.forEach(function(command) {
            for(instanceName in self.instances) {
                var instance = self.instances[instanceName];

                if(command.bitmask & instance.flag) {
                    instance.send(command.message);
                }
            }
        });
    };
};

Multibrowser.register = function(fnName, chainedCommand) {
    /**
     * skip non selenium commands so they still get handled like
     * normal chained commands
     */
    if(nonSeleniumCommands.indexOf(fnName) === 0) {
        return chainedCommand;
    }

    /**
     * custom multibrowser call command
     * gets called onced given instances reach to that point (if no instances are given it will wait on all
     * created instances)
     *
     * @param  {Object[]}   instances  instances to wait
     * @param  {Function}   callback   function to be called
     * @type utility
     */
    if(fnName === 'call') {
        return function(instances, callback) {
            var self = this,
                commandId = this.commandState.length - 1;

            if(typeof instances === 'function') {
                callback = instances;
                instances = Object.keys(this.instances);
            }

            /**
             * stop instances
             */
            this.stopInstances(instances);

            this.eventHandler.on('command_' + commandId, function(finishedInstance, message) {
                var instancesAreSynced = true;

                instances.forEach(function(instanceName) {
                    instancesAreSynced = instancesAreSynced && (self.commandState[commandId] & self.instances[instanceName].flag);
                });

                /**
                 * check if instances are synced
                 */
                if(instancesAreSynced || (instances.length !== Object.keys(self.instances).length)) {
                    /**
                     * start instances again
                     */
                    self.startInstances(instances);

                    /**
                     * execute callback
                     */
                    return chainedCommand.call(this, callback);
                }
            });

            return this;
        }
    }

    return function() {
        var self = this,
            args = Array.prototype.slice.call(arguments);

        this.commandState.push(0);
        chainedCommand.apply(this, [this.commandState.length - 1, args.pop()]);

        var processMessage = {
            action: 'command',
            fnName: fnName,
            args: args,
            commandId: this.commandState.length - 1
        };

        missingInstances = 0;
        for(var instanceName in this.instances) {
            var instance = this.instances[instanceName];

            /**
             * only send message if instance is running
             */
            if(this.runState & instance.flag) {
                instance.send(processMessage);
                continue;
            }

            missingInstances |= instance.flag;
        }

        /**
         * add commands to command loop if some instances aren't running
         */
        if(missingInstances) {
            this.commandLoop.push({
                bitmask: missingInstances,
                message: processMessage
            });
        }

        return this;
    };
};

/**
 * assign custom multibrowser methods to each WebdriverIO command to make sure they'll get chained
 */
Object.keys(WebdriverIOPrototype.prototype).forEach(function(fnName) {
    /**
     * skip non selenium commands so they still get handled like
     * normal chained commands
     */
    if(!Multibrowser.prototype[fnName] && nonSeleniumCommands.indexOf(fnName) === 0) {
        return Multibrowser.prototype[fnName] = WebdriverIOPrototype.prototype[fnName];
    }

    /**
     * handle Selenium methods as real chained methods but instead of doing
     * HTTP requests we listen on the result from all instances
     */
    Multibrowser.prototype[fnName] = function(commandId, cb){
        this.eventHandler.on('command_' + commandId, function(instance, message) {
            /**
             * make sure that we call the callback method of
             * the correct commandId
             */
            if(commandId !== message.commandId) {
                return;
            }

            cb.apply(this, message.args);
        });
    };
});

/**
 * process message listener
 * gets called once an instance finishes a command
 */
var onMessage = function(instance, message) {
    this.commandState[message.commandId] |= instance.flag;
    this.eventHandler.emit('command_' + message.commandId, instance, message);
};

/**
 * process disconnect listener
 * gets called once an instance called the `end` method
 */
var onDisconnect = function(instance, e) {
    instance.kill();
    delete this.instances[instance.name];
};

/**
 * TODO
 * implement ticker that checks command loop
 */

Multibrowser.prototype.call = WebdriverIOPrototype.prototype.__call;
module.exports = Multibrowser;<|endoftext|>


<commit_before>var q = require('q');

/**
 * Constructor
 */
function Multibrowser(instances) {
    this.instances = {};
    this.promiseBucket = [];
}

Multibrowser.prototype.addInstance = function(browserName, client) {
    if(this.instances[browserName]) {
        throw new Error('webdriver instance "' + browserName + '" is already defined');
    }
    this.instances[browserName] = client;
}

/**
 * modifier for multibrowser instance
 */
Multibrowser.prototype.getModifier = function() {
    var browserNames = Object.keys(this.instances),
        multibrowser = this;

    return function(client) {
        client.next = function() {
            var self = this,
                promises = [],
                args = Array.prototype.slice.apply(arguments),
                fnName = args.pop(),
                instance;

            /**
             * no need for actual function here
             */
            args.shift();

            /**
             * flush promise bucket
             */
            multibrowser.promiseBucket = [];

            return this.lastPromise.done(function() {
                browserNames.forEach(function(browserName) {
                    instance = multibrowser.instances[browserName];
                    instance = instance[fnName].apply(instance, args[0]);
                    promises.push(instance.promise);
                });

                return q.all(promises).then(function(result) {

                    /**
                     * custom event handling since multibrowser instance
                     * actually never executes any command
                     */
                    var payload = {
                        fnName: fnName
                    };

                    for(var i = 0; i < browserNames.length; ++i) {
                        payload[browserNames[i]] = result[i];
                    }

                    if(fnName.match(/(init|end)/)) {
                        self.emit(fnName, payload);
                    }

                    self.emit('result', payload);
                    self.defer.resolve(result);
                }, function(err) {
                    self.emit('error', err);
                    self.defer.reject(err);
                });
            });
        };

        var _then = client.then;
        client.then = function(onFulfilled, onRejected) {

            var curryArguments = function() {
                return onFulfilled.apply(this, arguments[0]);
            };

            if(!onFulfilled && !onRejected) {
                return this;
            }

            if(onFulfilled && !onRejected) {
                return _then.call(this, curryArguments);
            }

            if(onFulfilled && onRejected) {
                return _then.call(this, curryArguments, onRejected);
            }

            return _then.call(this, undefined, onRejected);
        };

        client.select = function(browserName) {
            var instance = multibrowser.instances[browserName];

            if(!instance) {
                throw new Error('browser name "' + browserName + '" was not defined');
            }

            instance.isMultibrowser = false;
            return instance;
        };

        client.sync = function() {
            var bucket = multibrowser.flushPromiseBucket();

            return this.call(function() {
                return q.all(bucket);
            });

        };

        var _addCommand = client.addCommand;
        client.addCommand = function(fnName, fn) {
            _addCommand.call(this, fnName, fn);
            browserNames.forEach(function(browserName) {
                multibrowser.instances[browserName].addCommand(fnName, fn);
            });
            return this;
        }

        return client;
    };
};

/**
 * flush bucket and return current pending promises
 */
Multibrowser.prototype.flushPromiseBucket = function() {
    var bucket = this.promiseBucket.filter(function(promise) {
        return promise.inspect().state === 'pending';
    });
    this.promiseBucket = [];
    return bucket;
};

/**
 * modifier for single webdriverio instances
 */
Multibrowser.prototype.getInstanceModifier = function() {
    var multibrowser = this;

    return function(client) {
        var _next = client.next;

        /**
         * Overwrite next (bind) method to put each command into a bucket.
         * This provides us useful information about all current running
         * commands.
         */
        client.next = function() {
            multibrowser.promiseBucket.push(this.promise);
            return _next.apply(this, arguments);
        }

        return client;
    }
};

module.exports = Multibrowser;<commit_msg>better handling of then calls in multibrowser monad<commit_after>var q = require('q');

/**
 * Constructor
 */
function Multibrowser(instances) {
    this.instances = {};
    this.promiseBucket = [];

    var defer = q.defer();
    this.qResolve = defer.resolve.toString();
    delete defer;
}

Multibrowser.prototype.addInstance = function(browserName, client) {
    if(this.instances[browserName]) {
        throw new Error('webdriver instance "' + browserName + '" is already defined');
    }
    this.instances[browserName] = client;
}

/**
 * modifier for multibrowser instance
 */
Multibrowser.prototype.getModifier = function() {
    var browserNames = Object.keys(this.instances),
        multibrowser = this;

    return function(client) {
        client.next = function() {
            var self = this,
                promises = [],
                args = Array.prototype.slice.apply(arguments),
                fnName = args.pop(),
                instance;

            /**
             * no need for actual function here
             */
            args.shift();

            /**
             * flush promise bucket
             */
            multibrowser.promiseBucket = [];

            return this.lastPromise.done(function() {
                browserNames.forEach(function(browserName) {
                    instance = multibrowser.instances[browserName];
                    instance = instance[fnName].apply(instance, args[0]);
                    promises.push(instance.promise);
                });

                return q.all(promises).then(function(result) {

                    /**
                     * custom event handling since multibrowser instance
                     * actually never executes any command
                     */
                    var payload = {
                        fnName: fnName
                    };

                    for(var i = 0; i < browserNames.length; ++i) {
                        payload[browserNames[i]] = result[i];
                    }

                    if(fnName.match(/(init|end)/)) {
                        self.emit(fnName, payload);
                    }

                    self.emit('result', payload);
                    self.defer.resolve(result);
                }, function(err) {
                    self.emit('error', err);
                    self.defer.reject(err);
                });
            });
        };

        var _then = client.then;
        client.then = function(onFulfilled, onRejected) {

            /**
             * curry arguments
             * as multibrowser commands return with an array of results for each instance
             * respectively (see q.all) we need to curry them (expand arguments) to help
             * users to better work with the results
             *
             * uncurried original version:
             * ```js
             * matrix.getTitle().then(function(result) {
             *     expect(result[0]).to.be.equal('title of browser A');
             *     expect(result[1]).to.be.equal('title of browser B');
             * })
             * ```
             *
             * curried version:
             * ```js
             * matrix.getTitle().then(function(titleBrowserA, titleBrowserB) {
             *     expect(titleBrowserA).to.be.equal('title of browser A');
             *     expect(titleBrowserB).to.be.equal('title of browser B');
             * })
             * ```
             */
            var curryArguments = function(args) {

                /**
                 * when returning with a promise within a multibrowser promise like
                 *
                 * ```js
                 * matrix.url(...).getTitle().then(function() {
                 *     return matrix.getSource();
                 * })
                 * ```
                 *
                 * we will have problems as we are running through curryArguments twice.
                 * Therefor check if the onFulFilled handler is from the Q library and
                 * handle that promise as usual here.
                 * It's an ugly hack but the only way to get around with this and having
                 * nice curried arguments.
                 *
                 */
                if(onFulfilled.toString() === multibrowser.qResolve) {
                    return onFulfilled.apply(this, arguments);
                }
                if(arguments.length === 1 && !Array.isArray(args)) {
                    return onFulfilled.call(this, args);
                }

                if(arguments.length > 1) {
                    args = Array.prototype.slice.call(arguments);
                }

                return onFulfilled.apply(this, args);
            };

            if(!onFulfilled && !onRejected) {
                return this;
            }

            if(onFulfilled && !onRejected) {
                return _then.call(this, curryArguments);
            }

            if(onFulfilled && onRejected) {
                return _then.call(this, curryArguments, onRejected);
            }

            return _then.call(this, undefined, onRejected);
        };

        client.select = function(browserName) {
            var instance = multibrowser.instances[browserName];

            if(!instance) {
                throw new Error('browser name "' + browserName + '" was not defined');
            }

            instance.isMultibrowser = false;
            return instance;
        };

        client.sync = function() {
            var bucket = multibrowser.flushPromiseBucket();

            return this.call(function() {
                return q.all(bucket);
            });

        };

        var _addCommand = client.addCommand;
        client.addCommand = function(fnName, fn) {
            _addCommand.call(this, fnName, fn);
            browserNames.forEach(function(browserName) {
                multibrowser.instances[browserName].addCommand(fnName, fn);
            });
            return this;
        }

        return client;
    };
};

/**
 * flush bucket and return current pending promises
 */
Multibrowser.prototype.flushPromiseBucket = function() {
    var bucket = this.promiseBucket.filter(function(promise) {
        return promise.inspect().state === 'pending';
    });
    this.promiseBucket = [];
    return bucket;
};

/**
 * modifier for single webdriverio instances
 */
Multibrowser.prototype.getInstanceModifier = function() {
    var multibrowser = this;

    return function(client) {
        var _next = client.next;

        /**
         * Overwrite next (bind) method to put each command into a bucket.
         * This provides us useful information about all current running
         * commands.
         */
        client.next = function() {
            multibrowser.promiseBucket.push(this.promise);
            return _next.apply(this, arguments);
        }

        return client;
    }
};

module.exports = Multibrowser;<|endoftext|>