<commit_before>require("babel-polyfill");

import config from "config";
import promisify from "promisify-node";
import log from "./log";
import dbCache from "./dbCache";
import image from "./image";
import aws from "./aws";
import {futureDate} from "./helper";

const fs = promisify('fs');

const imagePath = (name) => `${config.get('originals_dir')}/${name}`;

const redirectTimeout = config.has('redirect_cache_timeout') ? config.get('redirect_cache_timeout') : 0;

// Determine whether the image exists on disk
// Returns either true of false
const doesImageExist = async(name) => {
  try {
    await fs.access(imagePath(name), fs.R_OK);
    return true;
  } catch (e) {
    return false;
  }
};

const isRequestedImageWithinBounds = (params) => {
  // Check if we are allowed to serve an image of this size and optionally redirect
  return (params.width <= config.get('constraints.max_width') && params.height <= config.get('constraints.max_height'));
};

const calculateNewBounds = (params) => {
  // Resize the parameters
  const providedRatio = params.width / params.height;
  if (params.width > config.get('constraints.max_width')) {
    params.width = config.get('constraints.max_width');
    if (params.fit === 'crop') {
      params.height = params.width / providedRatio;
    }
  }
  if (params.height > config.get('constraints.max_height')) {
    params.height = config.get('constraints.max_height');
    if (params.fit === 'crop') {
      params.width = params.height * providedRatio;
    }
  }
  return params;
};

const isHeadRequest = method => (method === 'HEAD');

const redirectImageToWithinBounds = (params, response) => {
  return response.status(307).set({
    'X-Redirect-Info': 'The requested image size falls outside of the allowed boundaries of this service. We are directing you to the closest available match.', //eslint-disable-line max-len
    'Location': `/${params.name}_${params.width}_${params.height}.${params.type}?fit=${params.fit}&blur=${Boolean(params.blur)}`
  }).end();
};

const redirectToCachedEntity = (cacheUrl, params, response) => {
  // We redirect the user to the new location. We issue a temporary redirect such that the
  // request url stays the representitive url, and because temporary redirects generally
  // give less issues than permanent redirects for the odd chance that the underlying resource
  // does actually change
  const headers = {
    'Location': cacheUrl
  };
  if (redirectTimeout) {
    headers['Cache-Control'] = `max-age=${redirectTimeout}`;
  }

  response.status(307).set(headers).end();
  response.end();
};

const sendFoundHeaders = (params, response) => {
  // Get the image and resize it. We allow any intermediate servers and browsers to use this cached resource as well
  response.status(200).type(params.mime).set({
    'Expires': futureDate(),
    'Cache-Control': 'public'
  });
};

const logErrIfNeeded = (stream, streamName) => {
  let data = '';
  stream.on('data', (chunk) => {
    log('debug', `Got a chunk of data: ${chunk}`);
    data = `${data}${chunk}`;
  });
  stream.on('end', () => {
    if (data !== '') {
      log('error', `Got error data from stream '${streamName}': ${data}`);
    } else {
      log('debug', 'Finished error stream. Nothing was in the stream.');
    }
  });
};

const imageKey = params => `${params.name}_${params.width}x${params.height}.${params.fit}.b-${Boolean(params.blur)}.${params.type}`;

export default {
  magic: async function (db, params, method, response, request) {
    const cache = dbCache(db);
    if (params === null) {
      // Invalid, hence reject
      response.status(400).end();
      return;
    }

    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      return;
    }

    // Image exists
    if (!isRequestedImageWithinBounds(params)) {
      redirectImageToWithinBounds(calculateNewBounds(params), response);
      return;
    }

    // Image exists and is within bounds.
    // This method is mainly used by browsers to serve retina images
    if (isHeadRequest(method)) {
      response.status(200).end();
      log('info', `HEAD request for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}) can be served`);  //eslint-disable-line max-len
      return;
    }
    log('debug', `Request for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len

    const cacheValue = await cache.getFromCache(params);
    if (cacheValue) {
      log('info', `Cache hit for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
      redirectToCachedEntity(cacheValue, params, response);
      return;
    }

    // Image is present but not in the correct setting
    log('info', `Cache miss for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
    sendFoundHeaders(params, response);

    const clientStartTime = new Date();
    const browserImage = await image.magic(imagePath(params.name), params, response);
    browserImage.stream(params.type, (err, stdout, stderr) => {
      logErrIfNeeded(stderr, 'Live image creation stderr');
      if (err) {
        response.status(500).end();
        log('error', `Error occurred while creating live image: ${err}`);
        return;
      }
      const errors = [];
      const r = stdout.pipe(response);
      r.on('finish', () => {
        if (errors.length === 0) {
          log('info', `Creating live image took ${new Date() - clientStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
        } else {
          log('warn', `Got an error while creating live image. Took ${new Date() - clientStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}). Errors: ${JSON.stringify(errors)}`);  //eslint-disable-line max-len
        }
        response.end();
      });
      r.on('error', (error) => {
        log('error', `The live image stream hit an error: ${error}`);
        stdout.unpipe(response);
        errors.push(error);
        response.end();
        r.end();
      });
      if (request) {
        request.once('close', () => {
          log('warn', 'Client disconnected prematurely. Terminating stream');
          stdout.unpipe(response);
          errors.push('Client disconnected prematurely.');
          response.end();
          r.end();
        });
      }

    });

    const awsStartTime = new Date();
    const awsImage = await image.magic(imagePath(params.name), params, response);
    awsImage.toBuffer(params.type, (err, stream) => {
      if (!err) {
        log('info', `Creating AWS image took ${new Date() - awsStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
        aws(cache)(imageKey(params), params, stream);
      }
    });
  },
  original: async function (db, params, method, response) {
    const startTime = new Date();
    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      return;
    }
    response.status(200).set({
      'Content-Type': params.mime,
      'Cache-Control': 'public',
      Etag: `${params.name}_${params.type}`,
      Expires: futureDate()
    });
    const data = await fs.readFile(imagePath(params.name));
    response.end(data);
    log('info', `Serving original image ${params.name} took ${new Date() - startTime}ms`);  //eslint-disable-line max-len
  },
  upload: async function (name, path, cropParameters) {
    const destinationPath = `${config.get('originals_dir')}/${name}`;
    return await image.writeOriented(path, destinationPath, cropParameters);
  }
};
<commit_msg>Switch .on to .once<commit_after>require("babel-polyfill");

import config from "config";
import promisify from "promisify-node";
import log from "./log";
import dbCache from "./dbCache";
import image from "./image";
import aws from "./aws";
import {futureDate} from "./helper";

const fs = promisify('fs');

const imagePath = (name) => `${config.get('originals_dir')}/${name}`;

const redirectTimeout = config.has('redirect_cache_timeout') ? config.get('redirect_cache_timeout') : 0;

// Determine whether the image exists on disk
// Returns either true of false
const doesImageExist = async(name) => {
  try {
    await fs.access(imagePath(name), fs.R_OK);
    return true;
  } catch (e) {
    return false;
  }
};

const isRequestedImageWithinBounds = (params) => {
  // Check if we are allowed to serve an image of this size and optionally redirect
  return (params.width <= config.get('constraints.max_width') && params.height <= config.get('constraints.max_height'));
};

const calculateNewBounds = (params) => {
  // Resize the parameters
  const providedRatio = params.width / params.height;
  if (params.width > config.get('constraints.max_width')) {
    params.width = config.get('constraints.max_width');
    if (params.fit === 'crop') {
      params.height = params.width / providedRatio;
    }
  }
  if (params.height > config.get('constraints.max_height')) {
    params.height = config.get('constraints.max_height');
    if (params.fit === 'crop') {
      params.width = params.height * providedRatio;
    }
  }
  return params;
};

const isHeadRequest = method => (method === 'HEAD');

const redirectImageToWithinBounds = (params, response) => {
  return response.status(307).set({
    'X-Redirect-Info': 'The requested image size falls outside of the allowed boundaries of this service. We are directing you to the closest available match.', //eslint-disable-line max-len
    'Location': `/${params.name}_${params.width}_${params.height}.${params.type}?fit=${params.fit}&blur=${Boolean(params.blur)}`
  }).end();
};

const redirectToCachedEntity = (cacheUrl, params, response) => {
  // We redirect the user to the new location. We issue a temporary redirect such that the
  // request url stays the representitive url, and because temporary redirects generally
  // give less issues than permanent redirects for the odd chance that the underlying resource
  // does actually change
  const headers = {
    'Location': cacheUrl
  };
  if (redirectTimeout) {
    headers['Cache-Control'] = `max-age=${redirectTimeout}`;
  }

  response.status(307).set(headers).end();
  response.end();
};

const sendFoundHeaders = (params, response) => {
  // Get the image and resize it. We allow any intermediate servers and browsers to use this cached resource as well
  response.status(200).type(params.mime).set({
    'Expires': futureDate(),
    'Cache-Control': 'public'
  });
};

const logErrIfNeeded = (stream, streamName) => {
  let data = '';
  stream.on('data', (chunk) => {
    log('debug', `Got a chunk of data: ${chunk}`);
    data = `${data}${chunk}`;
  });
  stream.on('end', () => {
    if (data !== '') {
      log('error', `Got error data from stream '${streamName}': ${data}`);
    } else {
      log('debug', 'Finished error stream. Nothing was in the stream.');
    }
  });
};

const imageKey = params => `${params.name}_${params.width}x${params.height}.${params.fit}.b-${Boolean(params.blur)}.${params.type}`;

export default {
  magic: async function (db, params, method, response, request) {
    const cache = dbCache(db);
    if (params === null) {
      // Invalid, hence reject
      response.status(400).end();
      return;
    }

    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      return;
    }

    // Image exists
    if (!isRequestedImageWithinBounds(params)) {
      redirectImageToWithinBounds(calculateNewBounds(params), response);
      return;
    }

    // Image exists and is within bounds.
    // This method is mainly used by browsers to serve retina images
    if (isHeadRequest(method)) {
      response.status(200).end();
      log('info', `HEAD request for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}) can be served`);  //eslint-disable-line max-len
      return;
    }
    log('debug', `Request for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len

    const cacheValue = await cache.getFromCache(params);
    if (cacheValue) {
      log('info', `Cache hit for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
      redirectToCachedEntity(cacheValue, params, response);
      return;
    }

    // Image is present but not in the correct setting
    log('info', `Cache miss for ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
    sendFoundHeaders(params, response);

    const clientStartTime = new Date();
    const browserImage = await image.magic(imagePath(params.name), params, response);
    browserImage.stream(params.type, (err, stdout, stderr) => {
      logErrIfNeeded(stderr, 'Live image creation stderr');
      if (err) {
        response.status(500).end();
        log('error', `Error occurred while creating live image: ${err}`);
        return;
      }
      const errors = [];
      const r = stdout.pipe(response);
      r.once('finish', () => {
        if (errors.length === 0) {
          log('info', `Creating live image took ${new Date() - clientStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
        } else {
          log('warn', `Got an error while creating live image. Took ${new Date() - clientStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)}). Errors: ${JSON.stringify(errors)}`);  //eslint-disable-line max-len
        }
        response.end();
      });
      r.once('error', (error) => {
        log('error', `The live image stream hit an error: ${error}`);
        stdout.unpipe(response);
        errors.push(error);
        response.end();
        r.end();
      });
      if (request) {
        request.once('close', () => {
          log('warn', 'Client disconnected prematurely. Terminating stream');
          stdout.unpipe(response);
          errors.push('Client disconnected prematurely.');
          response.end();
          r.end();
        });
      }

    });

    const awsStartTime = new Date();
    const awsImage = await image.magic(imagePath(params.name), params, response);
    awsImage.toBuffer(params.type, (err, stream) => {
      if (!err) {
        log('info', `Creating AWS image took ${new Date() - awsStartTime}ms: ${params.name}.${params.type} (${params.width}x${params.height}px, fit: ${params.fit}, blur: ${Boolean(params.blur)})`);  //eslint-disable-line max-len
        aws(cache)(imageKey(params), params, stream);
      }
    });
  },
  original: async function (db, params, method, response) {
    const startTime = new Date();
    const imageExists = await doesImageExist(params.name);
    if (!imageExists) {
      response.status(404).end();
      return;
    }
    response.status(200).set({
      'Content-Type': params.mime,
      'Cache-Control': 'public',
      Etag: `${params.name}_${params.type}`,
      Expires: futureDate()
    });
    const data = await fs.readFile(imagePath(params.name));
    response.end(data);
    log('info', `Serving original image ${params.name} took ${new Date() - startTime}ms`);  //eslint-disable-line max-len
  },
  upload: async function (name, path, cropParameters) {
    const destinationPath = `${config.get('originals_dir')}/${name}`;
    return await image.writeOriented(path, destinationPath, cropParameters);
  }
};
<|endoftext|>


<commit_before>import mdc from './index-node'
import Chart from 'chart.js'
import $ from 'jquery'
import Cookies from 'js-cookie'

import 'github-css/index.css'
import 'highlight.js/styles/atom-one-light.css'
import 'emojione/assets/css/emojione-awesome.css'
import './index.css'
import 'font-awesome/css/font-awesome.css'
import 'katex/dist/katex.min.css'

// convert an element to png image, aka screenshot an element
mdc.elementToPng = function ($element) {
  return '' // hook method, needs to be implemented in native code, such as Cocoa or WPF
}
// mermaid charts
mdc.mermaid = {
  toPng: function () {
    $($('article#preview div.mermaid > svg').get().reverse()).each(function () { // reverse, so latter won't affect former
      let png = mdc.elementToPng($(this))
      $(this).replaceWith(`<img src="${png}"/>`)
    })
  },
  gantt: {
    axisFormat: function (format) {
      mermaid.ganttConfig = {
        axisFormatter: [
          [format, function (d) {
            return d.getDay() === 1
          }]
        ]
      }
    }
  }
}
// charts
mdc.charts = {
  toPng: function () {
    $($('canvas.chartjs').get().reverse()).each(function () { // reverse, so latter won't affect former
      let png = mdc.elementToPng($(this))
      $(this).replaceWith(`<img src="${png}"/>`)
    })
  }
}
mdc.loadPreferences = function () {
  let ganttAxisFormat = Cookies.get('gantt-axis-format')
  if (ganttAxisFormat === undefined) {
    ganttAxisFormat = '%Y-%m-%d'
  }
  mdc.mermaid.gantt.axisFormat(ganttAxisFormat)
  return { 'gantt-axis-format': ganttAxisFormat }
}
mdc.loadPreferences()
mermaid.parseError = function (err, hash) {
  mdc.mermaidError = err
}
mdc.mermaid_charts = function (code, map) {
  if (mermaid.parse(code)) {
    return `<div${map} class="mermaid">${code}</div>`
  } else {
    return `<pre${map}>${mdc.mermaidError}</pre>`
  }
}

mdc.inited = function () {
  // this is a hook method
}
mdc.init = function (markdown, debug) {
  let result = mdc.render(markdown)
  if (debug === true) {
    console.log(result)
  }

  // 通过 cache 来防止 mermaid init 造成页面抖动
  $('#cache').show()
  $('#cache').html(result)
  mermaid.init(undefined, $('#cache .mermaid'))
  $('#cache').hide()
  $('#preview').html($('#cache').html())
  $('#cache').empty()

  // charts
  $('#preview .chartjs').each(function () {
    try {
      let chart = new Chart($(this), JSON.parse($(this).text()))
      if (process.env.NODE_ENV === 'development') {
        console.log(chart.generateLegend())
      }
    } catch (e) {
      $(this).replaceWith(`<pre>Chart.js complains: "${e}"</pre>`)
    }
  })

  mdc.inited()
}

export default mdc
<commit_msg>Fix eslint errors<commit_after>import mdc from './index-node'
import Chart from 'chart.js'
import $ from 'jquery'
import Cookies from 'js-cookie'

import 'github-css/index.css'
import 'highlight.js/styles/atom-one-light.css'
import 'emojione/assets/css/emojione-awesome.css'
import './index.css'
import 'font-awesome/css/font-awesome.css'
import 'katex/dist/katex.min.css'

// convert an element to png image, aka screenshot an element
mdc.elementToPng = function ($element) {
  return '' // hook method, needs to be implemented in native code, such as Cocoa or WPF
}
// mermaid charts
mdc.mermaid = {
  toPng: function () {
    $($('article#preview div.mermaid > svg').get().reverse()).each(function () { // reverse, so latter won't affect former
      let png = mdc.elementToPng($(this))
      $(this).replaceWith(`<img src="${png}"/>`)
    })
  },
  gantt: {
    axisFormat: function (format) {
      window.mermaid.ganttConfig = {
        axisFormatter: [
          [format, function (d) {
            return d.getDay() === 1
          }]
        ]
      }
    }
  }
}
// charts
mdc.charts = {
  toPng: function () {
    $($('canvas.chartjs').get().reverse()).each(function () { // reverse, so latter won't affect former
      let png = mdc.elementToPng($(this))
      $(this).replaceWith(`<img src="${png}"/>`)
    })
  }
}
mdc.loadPreferences = function () {
  let ganttAxisFormat = Cookies.get('gantt-axis-format')
  if (ganttAxisFormat === undefined) {
    ganttAxisFormat = '%Y-%m-%d'
  }
  mdc.mermaid.gantt.axisFormat(ganttAxisFormat)
  return { 'gantt-axis-format': ganttAxisFormat }
}
mdc.loadPreferences()
window.mermaid.parseError = function (err, hash) {
  mdc.mermaidError = err
}
mdc.mermaid_charts = function (code, map) {
  if (window.mermaid.parse(code)) {
    return `<div${map} class="mermaid">${code}</div>`
  } else {
    return `<pre${map}>${mdc.mermaidError}</pre>`
  }
}

mdc.inited = function () {
  // this is a hook method
}
mdc.init = function (markdown, debug) {
  let result = mdc.render(markdown)
  if (debug === true) {
    console.log(result)
  }

  // 通过 cache 来防止 mermaid init 造成页面抖动
  $('#cache').show()
  $('#cache').html(result)
  window.mermaid.init(undefined, $('#cache .mermaid'))
  $('#cache').hide()
  $('#preview').html($('#cache').html())
  $('#cache').empty()

  // charts
  $('#preview .chartjs').each(function () {
    try {
      let chart = new Chart($(this), JSON.parse($(this).text()))
      if (process.env.NODE_ENV === 'development') {
        console.log(chart.generateLegend())
      }
    } catch (e) {
      $(this).replaceWith(`<pre>Chart.js complains: "${e}"</pre>`)
    }
  })

  mdc.inited()
}

export default mdc
<|endoftext|>


<commit_before>const crypto = require("crypto");

module.exports.encrypt = (input, encryptionKey) => {
  try {
    let cipher = crypto.createCipher("aes-256-ctr", encryptionKey);
    let crypted = cipher.update(input, "utf8", "hex");
    crypted += cipher.final("hex");
    return crypted;
  } catch (e) {
    return "";
  }
};

module.exports.decrypt = (input, encryptionKey) => {
  try {
    let decipher = crypto.createDecipher("aes-256-ctr", encryptionKey);
    let text = decipher.update(input, "hex", "utf8");
    text += decipher.final("utf8");
    return text;
  } catch (e) {
    return "";
  }
};
<commit_msg>use non-depreciated iv-based encryption<commit_after>const crypto = require("crypto");

const ivLength = 16;
const saltLength = 64;
const tagLength = 16;
const tagPosition = saltLength + ivLength;
const inputPosition = tagPosition + tagLength;

module.exports.encrypt = (input, encryptionKey) => {
  try {
    const iv = crypto.randomBytes(ivLength);
    const salt = crypto.randomBytes(saltLength);
    const key = crypto.pbkdf2Sync(encryptionKey, salt, 100000, 32, "sha512");
    const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
    const encrypted = Buffer.concat([
      cipher.update(input, "utf8"),
      cipher.final(),
    ]);
    const tag = cipher.getAuthTag();
    return Buffer.concat([salt, iv, tag, encrypted]).toString("hex");
  } catch (error) {
    return "";
  }
};

module.exports.decrypt = (output, encryptionKey) => {
  try {
    const stringValue = Buffer.from(output, "hex");
    const salt = stringValue.slice(0, saltLength);
    const iv = stringValue.slice(saltLength, tagPosition);
    const tag = stringValue.slice(tagPosition, inputPosition);
    const encrypted = stringValue.slice(inputPosition);
    const key = crypto.pbkdf2Sync(encryptionKey, salt, 100000, 32, "sha512");
    const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
    decipher.setAuthTag(tag);
    return decipher.update(encrypted) + decipher.final("utf8");
  } catch (error) {
    return "";
  }
};
<|endoftext|>


<commit_before>'use strict';

/*!
 * Collection
 * https://github.com/kobezzza/Collection
 *
 * Released under the MIT license
 * https://github.com/kobezzza/Collection/blob/master/LICENSE
 */

import { Collection } from '../core';

import { isArray, isFunction, isPromise, isPositive } from '../helpers/types';
import { any } from '../helpers/gcc';

import { IS_NODE } from '../consts/env';
import { FN_LENGTH } from '../consts/symbols';

/**
 * Creates a new collection based on the current by the specified parameters
 *
 * @see Collection.prototype.forEach
 * @param {($$CollectionCb|$$Collection_map)=} opt_cb - callback function
 * @param {($$Collection_map|$$CollectionFilter)=} [opt_params] - additional parameters:
 *   *) [initial] - initial object for adding elements
 *
 * @return {(?|!Promise)}
 */
Collection.prototype.map = function (opt_cb, opt_params) {
	let p = opt_params || {};

	if (!isFunction(opt_cb)) {
		p = opt_cb || p;
		opt_cb = (el) => el;
	}

	if (isArray(p) || isFunction(p)) {
		p = {filter: p};
	}

	this._initParams(p);
	p = any(Object.assign(Object.create(this.p), p));

	const
		{data} = this;

	let
		type = p.initialType || p.type,
		res = p.initial;

	const
		hasInitial = p.initial != null,
		source = hasInitial ? p.initial : data;

	if (!hasInitial) {
		switch (type) {
			case 'object':
				res = {};
				break;

			case 'array':
				if (isArray(source)) {
					res = [];

				} else {
					res = {};
					type = 'object';
				}

				break;

			case 'generator':
			case 'iterator':
			case 'asyncIterator':
			case 'idbRequest':
				res = [];
				type = 'array';
				break;

			default:
				if (type === 'stream') {
					if (IS_NODE) {
						//#if isNode

						const
							{Transform} = require('stream');

						res = new Transform({
							readableObjectMode: true,
							writableObjectMode: true,
							transform(data, enc, cb) {
								cb(null, data);
							}
						});

						//#endif

					} else {
						res = [];
						type = 'array';
					}

				} else {
					res = new source.constructor();
				}
		}
	}

	let fn;
	p.result = res;

	switch (type) {
		case 'array':
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.push(val));
				}

				//#endif

				isPositive(val) && res.push(val);
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		case 'object':
			fn = function (el, key) {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => {
						if (isPositive(val)) {
							res[key] = val;
						}
					});
				}

				//#endif

				if (isPositive(val)) {
					res[key] = val;
				}
			};

			fn[FN_LENGTH] = fn.length > opt_cb.length ? fn.length : opt_cb.length;
			break;

		case 'map':
		case 'weakMap':
			fn = function (el, key) {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.set(key, val));
				}

				//#endif

				isPositive(val) && res.set(key, val);
			};

			fn[FN_LENGTH] = fn.length > opt_cb.length ? fn.length : opt_cb.length;
			break;

		case 'set':
		case 'weakSet':
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.add(val));
				}

				//#endif

				isPositive(val) && res.add(val);
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		case 'stream':
			fn = function () {
				return new Promise((resolve) => {
					let
						val = opt_cb.apply(null, arguments);

					function end() {
						clear();
						resolve();
					}

					function clear() {
						res.removeListener('drain', write);
						res.removeListener('error', end);
						res.removeListener('close', end);
					}

					function write() {
						clear();

						try {
							if (!isPositive(val)) {
								resolve();
								return;
							}

							if (res.write(val)) {
								resolve(val);

							} else {
								res.addListener('drain', write);
								res.addListener('error', end);
								res.addListener('close', end);
							}

						} catch (_) {
							end();
						}
					}

					//#if iterators/async

					if (p.async && isPromise(val)) {
						return val.then((res) => {
							val = res;
							write();
						});
					}

					//#endif

					return write();
				});
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		default:
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => {
						if (isPositive(val)) {
							p.result = res += val;
						}
					});
				}

				//#endif

				if (isPositive(val)) {
					p.result = res += val;
				}
			};

			fn[FN_LENGTH] = opt_cb.length;
	}

	const
		returnVal = any(this.forEach(any(fn), p));

	if (type === 'stream') {
		returnVal.then(() => res.end(), (err) => res.destroy(err));
		return p.result;
	}

	if (returnVal !== this) {
		return returnVal;
	}

	return p.result;
};
<commit_msg>Fixed stream support<commit_after>'use strict';

/*!
 * Collection
 * https://github.com/kobezzza/Collection
 *
 * Released under the MIT license
 * https://github.com/kobezzza/Collection/blob/master/LICENSE
 */

import { Collection } from '../core';

import { isArray, isFunction, isPromise, isPositive } from '../helpers/types';
import { any } from '../helpers/gcc';

import { IS_NODE } from '../consts/env';
import { FN_LENGTH } from '../consts/symbols';

/**
 * Creates a new collection based on the current by the specified parameters
 *
 * @see Collection.prototype.forEach
 * @param {($$CollectionCb|$$Collection_map)=} opt_cb - callback function
 * @param {($$Collection_map|$$CollectionFilter)=} [opt_params] - additional parameters:
 *   *) [initial] - initial object for adding elements
 *
 * @return {(?|!Promise)}
 */
Collection.prototype.map = function (opt_cb, opt_params) {
	let p = opt_params || {};

	if (!isFunction(opt_cb)) {
		p = opt_cb || p;
		opt_cb = (el) => el;
	}

	if (isArray(p) || isFunction(p)) {
		p = {filter: p};
	}

	this._initParams(p);
	p = any(Object.assign(Object.create(this.p), p));

	const
		{data} = this;

	let
		type = p.initialType || p.type,
		res = p.initial;

	const
		hasInitial = p.initial != null,
		source = hasInitial ? p.initial : data;

	if (!hasInitial) {
		switch (type) {
			case 'object':
				res = {};
				break;

			case 'array':
				if (isArray(source)) {
					res = [];

				} else {
					res = {length: source.length};
					type = 'object';
				}

				break;

			case 'generator':
			case 'iterator':
			case 'asyncIterator':
			case 'idbRequest':
				res = [];
				type = 'array';
				break;

			default:
				if (type === 'stream') {
					if (IS_NODE) {
						//#if isNode

						const
							{Transform} = require('stream');

						res = new Transform({
							readableObjectMode: true,
							writableObjectMode: true,
							transform(data, enc, cb) {
								cb(null, data);
							}
						});

						//#endif

					} else {
						res = [];
						type = 'array';
					}

				} else {
					res = new source.constructor();
				}
		}
	}

	let fn;
	p.result = res;

	switch (type) {
		case 'array':
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.push(val));
				}

				//#endif

				isPositive(val) && res.push(val);
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		case 'object':
			fn = function (el, key) {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => {
						if (isPositive(val)) {
							res[key] = val;
						}
					});
				}

				//#endif

				if (isPositive(val)) {
					res[key] = val;
				}
			};

			fn[FN_LENGTH] = fn.length > opt_cb.length ? fn.length : opt_cb.length;
			break;

		case 'map':
		case 'weakMap':
			fn = function (el, key) {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.set(key, val));
				}

				//#endif

				isPositive(val) && res.set(key, val);
			};

			fn[FN_LENGTH] = fn.length > opt_cb.length ? fn.length : opt_cb.length;
			break;

		case 'set':
		case 'weakSet':
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => isPositive(val) && res.add(val));
				}

				//#endif

				isPositive(val) && res.add(val);
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		case 'stream':
			fn = function () {
				return new Promise((resolve, reject) => {
					let
						val = opt_cb.apply(null, arguments);

					function end() {
						clear();
						resolve();
					}

					function onError(err) {
						clear();
						reject(err);
					}

					function clear() {
						res.removeListener('drain', write);
						res.removeListener('error', onError);
						res.removeListener('close', end);
					}

					function write() {
						clear();

						res.addListener('error', onError);
						res.addListener('close', end);

						try {
							if (!isPositive(val)) {
								res.end();
								end();
								return;
							}

							if (res.writableLength < res.writableHighWaterMark) {
								res.write(val, (err) => {
									if (err) {
										onError(err);
										return;
									}

									clear();
									resolve(val);
								});

							} else {
								res.addListener('drain', write);
							}

						} catch (err) {
							onError(err);
						}
					}

					//#if iterators/async

					if (p.async && isPromise(val)) {
						return val.then((res) => {
							val = res;
							write();
						});
					}

					//#endif

					return write();
				});
			};

			fn[FN_LENGTH] = opt_cb.length;
			break;

		default:
			fn = function () {
				const
					val = opt_cb.apply(null, arguments);

				//#if iterators/async

				if (p.async && isPromise(val)) {
					return val.then((val) => {
						if (isPositive(val)) {
							p.result = res += val;
						}
					});
				}

				//#endif

				if (isPositive(val)) {
					p.result = res += val;
				}
			};

			fn[FN_LENGTH] = opt_cb.length;
	}

	const
		returnVal = any(this.forEach(any(fn), p));

	if (type === 'stream') {
		returnVal.then(() => res.end(), (err) => res.destroy(err));
		return p.result;
	}

	if (returnVal !== this) {
		return returnVal;
	}

	return p.result;
};
<|endoftext|>


<commit_before>/*
Description: $.fn.dialog
Author: Kris Zhang
require:
  string.format.js
*/
(function($) {

  $.fn.dialog = function(options) {

    var self    = this
      , $this   = $(self)
      , $body   = $(document.body)
      , $msgbox = $this.closest(".dialog");

    var create = function() {

      var msghtml
        = ''
        // + '
'
        // + '
'
        // +   '

'
        // +   '

'
        // +   '
'
        // +   '

'
        // +   '
'
        // + '
'
        // + '

'
        // + '
'
        + '
'
        + '
'
        +   '
'
        +     '
'
        +         '<button type="button" class="close">×</button>'
        +         '

'
        +     '
'
        +     '

'
        +     '

'
        +   '
'
        + '
'
        + '

'
        + '
'
        ;

      $msgbox = $(msghtml);
      $(document.body).append($msgbox);
      $msgbox.find(".modal-body").append($this);
    };

    var createButton = function() {
      var buttons = options.buttons || {}
        , $btnrow = $msgbox.find(".modal-footer");

      //clear old buttons
      $btnrow.html('');

      for (var button in buttons) {
        var btnObj  = buttons[button]
          , id      = ""
          , text    = ""
          , classed = "btn-default"
          , click   = "";

        if (btnObj.constructor == Object) {
          id      = btnObj.id;
          text    = btnObj.text;
          classed = btnObj.classed || classed;
          click   = btnObj.click;
        }

        if (btnObj.constructor == Function) {
          click = btnObj;
        }

        //<button data-bb-handler="danger" type="button" class="btn btn-danger">Danger!</button>
        $button = $('<button type="button" class="btn {1}">{0}</button>'.format(text, classed));

        id && $button.attr("id", id);
        if (click) {
          (function(click) {
            $button.click(function() {
              click.call(self);
            });
          })(click);
        }

        $btnrow.append($button);
      }
    };

    var show = function() {
      $msgbox.show();
      $body.addClass("modal-open");
    };

    var close = function() {
      $msgbox.hide();
      $body.removeClass("modal-open");
    };

    if (options.constructor == Object) {
      if ($msgbox.size() < 1) {
        create();
      }
      createButton();
      $(".modal-title", $msgbox).html(options.title || "");
      $(".modal-header .close", $msgbox).click(function() {
        var closeHandler = options.onClose || close;
        closeHandler.call(self);
      });
      options.classed && $msgbox.addClass(options.classed);
      show();
    }

    if (options == "destroy") {
      close();
      $msgbox.remove();
    }

    if (options == "close") {
      close();
    }

    if (options == "open") {
      show();
    }

    return $this;
  };

})(jQuery);<commit_msg>Update jquery.dialog.js<commit_after>/*
Description: $.fn.dialog
Author: Kris Zhang
require:
  string.format.js
*/
(function($) {

  $.fn.dialog = function(options) {

    var self    = this
      , $this   = $(self)
      , $body   = $(document.body)
      , $msgbox = $this.closest(".dialog");

    var create = function() {

      var msghtml
        = ''
        // + '
'
        // + '
'
        // +   '

'
        // +   '

'
        // +   '
'
        // +   '

'
        // +   '
'
        // + '
'
        // + '

'
        // + '
'
        + '
'
        + '
'
        +   '
'
        +     '
'
        +         '<button type="button" class="close">×</button>'
        +         '

'
        +     '
'
        +     '

'
        +     '

'
        +   '
'
        + '
'
        + '

'
        + '
'
        ;

      $msgbox = $(msghtml);
      $(document.body).append($msgbox);
      $msgbox.find(".modal-body").append($this);
    };

    var createButton = function() {
      var buttons = options.buttons || {}
        , $btnrow = $msgbox.find(".modal-footer");

      //clear old buttons
      $btnrow.html('');

      for (var button in buttons) {
        var btnObj  = buttons[button]
          , id      = ""
          , text    = ""
          , classed = "btn-default"
          , click   = "";

        if (btnObj.constructor == Object) {
          id      = btnObj.id;
          text    = btnObj.text;
          classed = btnObj.classed || classed;
          click   = btnObj.click;
        }

        if (btnObj.constructor == Function) {
          click = btnObj;
        }

        //<button data-bb-handler="danger" type="button" class="btn btn-danger">Danger!</button>
        $button = $('<button type="button" class="btn {1}">{0}</button>'.format(text, classed));

        id && $button.attr("id", id);
        if (click) {
          (function(click) {
            $button.click(function() {
              click.call(self);
            });
          })(click);
        }

        $btnrow.append($button);
      }
    };

    var show = function() {
      $msgbox.show();
      $body.addClass("modal-open");
    };

    var close = function() {
      $msgbox.hide();
      $body.removeClass("modal-open");
    };

    if (options.constructor == Object) {
      if ($msgbox.size() < 1) {
        create();
      }
      createButton();
      $(".modal-title", $msgbox).html(options.title || "");
      $(".modal-header .close", $msgbox).click(function() {
        var closeHandler = options.onClose || close;
        closeHandler.call(self);
      });
      options.classed && $msgbox.addClass(options.classed);
      show();
    }

    if (options == "destroy") {
      close();
      $msgbox.remove();
    }

    if (options == "close") {
      close();
    }

    if (options == "open") {
      show();
    }

    return $this;
  };

})(jQuery);
<|endoftext|>


<commit_before>(function($) {
	$('body').on('change', '.filter', function() {
		var filter = $(this);
		$(filter.attr('data-filter-container')).children('.filterable').each(function(index, element) {
			var filterVal = $(element).attr('data-' + filter.attr('data-filter')).toLowerCase();
			if (filter.is('input')) {
				if (filterVal.indexOf(filter.val().toLowerCase()) === -1 && filter.val() !== '') {
					$(element).fadeOut();
				}else {
					$(element).fadeIn();
				}
			}else if (filter.is('select')) {
				if (filter.children(':selected').is('option[data-filter-ignore]')) {
					$(element).fadeIn();
					return;
				}
				if (filter.children(':selected').is('option[data-filter-value]')) {
					if (filterVal !== filter.children(':selected').attr('data-filter-value').toLowerCase()) {
						$(element).fadeOut();
					}else {
						$(element).fadeIn();
					}
				}else {
					if (filterVal !== filter.children(':selected').html().toLowerCase()) {
						$(element).fadeOut();
					}else {
						$(element).fadeIn();
					}
				}
			}
		});
	});

	$('body').on('click', '.reset-filter', function() {
		$('.filter').each(function(index, element) {
			if ($(element).is('input')) {
				$(element).val('');
			}else if ($(element).is('select')) {
				$(element).prop('selectedIndex', 0);
			}
		});
		$('.filterable').fadeIn();
	});
}(jQuery));
<commit_msg>Allow multiple filters to take effect<commit_after>$(function() {
	$('body').on('change', '.filter', function() {
		var originalFilter = $(this);
		$(originalFilter.attr('data-filter-container')).children('.filterable').each(function(index, filterable) {
			var filters = $('body').find(".filter[data-filter-container='" + originalFilter.attr('data-filter-container') + "']");
			var hide = false;
			$.each(filters, function(i, element) {
				var filter = $(element);
				var filterVal = $(filterable).attr('data-' + $(filter).attr('data-filter')).toLowerCase();
				if (filter.is('input')) {
					if (filterVal.indexOf(filter.val().toLowerCase()) === -1 && filter.val() !== '') {
						hide = true;
					}
				}else if (filter.is('select')) {
					if (filter.children(':selected').is('option[data-filter-ignore]')) {
						return;
					}
					if (filter.children(':selected').is('option[data-filter-value]')) {
						if (filterVal !== filter.children(':selected').attr('data-filter-value').toLowerCase()) {
							hide = true;
						}
					}else {
						if (filterVal !== filter.children(':selected').html().toLowerCase()) {
							hide = true;
						}
					}
				}
			});
			if (hide) $(filterable).fadeOut();
			else $(filterable).fadeIn();
		});
	});

	$('body').on('click', '.reset-filter', function() {
		$('.filter').each(function(index, element) {
			if ($(element).is('input')) {
				$(element).val('');
			}else if ($(element).is('select')) {
				$(element).prop('selectedIndex', 0);
			}
		});
		$('.filterable').fadeIn();
	});
});<|endoftext|>


<commit_before>/*
 * Swipe 2.0
 *
 * Brad Birdsall
 * Copyright 2013, MIT License
 *
 */

function Swipe(container, options) {

	"use strict";

	// utilities
	var noop = function () {
	}; // simple no operation function
	var offloadFn = function (fn) {
		setTimeout(fn || noop, 0)
	}; // offload a functions execution

	// check browser capabilities
	var browser = {
		addEventListener: !!window.addEventListener,
		touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
		transitions: (function (temp) {
			var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
			for (var i in props) if (temp.style[props[i]] !== undefined) return true;
			return false;
		})(document.createElement('swipe'))
	};

	// quit if no root element
	if (!container) return;
	var element = container.children[0];
	var slides, slidePos, width, length;
	options = options || {};
	var index = parseInt(options.startSlide, 10) || 0;
	var speed = options.speed || 300;
	options.continuous = options.continuous !== undefined ? options.continuous : true;

	function setup() {

		// cache slides
		slides = element.children;
		length = slides.length;

		// set continuous to false if only one slide
		if (slides.length < 2) options.continuous = false;

		//special case if two slides
		if (browser.transitions && options.continuous && slides.length < 3) {
			element.appendChild(slides[0].cloneNode(true));
			element.appendChild(element.children[1].cloneNode(true));
			slides = element.children;
		}

		// create an array to store current positions of each slide
		slidePos = new Array(slides.length);

		// determine width of each slide
		width = container.getBoundingClientRect().width || container.offsetWidth;

		element.style.width = (slides.length * width) + 'px';

		// stack elements
		var pos = slides.length;
		while (pos--) {

			var slide = slides[pos];

			slide.style.width = width + 'px';
			slide.setAttribute('data-index', pos);

			if (browser.transitions) {
				slide.style.left = (pos * -width) + 'px';
				move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
			}

		}

		// reposition elements before and after index
		if (options.continuous && browser.transitions) {
			move(circle(index - 1), -width, 0);
			move(circle(index + 1), width, 0);
		}

		if (!browser.transitions) element.style.left = (index * -width) + 'px';

		container.style.visibility = 'visible';

	}

	function prev() {

		if (options.continuous) slide(index - 1);
		else if (index) slide(index - 1);

	}

	function next() {

		if (options.continuous) slide(index + 1);
		else if (index < slides.length - 1) slide(index + 1);

	}

	function circle(index) {

		// a simple positive modulo using slides.length
		return (slides.length + (index % slides.length)) % slides.length;

	}

	function slide(to, slideSpeed) {

		// do nothing if already on requested slide
		if (index == to) return;

		if (browser.transitions) {

			var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward

			// get the actual position of the slide
			if (options.continuous) {
				var natural_direction = direction;
				direction = -slidePos[circle(to)] / width;

				// if going forward but to < index, use to = slides.length + to
				// if going backward but to > index, use to = -slides.length + to
				if (direction !== natural_direction) to = -direction * slides.length + to;

			}

			var diff = Math.abs(index - to) - 1;

			// move all the slides between index and to in the right direction
			while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0);

			to = circle(to);

			move(index, width * direction, slideSpeed || speed);
			move(to, 0, slideSpeed || speed);

			if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place

		} else {

			to = circle(to);
			animate(index * -width, to * -width, slideSpeed || speed);
			//no fallback for a circular continuous if the browser does not accept transitions
		}

		index = to;
		offloadFn(options.callback && options.callback(index, slides[index]));
	}

	function move(index, dist, speed) {

		translate(index, dist, speed);
		slidePos[index] = dist;

	}

	function translate(index, dist, speed) {

		var slide = slides[index];
		var style = slide && slide.style;

		if (!style) return;

		style.webkitTransitionDuration =
			style.MozTransitionDuration =
				style.msTransitionDuration =
					style.OTransitionDuration =
						style.transitionDuration = speed + 'ms';

		style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
		style.msTransform =
			style.MozTransform =
				style.OTransform = 'translateX(' + dist + 'px)';

	}

	function animate(from, to, speed) {

		// if not an animation, just reposition
		if (!speed) {

			element.style.left = to + 'px';
			return;

		}

		var start = +new Date;

		var timer = setInterval(function () {

			var timeElap = +new Date - start;

			if (timeElap > speed) {

				element.style.left = to + 'px';

				if (delay) begin();

				options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

				clearInterval(timer);
				return;

			}

			element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';

		}, 4);

	}

	// setup auto slideshow
	var delay = options.auto || 0;
	var interval;

	function begin() {

		interval = setTimeout(next, delay);

	}

	function stop() {

		delay = 0;
		clearTimeout(interval);

	}

	// setup initial vars
	var start = {};
	var delta = {};
	var isScrolling;

	// setup event capturing
	var events = {

		handleEvent: function (event) {

			switch (event.type) {
				case 'touchstart':
					this.start(event);
					break;
				case 'touchmove':
					this.move(event);
					break;
				case 'touchend':
					offloadFn(this.end(event));
					break;
				case 'webkitTransitionEnd':
				case 'msTransitionEnd':
				case 'oTransitionEnd':
				case 'otransitionend':
				case 'transitionend':
					offloadFn(this.transitionEnd(event));
					break;
				case 'resize':
					offloadFn(setup);
					break;
			}

			if (options.stopPropagation) event.stopPropagation();

		},
		start: function (event) {

			var touches = event.touches[0];

			// measure start values
			start = {

				// get initial touch coords
				x: touches.pageX,
				y: touches.pageY,

				// store time to determine touch duration
				time: +new Date

			};

			// used for testing first move event
			isScrolling = undefined;

			// reset delta and end measurements
			delta = {};

			// attach touchmove and touchend listeners
			element.addEventListener('touchmove', this, false);
			element.addEventListener('touchend', this, false);

		},
		move: function (event) {

			// ensure swiping with one touch and not pinching
			if (event.touches.length > 1 || event.scale && event.scale !== 1) return

			if (options.disableScroll) event.preventDefault();

			var touches = event.touches[0];

			// measure change in x and y
			delta = {
				x: touches.pageX - start.x,
				y: touches.pageY - start.y
			}

			// determine if scrolling test has run - one time test
			if (typeof isScrolling == 'undefined') {
				isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );
			}

			// if user is not trying to scroll vertically
			if (!isScrolling) {

				// prevent native scrolling
				event.preventDefault();

				// stop slideshow
				stop();

				// increase resistance if first or last slide
				if (options.continuous) { // we don't add resistance at the end

					translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);
					translate(index, delta.x + slidePos[index], 0);
					translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);

				} else {

					delta.x =
						delta.x /
						( (!index && delta.x > 0               // if first slide and sliding left
							|| index == slides.length - 1        // or if last slide and sliding right
							&& delta.x < 0                       // and if sliding at all
						) ?
							( Math.abs(delta.x) / width + 1 )      // determine resistance level
							: 1 );                                 // no resistance if false

					// translate 1:1
					translate(index - 1, delta.x + slidePos[index - 1], 0);
					translate(index, delta.x + slidePos[index], 0);
					translate(index + 1, delta.x + slidePos[index + 1], 0);
				}

			}

		},
		end: function (event) {

			// measure duration
			var duration = +new Date - start.time;

			// determine if slide attempt triggers next/prev slide
			var isValidSlide =
				Number(duration) < 250               // if slide duration is less than 250ms
				&& Math.abs(delta.x) > 20            // and if slide amt is greater than 20px
				|| Math.abs(delta.x) > width / 2;      // or if slide amt is greater than half the width

			// determine if slide attempt is past start and end
			var isPastBounds =
				!index && delta.x > 0                            // if first slide and slide amt is greater than 0
				|| index == slides.length - 1 && delta.x < 0;    // or if last slide and slide amt is less than 0

			if (options.continuous) isPastBounds = false;

			// determine direction of swipe (true:right, false:left)
			var direction = delta.x < 0;

			// if not scrolling vertically
			if (!isScrolling) {

				if (isValidSlide && !isPastBounds) {

					if (direction) {

						if (options.continuous) { // we need to get the next in this direction in place

							move(circle(index - 1), -width, 0);
							move(circle(index + 2), width, 0);

						} else {
							move(index - 1, -width, 0);
						}

						move(index, slidePos[index] - width, speed);
						move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);
						index = circle(index + 1);

					} else {
						if (options.continuous) { // we need to get the next in this direction in place

							move(circle(index + 1), width, 0);
							move(circle(index - 2), -width, 0);

						} else {
							move(index + 1, width, 0);
						}

						move(index, slidePos[index] + width, speed);
						move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);
						index = circle(index - 1);

					}

					options.callback && options.callback(index, slides[index]);

				} else {

					if (options.continuous) {

						move(circle(index - 1), -width, speed);
						move(index, 0, speed);
						move(circle(index + 1), width, speed);

					} else {

						move(index - 1, -width, speed);
						move(index, 0, speed);
						move(index + 1, width, speed);
					}

				}

			}

			// kill touchmove and touchend event listeners until touchstart called again
			element.removeEventListener('touchmove', events, false)
			element.removeEventListener('touchend', events, false)

		},
		transitionEnd: function (event) {

			if (parseInt(event.target.getAttribute('data-index'), 10) == index) {

				if (delay) begin();

				options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

			}

		}

	}

	// trigger setup
	setup();

	// start auto slideshow if applicable
	if (delay) begin();

	// add event listeners
	if (browser.addEventListener) {

		// set touchstart event on element
		if (browser.touch) element.addEventListener('touchstart', events, false);

		if (browser.transitions) {
			element.addEventListener('webkitTransitionEnd', events, false);
			element.addEventListener('msTransitionEnd', events, false);
			element.addEventListener('oTransitionEnd', events, false);
			element.addEventListener('otransitionend', events, false);
			element.addEventListener('transitionend', events, false);
		}

		// set resize event on window
		window.addEventListener('resize', events, false);

	} else {

		window.onresize = function () {
			setup()
		}; // to play nice with old IE

	}

	// expose the Swipe API
	return {
		setup: function () {

			setup();

		},

		refresh: function () {
			setup();
		},
		slide: function (to, speed) {

			// cancel slideshow
			stop();

			slide(to, speed);

		},
		prev: function () {

			// cancel slideshow
			stop();

			prev();

		},
		next: function () {

			// cancel slideshow
			stop();

			next();

		},
		stop: function () {

			// cancel slideshow
			stop();

		},
		getPos: function () {

			// return current index position
			return index;

		},
		getNumSlides: function () {

			// return total number of slides
			return length;
		},
		kill: function () {

			// cancel slideshow
			stop();

			// reset element
			element.style.width = '';
			element.style.left = '';

			// reset slides
			var pos = slides.length;
			while (pos--) {

				var slide = slides[pos];
				slide.style.width = '';
				slide.style.left = '';

				if (browser.transitions) translate(pos, 0, 0);

			}

			// removed event listeners
			if (browser.addEventListener) {

				// remove current event listeners
				element.removeEventListener('touchstart', events, false);
				element.removeEventListener('webkitTransitionEnd', events, false);
				element.removeEventListener('msTransitionEnd', events, false);
				element.removeEventListener('oTransitionEnd', events, false);
				element.removeEventListener('otransitionend', events, false);
				element.removeEventListener('transitionend', events, false);
				window.removeEventListener('resize', events, false);

			}
			else {

				window.onresize = null;

			}

		}
	}

}

if (window.jQuery || window.Zepto) {
	(function ($) {
		$.fn.Swipe = function (params) {
			return this.each(function () {
				$(this).data('Swipe', new Swipe($(this)[0], params));
			});
		}
	})(window.jQuery || window.Zepto)
}
<commit_msg>修复carousel 模板模式下的小bug<commit_after>/*
 * Swipe 2.0
 *
 * Brad Birdsall
 * Copyright 2013, MIT License
 *
 */

function Swipe(container, options) {

	"use strict";

	// utilities
	var noop = function () {
	}; // simple no operation function
	var offloadFn = function (fn) {
		setTimeout(fn || noop, 0)
	}; // offload a functions execution

	// check browser capabilities
	var browser = {
		addEventListener: !!window.addEventListener,
		touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,
		transitions: (function (temp) {
			var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition'];
			for (var i in props) if (temp.style[props[i]] !== undefined) return true;
			return false;
		})(document.createElement('swipe'))
	};

	// quit if no root element
	if (!container) return;
	var element = $fly(container).find("> .items-wrap")[0];
	var slides, slidePos, width, length;
	options = options || {};
	var index = parseInt(options.startSlide, 10) || 0;
	var speed = options.speed || 300;
	options.continuous = options.continuous !== undefined ? options.continuous : true;

	function setup() {

		// cache slides
		slides = element.children;
		length = slides.length;

		// set continuous to false if only one slide
		if (slides.length < 2) options.continuous = false;

		//special case if two slides
		if (browser.transitions && options.continuous && slides.length < 3) {
			element.appendChild(slides[0].cloneNode(true));
			element.appendChild(element.children[1].cloneNode(true));
			slides = element.children;
		}

		// create an array to store current positions of each slide
		slidePos = new Array(slides.length);

		// determine width of each slide
		width = container.getBoundingClientRect().width || container.offsetWidth;

		element.style.width = (slides.length * width) + 'px';

		// stack elements
		var pos = slides.length;
		while (pos--) {

			var slide = slides[pos];

			slide.style.width = width + 'px';
			slide.setAttribute('data-index', pos);

			if (browser.transitions) {
				slide.style.left = (pos * -width) + 'px';
				move(pos, index > pos ? -width : (index < pos ? width : 0), 0);
			}

		}

		// reposition elements before and after index
		if (options.continuous && browser.transitions) {
			move(circle(index - 1), -width, 0);
			move(circle(index + 1), width, 0);
		}

		if (!browser.transitions) element.style.left = (index * -width) + 'px';

		container.style.visibility = 'visible';

	}

	function prev() {

		if (options.continuous) slide(index - 1);
		else if (index) slide(index - 1);

	}

	function next() {

		if (options.continuous) slide(index + 1);
		else if (index < slides.length - 1) slide(index + 1);

	}

	function circle(index) {

		// a simple positive modulo using slides.length
		return (slides.length + (index % slides.length)) % slides.length;

	}

	function slide(to, slideSpeed) {

		// do nothing if already on requested slide
		if (index == to) return;

		if (browser.transitions) {

			var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward

			// get the actual position of the slide
			if (options.continuous) {
				var natural_direction = direction;
				direction = -slidePos[circle(to)] / width;

				// if going forward but to < index, use to = slides.length + to
				// if going backward but to > index, use to = -slides.length + to
				if (direction !== natural_direction) to = -direction * slides.length + to;

			}

			var diff = Math.abs(index - to) - 1;

			// move all the slides between index and to in the right direction
			while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0);

			to = circle(to);

			move(index, width * direction, slideSpeed || speed);
			move(to, 0, slideSpeed || speed);

			if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place

		} else {

			to = circle(to);
			animate(index * -width, to * -width, slideSpeed || speed);
			//no fallback for a circular continuous if the browser does not accept transitions
		}

		index = to;
		offloadFn(options.callback && options.callback(index, slides[index]));
	}

	function move(index, dist, speed) {

		translate(index, dist, speed);
		slidePos[index] = dist;

	}

	function translate(index, dist, speed) {

		var slide = slides[index];
		var style = slide && slide.style;

		if (!style) return;

		style.webkitTransitionDuration =
			style.MozTransitionDuration =
				style.msTransitionDuration =
					style.OTransitionDuration =
						style.transitionDuration = speed + 'ms';

		style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)';
		style.msTransform =
			style.MozTransform =
				style.OTransform = 'translateX(' + dist + 'px)';

	}

	function animate(from, to, speed) {

		// if not an animation, just reposition
		if (!speed) {

			element.style.left = to + 'px';
			return;

		}

		var start = +new Date;

		var timer = setInterval(function () {

			var timeElap = +new Date - start;

			if (timeElap > speed) {

				element.style.left = to + 'px';

				if (delay) begin();

				options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

				clearInterval(timer);
				return;

			}

			element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px';

		}, 4);

	}

	// setup auto slideshow
	var delay = options.auto || 0;
	var interval;

	function begin() {

		interval = setTimeout(next, delay);

	}

	function stop() {

		delay = 0;
		clearTimeout(interval);

	}

	// setup initial vars
	var start = {};
	var delta = {};
	var isScrolling;

	// setup event capturing
	var events = {

		handleEvent: function (event) {

			switch (event.type) {
				case 'touchstart':
					this.start(event);
					break;
				case 'touchmove':
					this.move(event);
					break;
				case 'touchend':
					offloadFn(this.end(event));
					break;
				case 'webkitTransitionEnd':
				case 'msTransitionEnd':
				case 'oTransitionEnd':
				case 'otransitionend':
				case 'transitionend':
					offloadFn(this.transitionEnd(event));
					break;
				case 'resize':
					offloadFn(setup);
					break;
			}

			if (options.stopPropagation) event.stopPropagation();

		},
		start: function (event) {

			var touches = event.touches[0];

			// measure start values
			start = {

				// get initial touch coords
				x: touches.pageX,
				y: touches.pageY,

				// store time to determine touch duration
				time: +new Date

			};

			// used for testing first move event
			isScrolling = undefined;

			// reset delta and end measurements
			delta = {};

			// attach touchmove and touchend listeners
			element.addEventListener('touchmove', this, false);
			element.addEventListener('touchend', this, false);

		},
		move: function (event) {

			// ensure swiping with one touch and not pinching
			if (event.touches.length > 1 || event.scale && event.scale !== 1) return

			if (options.disableScroll) event.preventDefault();

			var touches = event.touches[0];

			// measure change in x and y
			delta = {
				x: touches.pageX - start.x,
				y: touches.pageY - start.y
			}

			// determine if scrolling test has run - one time test
			if (typeof isScrolling == 'undefined') {
				isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) );
			}

			// if user is not trying to scroll vertically
			if (!isScrolling) {

				// prevent native scrolling
				event.preventDefault();

				// stop slideshow
				stop();

				// increase resistance if first or last slide
				if (options.continuous) { // we don't add resistance at the end

					translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0);
					translate(index, delta.x + slidePos[index], 0);
					translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0);

				} else {

					delta.x =
						delta.x /
						( (!index && delta.x > 0               // if first slide and sliding left
							|| index == slides.length - 1        // or if last slide and sliding right
							&& delta.x < 0                       // and if sliding at all
						) ?
							( Math.abs(delta.x) / width + 1 )      // determine resistance level
							: 1 );                                 // no resistance if false

					// translate 1:1
					translate(index - 1, delta.x + slidePos[index - 1], 0);
					translate(index, delta.x + slidePos[index], 0);
					translate(index + 1, delta.x + slidePos[index + 1], 0);
				}

			}

		},
		end: function (event) {

			// measure duration
			var duration = +new Date - start.time;

			// determine if slide attempt triggers next/prev slide
			var isValidSlide =
				Number(duration) < 250               // if slide duration is less than 250ms
				&& Math.abs(delta.x) > 20            // and if slide amt is greater than 20px
				|| Math.abs(delta.x) > width / 2;      // or if slide amt is greater than half the width

			// determine if slide attempt is past start and end
			var isPastBounds =
				!index && delta.x > 0                            // if first slide and slide amt is greater than 0
				|| index == slides.length - 1 && delta.x < 0;    // or if last slide and slide amt is less than 0

			if (options.continuous) isPastBounds = false;

			// determine direction of swipe (true:right, false:left)
			var direction = delta.x < 0;

			// if not scrolling vertically
			if (!isScrolling) {

				if (isValidSlide && !isPastBounds) {

					if (direction) {

						if (options.continuous) { // we need to get the next in this direction in place

							move(circle(index - 1), -width, 0);
							move(circle(index + 2), width, 0);

						} else {
							move(index - 1, -width, 0);
						}

						move(index, slidePos[index] - width, speed);
						move(circle(index + 1), slidePos[circle(index + 1)] - width, speed);
						index = circle(index + 1);

					} else {
						if (options.continuous) { // we need to get the next in this direction in place

							move(circle(index + 1), width, 0);
							move(circle(index - 2), -width, 0);

						} else {
							move(index + 1, width, 0);
						}

						move(index, slidePos[index] + width, speed);
						move(circle(index - 1), slidePos[circle(index - 1)] + width, speed);
						index = circle(index - 1);

					}

					options.callback && options.callback(index, slides[index]);

				} else {

					if (options.continuous) {

						move(circle(index - 1), -width, speed);
						move(index, 0, speed);
						move(circle(index + 1), width, speed);

					} else {

						move(index - 1, -width, speed);
						move(index, 0, speed);
						move(index + 1, width, speed);
					}

				}

			}

			// kill touchmove and touchend event listeners until touchstart called again
			element.removeEventListener('touchmove', events, false)
			element.removeEventListener('touchend', events, false)

		},
		transitionEnd: function (event) {

			if (parseInt(event.target.getAttribute('data-index'), 10) == index) {

				if (delay) begin();

				options.transitionEnd && options.transitionEnd.call(event, index, slides[index]);

			}

		}

	}

	// trigger setup
	setup();

	// start auto slideshow if applicable
	if (delay) begin();

	// add event listeners
	if (browser.addEventListener) {

		// set touchstart event on element
		if (browser.touch) element.addEventListener('touchstart', events, false);

		if (browser.transitions) {
			element.addEventListener('webkitTransitionEnd', events, false);
			element.addEventListener('msTransitionEnd', events, false);
			element.addEventListener('oTransitionEnd', events, false);
			element.addEventListener('otransitionend', events, false);
			element.addEventListener('transitionend', events, false);
		}

		// set resize event on window
		window.addEventListener('resize', events, false);

	} else {

		window.onresize = function () {
			setup()
		}; // to play nice with old IE

	}

	// expose the Swipe API
	return {
		setup: function () {

			setup();

		},

		refresh: function () {
			setup();
		},
		slide: function (to, speed) {

			// cancel slideshow
			stop();

			slide(to, speed);

		},
		prev: function () {

			// cancel slideshow
			stop();

			prev();

		},
		next: function () {

			// cancel slideshow
			stop();

			next();

		},
		stop: function () {

			// cancel slideshow
			stop();

		},
		getPos: function () {

			// return current index position
			return index;

		},
		getNumSlides: function () {

			// return total number of slides
			return length;
		},
		kill: function () {

			// cancel slideshow
			stop();

			// reset element
			element.style.width = '';
			element.style.left = '';

			// reset slides
			var pos = slides.length;
			while (pos--) {

				var slide = slides[pos];
				slide.style.width = '';
				slide.style.left = '';

				if (browser.transitions) translate(pos, 0, 0);

			}

			// removed event listeners
			if (browser.addEventListener) {

				// remove current event listeners
				element.removeEventListener('touchstart', events, false);
				element.removeEventListener('webkitTransitionEnd', events, false);
				element.removeEventListener('msTransitionEnd', events, false);
				element.removeEventListener('oTransitionEnd', events, false);
				element.removeEventListener('otransitionend', events, false);
				element.removeEventListener('transitionend', events, false);
				window.removeEventListener('resize', events, false);

			}
			else {

				window.onresize = null;

			}

		}
	}

}

if (window.jQuery || window.Zepto) {
	(function ($) {
		$.fn.Swipe = function (params) {
			return this.each(function () {
				$(this).data('Swipe', new Swipe($(this)[0], params));
			});
		}
	})(window.jQuery || window.Zepto)
}
<|endoftext|>


<commit_before>import Const from './const';

export default class JsSerializer {
  constructor(config) {
    this.config = config;
    this.colorStack = [];
  }

  serialize(root, option = {}) {
    let result = [];
    const varNo = option.varNo ? option.varNo : this.config.varNo;

    result = result.concat(this._serializeScenarioBlock(root, varNo));

    return result.join("\n");
  }

  _serializeScenarioBlock(scenarioBlock, varNo) {
    let result = [];

    scenarioBlock.child.forEach((child) => {
      if(child.type === Const.block_type.scenario) {
        const nextVarNo = scenarioBlock.no < 1 ? varNo : varNo + 1;
        result = result.concat(this._serializeScenarioBlock(child, nextVarNo));
      } else {
        result = result.concat(this._serializeMessageBlock(child));
      }
    });

    if (scenarioBlock.no > 0) {
      // nested block
      result.unshift(`if (_text${varNo} == ${scenarioBlock.no}) {`);
      // result.push('tkMock.raw(`Exit`)'); // イベントを分けないのでExit不要
      result.push('}');
    }

    return result;
  }

  _serializeMessageBlock(messageBlockList) {
    const result = [];
    let showFace = true;
    messageBlockList.forEach((messageBlock) => {
      // 顔グラ関連
      let faceMessage = false;
      if (messageBlock.face) {
        faceMessage = this._toTbScript(messageBlock.face.name);
        if (messageBlock.face.filename) {
          showFace = true;
          const posCode = messageBlock.face.pos ? 1 : 0;
          const mirrorCode = messageBlock.face.mirror ? 1 : 0;
          result.push(`tkMock.raw('Faice("${messageBlock.face.filename}", ${messageBlock.face.number}, ${posCode}, ${mirrorCode})');`);
        } else {
          showFace = false;
          // 顔グラを非表示に
          result.push('tkMock.raw(`Faice(0, 0, 0)`);');
        }
      } else if (showFace) {
        showFace = false;
        // 顔グラを非表示に
        result.push('tkMock.raw(`Faice(0, 0, 0)`);');
      }
      messageBlock.messageList.forEach((message) => {
        // コメント行の出力
        message.comments.forEach((comment) => {
          result.push(`tkMock.comment('${comment}');`);
        });
        // タグ置換
        this.colorStack = []; // 色タグのスタックリセット
        let line = message.line.map((text)=> {
          return this._toTbScript(text);
        });

        // 改行を置換してまとめる
        if (faceMessage) {
          line.unshift(faceMessage);
        }
        // 常時瞬間表示の場合、制御文字を追加
        if (this.config.isFlash) {
          line = line.map((v) => {return `\\>${v}`;});
        }
        result.push(`tkMock.raw('Text("${line.join(cChar.br).replace(/\\/g, '\\\\')}")');`);
      });
    });

    return result;
  }

  _toTbScript(text) {
    // タグとメッセージに分解
    let parts = text.split(/(\\?<\/?[a-z\-\_]+>)/);
    if (parts.length == 1) {
      // 変換無し
      return this._removeEscapeChar(text);
    }
    // タグの変換
    let prevColor = 0;
    parts = parts.map((part) => {
      if (/^<[a-z\-\_]+>$/.test(part)) {
        // 開始タグ
        const tagName = part.substr(1, part.length - 2);
        const colorNumber = this.config.getColorNumber(tagName);
        if (colorNumber) {
          // 色タグ
          this.colorStack.push(prevColor);
          prevColor = colorNumber;
          return `${cChar.color}[${colorNumber}]`;
        }
        // 制御タグ
        return `${this._getCChar(tagName)}`;
      } else if (/^<\/[a-z\-\_]+>$/.test(part)) {
        // 閉じタグ
        const tagName = part.substr(2, part.length - 3);
        if (!this.config.getColorNumber(tagName) === false) {
          // 色タグ
          prevColor = this.colorStack.pop();
          return `${cChar.color}[${prevColor}]`;
        } else if (cNormalTags.includes(tagName)) {
          // 閉じタグ有りの制御タグ
          return `${this._getCChar(tagName + '_end')}`;
        } else if (cNoEndTags.includes(tagName)) {
          // 閉じタグが無いタグの場合、空
          return '';
        }
      }
      // タグ以外のテキストの場合、エスケープ文字を消す
      return this._removeEscapeChar(part);
    });
    return parts.join('');
  }

  _getCChar(name) {
    if (!cChar[name]) {
      throw new Error(`対応していないタグです。: ${name}`);
    }
    return cChar[name];
  }

  _removeEscapeChar(text) {
    return text.replace(/([^\\]?)\\/, '$1').replace('\\', '\\\\');
  }
}

const cChar = {
  br : '\\k',
  color: '\\C',
  stop: '\\!',
  wait: '\\|',
  q_wait: '\\.',
  close: '\\^',
  flash: '\\>',
  flash_end: '\\<'
};

const cNoEndTags = ['br', 'stop', 'wait', 'q_wait', 'close'];
const cNormalTags = ['flash'];
<commit_msg>Comment fix<commit_after>import Const from './const';

export default class JsSerializer {
  constructor(config) {
    this.config = config;
    this.colorStack = [];
  }

  serialize(root, option = {}) {
    let result = [];
    const varNo = option.varNo ? option.varNo : this.config.varNo;

    result = result.concat(this._serializeScenarioBlock(root, varNo));

    return result.join("\n");
  }

  _serializeScenarioBlock(scenarioBlock, varNo) {
    let result = [];

    scenarioBlock.child.forEach((child) => {
      if(child.type === Const.block_type.scenario) {
        const nextVarNo = scenarioBlock.no < 1 ? varNo : varNo + 1;
        result = result.concat(this._serializeScenarioBlock(child, nextVarNo));
      } else {
        result = result.concat(this._serializeMessageBlock(child));
      }
    });

    if (scenarioBlock.no > 0) {
      // nested block
      result.unshift(`if (_text${varNo} == ${scenarioBlock.no}) {`);
      // result.push('tkMock.raw(`Exit`)'); // イベントを分けないのでExit不要
      result.push('}');
    }

    return result;
  }

  _serializeMessageBlock(messageBlockList) {
    const result = [];
    let showFace = true;
    messageBlockList.forEach((messageBlock) => {
      // 顔グラ関連
      let faceMessage = false;
      if (messageBlock.face) {
        faceMessage = this._toTbScript(messageBlock.face.name);
        if (messageBlock.face.filename) {
          showFace = true;
          const posCode = messageBlock.face.pos ? 1 : 0;
          const mirrorCode = messageBlock.face.mirror ? 1 : 0;
          result.push(`tkMock.raw('Faice("${messageBlock.face.filename}", ${messageBlock.face.number}, ${posCode}, ${mirrorCode})');`);
        } else {
          showFace = false;
          // 顔グラを非表示に
          result.push('tkMock.raw(`Faice(0, 0, 0)`);');
        }
      } else if (showFace) {
        showFace = false;
        // 顔グラを非表示に
        result.push('tkMock.raw(`Faice(0, 0, 0)`);');
      }
      messageBlock.messageList.forEach((message) => {
        // コメント行の出力
        message.comments.forEach((comment) => {
          // コメントはJavaScriptのコメントとして出力する
          result.push(`// ${comment}`);
        });
        // タグ置換
        this.colorStack = []; // 色タグのスタックリセット
        let line = message.line.map((text)=> {
          return this._toTbScript(text);
        });

        // 改行を置換してまとめる
        if (faceMessage) {
          line.unshift(faceMessage);
        }
        // 常時瞬間表示の場合、制御文字を追加
        if (this.config.isFlash) {
          line = line.map((v) => {return `\\>${v}`;});
        }
        result.push(`tkMock.raw('Text("${line.join(cChar.br).replace(/\\/g, '\\\\')}")');`);
      });
    });

    return result;
  }

  _toTbScript(text) {
    // タグとメッセージに分解
    let parts = text.split(/(\\?<\/?[a-z\-\_]+>)/);
    if (parts.length == 1) {
      // 変換無し
      return this._removeEscapeChar(text);
    }
    // タグの変換
    let prevColor = 0;
    parts = parts.map((part) => {
      if (/^<[a-z\-\_]+>$/.test(part)) {
        // 開始タグ
        const tagName = part.substr(1, part.length - 2);
        const colorNumber = this.config.getColorNumber(tagName);
        if (colorNumber) {
          // 色タグ
          this.colorStack.push(prevColor);
          prevColor = colorNumber;
          return `${cChar.color}[${colorNumber}]`;
        }
        // 制御タグ
        return `${this._getCChar(tagName)}`;
      } else if (/^<\/[a-z\-\_]+>$/.test(part)) {
        // 閉じタグ
        const tagName = part.substr(2, part.length - 3);
        if (!this.config.getColorNumber(tagName) === false) {
          // 色タグ
          prevColor = this.colorStack.pop();
          return `${cChar.color}[${prevColor}]`;
        } else if (cNormalTags.includes(tagName)) {
          // 閉じタグ有りの制御タグ
          return `${this._getCChar(tagName + '_end')}`;
        } else if (cNoEndTags.includes(tagName)) {
          // 閉じタグが無いタグの場合、空
          return '';
        }
      }
      // タグ以外のテキストの場合、エスケープ文字を消す
      return this._removeEscapeChar(part);
    });
    return parts.join('');
  }

  _getCChar(name) {
    if (!cChar[name]) {
      throw new Error(`対応していないタグです。: ${name}`);
    }
    return cChar[name];
  }

  _removeEscapeChar(text) {
    return text.replace(/([^\\]?)\\/, '$1').replace('\\', '\\\\');
  }
}

const cChar = {
  br : '\\k',
  color: '\\C',
  stop: '\\!',
  wait: '\\|',
  q_wait: '\\.',
  close: '\\^',
  flash: '\\>',
  flash_end: '\\<'
};

const cNoEndTags = ['br', 'stop', 'wait', 'q_wait', 'close'];
const cNormalTags = ['flash'];
<|endoftext|>