<commit_before>/* ===================================================
 * JavaScript 2ch-client Library
 * https://github.com/sh19910711/js2ch
 * ===================================================
 * Copyright (c) 2013 Hiroyuki Sano
 *
 * Licensed under MIT License.
 * http://opensource.org/licenses/MIT
 * =================================================== */
/**
 * @fileOverview 2ch専用ブラウザ・クライアント用のライブラリ
 * @author Hiroyuki Sano
 */
(function() {
  'use strict';

  var root = this;

  define([
    'jquery',
    'underscore',
    'backbone',
    'http-lib',
    'storage',
    'cookie-manager',
    'parser',
    'encoding',
    'util-lib',
    'logger'
  ], function($, _, Backbone, HttpLib, Storage, CookieManager, Parser, encoding, UtilLib, Logger) {

    var STORAGE_FORM_APPEND_PARAMS = 'form_append_params';

    /**
     * @constructor Client
     */
    var Client = function(options, callback_context) {
      callback_context = callback_context || this;
      options = (options && options['client']) || options || {};

      /**
       * @description HTTPリクエスト時に渡すヘッダのリスト
       * @memberof Client
       *
       */
      this.HTTP_REQ_HEADERS_DEFAULT = {
        'User-Agent': 'Monazilla/1.00'
      };

      this.http = new HttpLib((options && options['http-lib']) || options, this);
      this.parser = new Parser((options && options['parser']) || options, this);
      this.logger = new Logger((options && options['logger']) || options, this);
      this.storage = new Storage((options && options['storage']) || options, this);
      this.cookie_manager = new CookieManager((options && options['cookie-manager']) || options || {}, this);

      // Deferredの設定
      var keys = ['getThreadList', 'getSettingText', 'getResponsesFromThread'];
      _(keys)
        .each(function(key) {
          this[key] = UtilLib.getDeferredFunc(this[key], this, callback_context);
        }, this);

      // Deferredの設定（失敗する可能性があるもの）
      var keys = ['putResponseToThread'];
      _(keys)
        .each(function(key) {
          this[key] = UtilLib.getDeferredFuncWillFail(this[key], this, callback_context);
        }, this);
    };

    Client.prototype = {};
    var proto_extend = function(obj) {
      _.extend(Client.prototype, obj);
    };

    proto_extend({
      /**
       * @description スレッド一覧を取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {Client#getThreadList-callback} callback
       * スレッド一覧取得後、callback(Array) として呼び出される。
       */
      getThreadList: function getThreadList(hostname, board_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + '/subject.txt');
        this.http.get(
          url,
          _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname,
          }))
          .done(function(http_response) {
            callback(this.parser.parseThreadList(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description スレッド一覧取得後、callback(Array)として呼び出される
     * @callback Client#getThreadList-callback
     * @param {Array} thread_list
     * スレッド情報の配列
     */

    proto_extend({
      /**
       * @description [未実装] SETTING.TXTを取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {Client#getSettingText-callback} callback
       * 情報取得後 callback(Object) として呼び出される
       */
      getSettingText: function getSettingText(hostname, board_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + '/SETTING.TXT');
        var http_request_headers = this.HTTP_REQ_HEADERS_DEFAULT;

        _(http_request_headers)
          .extend({
            'Host': hostname
          });

        this.http.get(url, http_request_headers)
          .done(function(http_response) {
            callback(this.parser.parseSettingText(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description 情報取得後 callback(Object)として呼び出される
     * @callback Client#getSettingText-callback
     * @param {Object} settings
     * 板の設定情報
     */

    proto_extend({
      /**
       * @description スレッドの書き込みを取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {String} thread_id
       * スレッドID
       * @param {Client#getResponsesFromThread-callback} callback
       * 書き込み一覧を取得後 callback(Array) として呼び出される
       */
      getResponsesFromThread: function getResponsesFromThread(hostname, board_id, thread_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + GetDatPath(hostname, thread_id));
        this.http.get(
          url,
          _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname
          }))
          .done(function(http_response) {
            callback(this.parser.parseResponsesFromThread(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description 書き込み一覧を取得後 callback(Array) として呼び出される
     * @callback Client#getResponsesFromThread-callback
     *
     * @param {Array} responses
     * スレッドに書き込まれたレスのリスト
     */

    proto_extend({
      // スレッドに書き込む
      /**
       * @description スレッドに書き込みを行う
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {String} thread_id
       * スレッドID
       * @param {Object} response
       * 書き込み内容
       * @param {Client#putResponseToThread-callback} callback
       * 書き込み完了後 callback(Object) として呼び出される
       */
      putResponseToThread: function putResponseToThread(hostname, board_id, thread_id, response, ok_callback, fail_callback) {
        var url = GetUrl(hostname, '/test/bbs.cgi?guid=ON');

        var http_req_headers = _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname,
            'Referer': GetUrl(hostname, '/' + board_id + '/')
          });
        var http_req_params = {};

        // 書き込みを行う
        var write = function write(ok_callback, fail_callback) {
          // 送信直前に必要な処理を行う（パラメータの追加など）
          var promise = $.when.apply(null, [
            add_http_req_params()
          ]);

          // 各処理が済んだらリクエストを送信
          promise.done(send_http_request);
        };
        write = write.bind(this);

        // 書き込み送信後にHTTPレスポンスヘッダを受け取る
        var receive_response = function receive_response(http_response) {

          // HTTPレスポンス受信後の処理（callbackの実行）
          var after_receive_response = function after_receive_response() {

            // 書き込み確認後の処理
            var confirm_callback = function confirm_callback_func() {
              var promise = new $.Deferred();

              // 不足しているパラメータを追加して保存し、再書き込みを行う
              this.storage.get(STORAGE_FORM_APPEND_PARAMS)
                .done(function(items) {
                  // ストレージに設定できたら再書き込みを行う
                  var after_storage_set = function after_storage_set_func() {
                    this.putResponseToThread(hostname, board_id, thread_id, response)
                      .done(promise.resolve)
                      .fail(promise.reject);
                  };
                  after_storage_set = after_storage_set.bind(this);

                  // 不足しているパラメータを取得する
                  var new_form_params = this.parser.parseFormFromHTML(ConvertToUTF8(http_response.body))['../test/bbs.cgi?guid=ON'].params;
                  _(_.keys(http_req_params))
                    .each(function(key) {
                      if (typeof new_form_params[key] === 'undefined')
                        delete new_form_params[key];
                      else if (key === 'FROM' || key === 'MESSAGE' || key === 'mail' || key === 'time')
                        delete new_form_params[key];
                      else if (http_req_params[key] === new_form_params[key])
                        delete new_form_params[key];
                      else
                        new_form_params[key] = ConvertToSJIS(new_form_params[key].toString());
                    });

                  // オブジェクトじゃなかったらオブジェクトにしておく
                  if (typeof items[STORAGE_FORM_APPEND_PARAMS] !== 'object')
                    items[STORAGE_FORM_APPEND_PARAMS] = {};

                  // ストレージに保存しておく
                  _(items[STORAGE_FORM_APPEND_PARAMS])
                    .extend(new_form_params);
                  this.storage.set(items)
                    .done(after_storage_set);
                });

              return promise;
            };
            confirm_callback = confirm_callback.bind(this);

            var title_text = this.parser.parseTitleFromHTML(ConvertToUTF8(http_response.body));

            if ('書きこみました。' === title_text) {
              ok_callback(ConvertToUTF8(http_response.body));
            }
            else if ('■ 書き込み確認 ■' === title_text) {
              fail_callback({
                type: 'confirm',
                httpResponse: http_response,
                confirm: confirm_callback
              });
            }
            else {
              fail_callback({
                type: 'error',
                httpResponse: http_response
              });
            }

          };
          after_receive_response = after_receive_response.bind(this);

          // HTTPレスポンスヘッダにSet-Cookieがある場合の処理
          var check_cookie = function check_cookie_func() {
            var deferred = new $.Deferred();
            if (typeof http_response.headers['Set-Cookie'] !== 'undefined') {
              this.cookie_manager.setCookieHeader(url, http_response.headers_source)
                .done(function() {
                  deferred.resolve();
                });
            }
            else {
              deferred.resolve();
            }
            return deferred;
          };
          check_cookie = check_cookie.bind(this);

          // リクエスト受信後のCookieなどの処理
          var promise = $.when.apply(null, [
            check_cookie()
          ]);
          promise.done(after_receive_response);
        };
        receive_response = receive_response.bind(this);

        // 準備ができたらPOSTリクエストを送信する
        var send_http_request = function send_http_request_func() {
          this.http.post(url, http_req_headers, http_req_params)
            .done(receive_response);
        };
        send_http_request = send_http_request.bind(this);

        // 追加のパラメータがあれば追加する（yuki=akariなどの対応）
        var add_http_req_params = function add_http_req_params_func() {
          var promise = this.storage.get(STORAGE_FORM_APPEND_PARAMS)
          promise.done(function(items) {
            _(http_req_params)
              .extend(items[STORAGE_FORM_APPEND_PARAMS]);
          });
          return promise;
        };
        add_http_req_params = add_http_req_params.bind(this);

        // リクエスト前に送信するクエリを準備する
        var prepare_http_req_params = function prepare_http_req_params() {
          var deferred = new $.Deferred();

          var func = function func() {
            // 書き込み内容などをSJISに変換する
            var converted_response = _.clone(response);
            _(converted_response)
              .each(function(value, key) {
                converted_response[key] = ConvertToSJIS(value);
              });

            // 書き込み内容などをパーセントエンコーディングでエスケープする
            var escaped_response = _.clone(converted_response)
            _(escaped_response)
              .each(function(value, key) {
                escaped_response[key] = EscapeSJIS(value);
              });

            // 送信するデータ
            _(http_req_params)
              .extend({
                'subject': '',
                'bbs': board_id,
                'key': thread_id,
                'time': 1,
                'submit': ConvertToSJIS('書き込む'),
                'FROM': escaped_response.name,
                'mail': escaped_response.mail,
                'MESSAGE': escaped_response.body,
              });

            deferred.resolve();
          };
          func = func.bind(this);

          setTimeout(func, 0);

          return deferred;
        };
        prepare_http_req_params = prepare_http_req_params.bind(this);

        // 取得したCookieをHTTPリクエストヘッダに追加する
        var after_get_cookie_header = function after_get_cookie_header_func(cookie_header) {
          if (cookie_header === 'Cookie: ')
            return;
          _(http_req_headers)
            .extend({
              'Cookie': cookie_header.substr(8)
            });
        };

        // リクエスト前に送信するCookieを準備する
        var prepare_cookie = function prepare_cookie_func() {
          var promise = this.cookie_manager.getCookieHeader(url);
          promise.done(after_get_cookie_header);
          return promise;
        };
        prepare_cookie = prepare_cookie.bind(this);

        // リクエスト前の準備
        var deferreds = $.when.apply(null, [
          prepare_cookie(),
          prepare_http_req_params()
        ]);

        // 準備ができたらwriteを実行する
        deferreds.done(function() {
          write(ok_callback, fail_callback);
        });
      }
    });

    /**
     * @description 書き込み完了後 callback(Object) として呼び出される
     * @callback Client#putResponseToThread-callback
     *
     * @param {Object} http_response
     * HTTPレスポンス
     */

    // ホスト名とパスを繋げたURLを返す

    function GetUrl(hostname, path) {
      return 'http://' + hostname + path;
    }

    // DATファイルのパスを返す

    function GetDatPath(hostname, thread_id) {
      return '/dat/' + thread_id + '.dat';
    }

    // 与えられた文字列をSJISに変換する

    function ConvertToSJIS(str) {
      return encoding.codeToString(encoding.convert(GetArray(str), 'SJIS', 'AUTO'));
    }

    // 与えられた文字列をUTF-8に変換する

    function ConvertToUTF8(str) {
      return encoding.codeToString(encoding.convert(GetArray(str), 'UNICODE', 'AUTO'));
    }

    // 与えられた文字列をSJISに変換する

    function EscapeSJIS(str) {
      return _(str)
        .map(function(c) {
          var code = c.charCodeAt()
            .toString(16)
            .toUpperCase();
          while (code.length < 2)
            code = "0" + code;
          return '%' + code;
        })
        .join('');
    }

    // 文字列を配列に変換する

    function GetArray(str) {
      var res = [];
      _(str.split(''))
        .each(function(c) {
          res.push(c.charCodeAt());
        });
      return res;
    }

    return Client;
  });

})();
<commit_msg>Edit sources/client.js: receive_response関数の内部にあった関数を外側へ移動<commit_after>/* ===================================================
 * JavaScript 2ch-client Library
 * https://github.com/sh19910711/js2ch
 * ===================================================
 * Copyright (c) 2013 Hiroyuki Sano
 *
 * Licensed under MIT License.
 * http://opensource.org/licenses/MIT
 * =================================================== */
/**
 * @fileOverview 2ch専用ブラウザ・クライアント用のライブラリ
 * @author Hiroyuki Sano
 */
(function() {
  'use strict';

  var root = this;

  define([
    'jquery',
    'underscore',
    'backbone',
    'http-lib',
    'storage',
    'cookie-manager',
    'parser',
    'encoding',
    'util-lib',
    'logger'
  ], function($, _, Backbone, HttpLib, Storage, CookieManager, Parser, encoding, UtilLib, Logger) {

    var STORAGE_FORM_APPEND_PARAMS = 'form_append_params';

    /**
     * @constructor Client
     */
    var Client = function(options, callback_context) {
      callback_context = callback_context || this;
      options = (options && options['client']) || options || {};

      /**
       * @description HTTPリクエスト時に渡すヘッダのリスト
       * @memberof Client
       *
       */
      this.HTTP_REQ_HEADERS_DEFAULT = {
        'User-Agent': 'Monazilla/1.00'
      };

      this.http = new HttpLib((options && options['http-lib']) || options, this);
      this.parser = new Parser((options && options['parser']) || options, this);
      this.logger = new Logger((options && options['logger']) || options, this);
      this.storage = new Storage((options && options['storage']) || options, this);
      this.cookie_manager = new CookieManager((options && options['cookie-manager']) || options || {}, this);

      // Deferredの設定
      var keys = ['getThreadList', 'getSettingText', 'getResponsesFromThread'];
      _(keys)
        .each(function(key) {
          this[key] = UtilLib.getDeferredFunc(this[key], this, callback_context);
        }, this);

      // Deferredの設定（失敗する可能性があるもの）
      var keys = ['putResponseToThread'];
      _(keys)
        .each(function(key) {
          this[key] = UtilLib.getDeferredFuncWillFail(this[key], this, callback_context);
        }, this);
    };

    Client.prototype = {};
    var proto_extend = function(obj) {
      _.extend(Client.prototype, obj);
    };

    proto_extend({
      /**
       * @description スレッド一覧を取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {Client#getThreadList-callback} callback
       * スレッド一覧取得後、callback(Array) として呼び出される。
       */
      getThreadList: function getThreadList(hostname, board_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + '/subject.txt');
        this.http.get(
          url,
          _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname,
          }))
          .done(function(http_response) {
            callback(this.parser.parseThreadList(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description スレッド一覧取得後、callback(Array)として呼び出される
     * @callback Client#getThreadList-callback
     * @param {Array} thread_list
     * スレッド情報の配列
     */

    proto_extend({
      /**
       * @description [未実装] SETTING.TXTを取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {Client#getSettingText-callback} callback
       * 情報取得後 callback(Object) として呼び出される
       */
      getSettingText: function getSettingText(hostname, board_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + '/SETTING.TXT');
        var http_request_headers = this.HTTP_REQ_HEADERS_DEFAULT;

        _(http_request_headers)
          .extend({
            'Host': hostname
          });

        this.http.get(url, http_request_headers)
          .done(function(http_response) {
            callback(this.parser.parseSettingText(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description 情報取得後 callback(Object)として呼び出される
     * @callback Client#getSettingText-callback
     * @param {Object} settings
     * 板の設定情報
     */

    proto_extend({
      /**
       * @description スレッドの書き込みを取得する
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {String} thread_id
       * スレッドID
       * @param {Client#getResponsesFromThread-callback} callback
       * 書き込み一覧を取得後 callback(Array) として呼び出される
       */
      getResponsesFromThread: function getResponsesFromThread(hostname, board_id, thread_id, callback) {
        var url = GetUrl(hostname, '/' + board_id + GetDatPath(hostname, thread_id));
        this.http.get(
          url,
          _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname
          }))
          .done(function(http_response) {
            callback(this.parser.parseResponsesFromThread(ConvertToUTF8(http_response.body)));
          });
      }
    });

    /**
     * @description 書き込み一覧を取得後 callback(Array) として呼び出される
     * @callback Client#getResponsesFromThread-callback
     *
     * @param {Array} responses
     * スレッドに書き込まれたレスのリスト
     */

    proto_extend({
      // スレッドに書き込む
      /**
       * @description スレッドに書き込みを行う
       * @memberof Client
       *
       * @param {String} hostname
       * ホスト名
       * @param {String} board_id
       * 板ID
       * @param {String} thread_id
       * スレッドID
       * @param {Object} response
       * 書き込み内容
       * @param {Client#putResponseToThread-callback} callback
       * 書き込み完了後 callback(Object) として呼び出される
       */
      putResponseToThread: function putResponseToThread(hostname, board_id, thread_id, response, ok_callback, fail_callback) {
        var url = GetUrl(hostname, '/test/bbs.cgi?guid=ON');

        var http_req_headers = _(this.HTTP_REQ_HEADERS_DEFAULT)
          .extend({
            'Host': hostname,
            'Referer': GetUrl(hostname, '/' + board_id + '/')
          });
        var http_req_params = {};

        // 書き込みを行う
        var write = function write(ok_callback, fail_callback) {
          // 送信直前に必要な処理を行う（パラメータの追加など）
          var promise = $.when.apply(null, [
            add_http_req_params()
          ]);

          // 各処理が済んだらリクエストを送信
          promise.done(send_http_request);
        };
        write = write.bind(this);

        // 書き込み送信後にHTTPレスポンスヘッダを受け取る
        var receive_response = function receive_response() {
          // HTTPレスポンスヘッダにSet-Cookieがある場合の処理
          var check_cookie = function check_cookie_func() {
            var deferred = new $.Deferred();
            if (typeof this.http_response.headers['Set-Cookie'] !== 'undefined') {
              this.cookie_manager.setCookieHeader(url, this.http_response.headers_source)
                .done(function() {
                  deferred.resolve();
                });
            }
            else {
              deferred.resolve();
            }
            return deferred;
          };
          check_cookie = check_cookie.bind(this);

          // リクエスト受信後のCookieなどの処理
          var promise = $.when.apply(null, [
            check_cookie()
          ]);
          promise.done(after_receive_response);
        };
        receive_response = receive_response.bind(this);

        // HTTPレスポンス受信後の処理（callbackの実行）
        var after_receive_response = function after_receive_response() {

          // 書き込み確認後の処理
          var confirm_callback = function confirm_callback_func() {
            var promise = new $.Deferred();

            // 不足しているパラメータを追加して保存し、再書き込みを行う
            this.storage.get(STORAGE_FORM_APPEND_PARAMS)
              .done(function(items) {
                // ストレージに設定できたら再書き込みを行う
                var after_storage_set = function after_storage_set_func() {
                  this.putResponseToThread(hostname, board_id, thread_id, response)
                    .done(promise.resolve)
                    .fail(promise.reject);
                };
                after_storage_set = after_storage_set.bind(this);

                // 不足しているパラメータを取得する
                var new_form_params = this.parser.parseFormFromHTML(ConvertToUTF8(this.http_response.body))['../test/bbs.cgi?guid=ON'].params;
                _(_.keys(http_req_params))
                  .each(function(key) {
                    if (typeof new_form_params[key] === 'undefined')
                      delete new_form_params[key];
                    else if (key === 'FROM' || key === 'MESSAGE' || key === 'mail' || key === 'time')
                      delete new_form_params[key];
                    else if (http_req_params[key] === new_form_params[key])
                      delete new_form_params[key];
                    else
                      new_form_params[key] = ConvertToSJIS(new_form_params[key].toString());
                  });

                // オブジェクトじゃなかったらオブジェクトにしておく
                if (typeof items[STORAGE_FORM_APPEND_PARAMS] !== 'object')
                  items[STORAGE_FORM_APPEND_PARAMS] = {};

                // ストレージに保存しておく
                _(items[STORAGE_FORM_APPEND_PARAMS])
                  .extend(new_form_params);
                this.storage.set(items)
                  .done(after_storage_set);
              });

            return promise;
          };
          confirm_callback = confirm_callback.bind(this);

          var title_text = this.parser.parseTitleFromHTML(ConvertToUTF8(this.http_response.body));

          if ('書きこみました。' === title_text) {
            ok_callback(ConvertToUTF8(this.http_response.body));
          }
          else if ('■ 書き込み確認 ■' === title_text) {
            fail_callback({
              type: 'confirm',
              httpResponse: this.http_response,
              confirm: confirm_callback
            });
          }
          else {
            fail_callback({
              type: 'error',
              httpResponse: this.http_response
            });
          }

        };
        after_receive_response = after_receive_response.bind(this);

        // 準備ができたらPOSTリクエストを送信する
        var send_http_request = function send_http_request_func() {
          this.http.post(url, http_req_headers, http_req_params)
            .done(function(http_response) {
              this.http_response = http_response;
              receive_response();
            });
        };
        send_http_request = send_http_request.bind(this);

        // 追加のパラメータがあれば追加する（yuki=akariなどの対応）
        var add_http_req_params = function add_http_req_params_func() {
          var promise = this.storage.get(STORAGE_FORM_APPEND_PARAMS)
          promise.done(function(items) {
            _(http_req_params)
              .extend(items[STORAGE_FORM_APPEND_PARAMS]);
          });
          return promise;
        };
        add_http_req_params = add_http_req_params.bind(this);

        // リクエスト前に送信するクエリを準備する
        var prepare_http_req_params = function prepare_http_req_params() {
          var deferred = new $.Deferred();

          var func = function func() {
            // 書き込み内容などをSJISに変換する
            var converted_response = _.clone(response);
            _(converted_response)
              .each(function(value, key) {
                converted_response[key] = ConvertToSJIS(value);
              });

            // 書き込み内容などをパーセントエンコーディングでエスケープする
            var escaped_response = _.clone(converted_response)
            _(escaped_response)
              .each(function(value, key) {
                escaped_response[key] = EscapeSJIS(value);
              });

            // 送信するデータ
            _(http_req_params)
              .extend({
                'subject': '',
                'bbs': board_id,
                'key': thread_id,
                'time': 1,
                'submit': ConvertToSJIS('書き込む'),
                'FROM': escaped_response.name,
                'mail': escaped_response.mail,
                'MESSAGE': escaped_response.body,
              });

            deferred.resolve();
          };
          func = func.bind(this);

          setTimeout(func, 0);

          return deferred;
        };
        prepare_http_req_params = prepare_http_req_params.bind(this);

        // 取得したCookieをHTTPリクエストヘッダに追加する
        var after_get_cookie_header = function after_get_cookie_header_func(cookie_header) {
          if (cookie_header === 'Cookie: ')
            return;
          _(http_req_headers)
            .extend({
              'Cookie': cookie_header.substr(8)
            });
        };

        // リクエスト前に送信するCookieを準備する
        var prepare_cookie = function prepare_cookie_func() {
          var promise = this.cookie_manager.getCookieHeader(url);
          promise.done(after_get_cookie_header);
          return promise;
        };
        prepare_cookie = prepare_cookie.bind(this);

        // リクエスト前の準備
        var deferreds = $.when.apply(null, [
          prepare_cookie(),
          prepare_http_req_params()
        ]);

        // 準備ができたらwriteを実行する
        deferreds.done(function() {
          write(ok_callback, fail_callback);
        });
      }
    });

    /**
     * @description 書き込み完了後 callback(Object) として呼び出される
     * @callback Client#putResponseToThread-callback
     *
     * @param {Object} http_response
     * HTTPレスポンス
     */

    // ホスト名とパスを繋げたURLを返す

    function GetUrl(hostname, path) {
      return 'http://' + hostname + path;
    }

    // DATファイルのパスを返す

    function GetDatPath(hostname, thread_id) {
      return '/dat/' + thread_id + '.dat';
    }

    // 与えられた文字列をSJISに変換する

    function ConvertToSJIS(str) {
      return encoding.codeToString(encoding.convert(GetArray(str), 'SJIS', 'AUTO'));
    }

    // 与えられた文字列をUTF-8に変換する

    function ConvertToUTF8(str) {
      return encoding.codeToString(encoding.convert(GetArray(str), 'UNICODE', 'AUTO'));
    }

    // 与えられた文字列をSJISに変換する

    function EscapeSJIS(str) {
      return _(str)
        .map(function(c) {
          var code = c.charCodeAt()
            .toString(16)
            .toUpperCase();
          while (code.length < 2)
            code = "0" + code;
          return '%' + code;
        })
        .join('');
    }

    // 文字列を配列に変換する

    function GetArray(str) {
      var res = [];
      _(str.split(''))
        .each(function(c) {
          res.push(c.charCodeAt());
        });
      return res;
    }

    return Client;
  });

})();
<|endoftext|>


<commit_before>require("./spec_helper.js");

describe("Jbob: nodes", function() {

  beforeEach(function(){
    _ = {}
    Jbob.apply(_, [{tags:["fooBar", "x", "y", "z", "J", "K", "iDontSelfClose"],
                    nonSelfClosingTags:["iDontSelfClose"]}])
  });

  describe("tag closing", function() {
    it("self-closes by default", function(){
      expect(_.fooBar()).
     toEqual("<fooBar/>\n");
    });

    it("doesn't self-close for non self closing tags", function(){
      expect(_.iDontSelfClose()).
     toEqual("<iDontSelfClose></iDontSelfClose>\n");
    });

    it("doesn't self-close if there are children.", function(){
      expect(_.fooBar(_.x())).
     toEqual("<fooBar>\n" +
             "<x/>\n" +
             "</fooBar>\n");
    });

    it("doesn't self-close if there are text nodes", function(){
      expect(_.fooBar("x")).
     toEqual("<fooBar>x</fooBar>\n");
    });

  });

  describe("attributes", function() {
    it("renders attributes as key value pairs separated by = in the tag", function(){
      expect(_.fooBar({a:"b"})).
     toEqual("<fooBar a=\"b\"/>\n");
    });

    it("renders several attributes in a row.  order is imposed", function(){
      expect(_.fooBar({x:"y", a:"b", c:"d"})).
     toEqual("<fooBar a=\"b\" c=\"d\" x=\"y\"/>\n");
    });

    it("renders booleans", function(){
      expect(_.fooBar({a:true})).toEqual("<fooBar a=\"true\"/>\n");
      expect(_.fooBar({a:false})).toEqual("<fooBar a=\"false\"/>\n");
    });

    it("renders integers", function(){
      expect(_.fooBar({a:0})).toEqual("<fooBar a=\"0\"/>\n");
      expect(_.fooBar({a:-1})).toEqual("<fooBar a=\"-1\"/>\n");
      expect(_.fooBar({a:7})).toEqual("<fooBar a=\"7\"/>\n");
    });

    it("renders floats", function(){
      expect(_.fooBar({a:0.0})).toEqual("<fooBar a=\"0\"/>\n");

      expect(_.fooBar({a:0.01})).toEqual("<fooBar a=\"0.01\"/>\n");
      expect(_.fooBar({a:0.0100})).toEqual("<fooBar a=\"0.01\"/>\n");

      expect(_.fooBar({a:23.45})).toEqual("<fooBar a=\"23.45\"/>\n");
      expect(_.fooBar({a:-23.45})).toEqual("<fooBar a=\"-23.45\"/>\n");
    });

    it("does the javascript thing on really big and really small numbers.  just calling this out.", function(){
      expect(_.fooBar({a:70000000000000000000000})).toEqual("<fooBar a=\"7e+22\"/>\n");
      expect(_.fooBar({a:70000000000000000000000.01})).toEqual("<fooBar a=\"7e+22\"/>\n");

      expect(_.fooBar({a:0.00000000000000000000007})).toEqual("<fooBar a=\"7e-23\"/>\n");
    });

    //todo: throw if the attribute value is a non-primitive?
  });

  describe("element children", function() {
    it("renders children as inner tags", function(){
      expect(_.fooBar(_.x())).
     toEqual("<fooBar>\n" +
               "<x/>\n" +
             "</fooBar>\n");
    });

    it("renders multiple children in order", function(){
      expect(_.fooBar(_.x(), _.y(), _.z())).
     toEqual("<fooBar>\n" +
               "<x/>\n" +
               "<y/>\n" +
               "<z/>\n" +
             "</fooBar>\n");
    });

    it("renders children of children", function(){
      expect(_.fooBar(
               _.x(_.J()),
               _.y(_.K())
             )).
     toEqual("<fooBar>\n" +
               "<x>\n" +
                 "<J/>\n" +
               "</x>\n" +
               "<y>\n" +
                 "<K/>\n" +
               "</y>\n" +
             "</fooBar>\n");
    });
  });
  //
  // describe("array children", function() {
  //   it("renders an array of nodes as a series of child nodes.  " +
  //      "this is often useful when you want to $.map or _.map an array of objects into the equivalent array of nodes.", function(){
  //     expect(fooBar.addChild([new Jaml.Node("x"), new Jaml.Node("y")]).
  //                   addChild(new Jaml.Node("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x/>\n" +
  //            "  <y/>\n" +
  //            "  <z/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  //   it("works with mixed node and textnodes", function(){
  //     expect(fooBar.addChild([new Jaml.Node("x"), new Jaml.TextNode("y")]).
  //                   addChild(new Jaml.TextNode("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x/>\n" +
  //            "yz</fooBar>\n");
  //   });
  //
  //   it("renders arrays of arrays too", function(){
  //      expect(fooBar.addChild([[new Jaml.Node("x"), [new Jaml.Node("y")]]]).
  //                    addChild(new Jaml.Node("z")).render()).
  //     toEqual("<fooBar>\n" +
  //             "  <x/>\n" +
  //             "  <y/>\n" +
  //             "  <z/>\n" +
  //             "</fooBar>\n");
  //   });
  //
  //   it("renders children of children in arrays properly", function(){
  //     expect(fooBar.addChild([[new Jaml.Node("x").addChild(new Jaml.Node("J")),
  //                             [new Jaml.Node("y").addChild(new Jaml.Node("K"))]]]).
  //                   addChild(new Jaml.Node("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x>\n" +
  //            "    <J/>\n" +
  //            "  </x>\n" +
  //            "  <y>\n" +
  //            "    <K/>\n" +
  //            "  </y>\n" +
  //            "  <z/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  //   it("renders array children, plus attributes, properly (bug)", function(){
  //     expect(fooBar.setAttributes({x:"y"}).
  //                   addChild([new Jaml.Node("x"), new Jaml.Node("y")]).render()).
  //    toEqual("<fooBar x=\"y\">\n" +
  //            "  <x/>\n" +
  //            "  <y/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  // })
  //
  // describe("textnode children", function() {
  //   it("renders", function(){
  //     expect(new Jaml.TextNode("x").render()).
  //    toEqual("x");
  //   });
  //
  //   it("renders a single textnode child all on one line", function(){
  //     expect(new Jaml.Node("fooBar").addChild(new Jaml.TextNode("x")).render()).
  //    toEqual("<fooBar>x</fooBar>\n");
  //   });
  //
  //   it("renders text nodes in one line, on a new line if there are multiple textnode children", function(){
  //     expect(fooBar.addChild(new Jaml.TextNode("x")).
  //                   addChild(new Jaml.TextNode("y")).render()).
  //    toEqual("<fooBar>\nxy</fooBar>\n");
  //   });
  // });
});

<commit_msg>text nodes<commit_after>require("./spec_helper.js");

describe("Jbob: nodes", function() {

  beforeEach(function(){
    _ = {}
    Jbob.apply(_, [{tags:["fooBar", "x", "y", "z", "J", "K", "iDontSelfClose"],
                    nonSelfClosingTags:["iDontSelfClose"]}])
  });

  describe("tag closing", function() {
    it("self-closes by default", function(){
      expect(_.fooBar()).
     toEqual("<fooBar/>\n");
    });

    it("doesn't self-close for non self closing tags", function(){
      expect(_.iDontSelfClose()).
     toEqual("<iDontSelfClose></iDontSelfClose>\n");
    });

    it("doesn't self-close if there are children.", function(){
      expect(_.fooBar(_.x())).
     toEqual("<fooBar>\n" +
             "<x/>\n" +
             "</fooBar>\n");
    });

    it("doesn't self-close if there are text nodes", function(){
      expect(_.fooBar("x")).
     toEqual("<fooBar>x</fooBar>\n");
    });

  });

  describe("attributes", function() {
    it("renders attributes as key value pairs separated by = in the tag", function(){
      expect(_.fooBar({a:"b"})).
     toEqual("<fooBar a=\"b\"/>\n");
    });

    it("renders several attributes in a row.  order is imposed", function(){
      expect(_.fooBar({x:"y", a:"b", c:"d"})).
     toEqual("<fooBar a=\"b\" c=\"d\" x=\"y\"/>\n");
    });

    it("renders booleans", function(){
      expect(_.fooBar({a:true})).toEqual("<fooBar a=\"true\"/>\n");
      expect(_.fooBar({a:false})).toEqual("<fooBar a=\"false\"/>\n");
    });

    it("renders integers", function(){
      expect(_.fooBar({a:0})).toEqual("<fooBar a=\"0\"/>\n");
      expect(_.fooBar({a:-1})).toEqual("<fooBar a=\"-1\"/>\n");
      expect(_.fooBar({a:7})).toEqual("<fooBar a=\"7\"/>\n");
    });

    it("renders floats", function(){
      expect(_.fooBar({a:0.0})).toEqual("<fooBar a=\"0\"/>\n");

      expect(_.fooBar({a:0.01})).toEqual("<fooBar a=\"0.01\"/>\n");
      expect(_.fooBar({a:0.0100})).toEqual("<fooBar a=\"0.01\"/>\n");

      expect(_.fooBar({a:23.45})).toEqual("<fooBar a=\"23.45\"/>\n");
      expect(_.fooBar({a:-23.45})).toEqual("<fooBar a=\"-23.45\"/>\n");
    });

    it("does the javascript thing on really big and really small numbers.  just calling this out.", function(){
      expect(_.fooBar({a:70000000000000000000000})).toEqual("<fooBar a=\"7e+22\"/>\n");
      expect(_.fooBar({a:70000000000000000000000.01})).toEqual("<fooBar a=\"7e+22\"/>\n");

      expect(_.fooBar({a:0.00000000000000000000007})).toEqual("<fooBar a=\"7e-23\"/>\n");
    });

    //todo: throw if the attribute value is a non-primitive?
  });

  describe("element children", function() {
    it("renders children as inner tags", function(){
      expect(_.fooBar(_.x())).
     toEqual("<fooBar>\n" +
               "<x/>\n" +
             "</fooBar>\n");
    });

    it("renders multiple children in order", function(){
      expect(_.fooBar(_.x(), _.y(), _.z())).
     toEqual("<fooBar>\n" +
               "<x/>\n" +
               "<y/>\n" +
               "<z/>\n" +
             "</fooBar>\n");
    });

    it("renders children of children", function(){
      expect(_.fooBar(
               _.x(_.J()),
               _.y(_.K())
             )).
     toEqual("<fooBar>\n" +
               "<x>\n" +
                 "<J/>\n" +
               "</x>\n" +
               "<y>\n" +
                 "<K/>\n" +
               "</y>\n" +
             "</fooBar>\n");
    });
  });

  // describe("array children", function() {
  //   it("renders an array of nodes as a series of child nodes.  " +
  //      "this is often useful when you want to $.map or _.map an array of objects into the equivalent array of nodes.", function(){
  //     expect(fooBar.addChild([new Jaml.Node("x"), new Jaml.Node("y")]).
  //                   addChild(new Jaml.Node("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x/>\n" +
  //            "  <y/>\n" +
  //            "  <z/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  //   it("works with mixed node and textnodes", function(){
  //     expect(fooBar.addChild([new Jaml.Node("x"), new Jaml.TextNode("y")]).
  //                   addChild(new Jaml.TextNode("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x/>\n" +
  //            "yz</fooBar>\n");
  //   });
  //
  //   it("renders arrays of arrays too", function(){
  //      expect(fooBar.addChild([[new Jaml.Node("x"), [new Jaml.Node("y")]]]).
  //                    addChild(new Jaml.Node("z")).render()).
  //     toEqual("<fooBar>\n" +
  //             "  <x/>\n" +
  //             "  <y/>\n" +
  //             "  <z/>\n" +
  //             "</fooBar>\n");
  //   });
  //
  //   it("renders children of children in arrays properly", function(){
  //     expect(fooBar.addChild([[new Jaml.Node("x").addChild(new Jaml.Node("J")),
  //                             [new Jaml.Node("y").addChild(new Jaml.Node("K"))]]]).
  //                   addChild(new Jaml.Node("z")).render()).
  //    toEqual("<fooBar>\n" +
  //            "  <x>\n" +
  //            "    <J/>\n" +
  //            "  </x>\n" +
  //            "  <y>\n" +
  //            "    <K/>\n" +
  //            "  </y>\n" +
  //            "  <z/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  //   it("renders array children, plus attributes, properly (bug)", function(){
  //     expect(fooBar.setAttributes({x:"y"}).
  //                   addChild([new Jaml.Node("x"), new Jaml.Node("y")]).render()).
  //    toEqual("<fooBar x=\"y\">\n" +
  //            "  <x/>\n" +
  //            "  <y/>\n" +
  //            "</fooBar>\n");
  //   });
  //
  // })
  //
  describe("textnode children", function() {
    it("renders a single textnode child all on one line", function(){
      expect(_.fooBar("x")).
     toEqual("<fooBar>x</fooBar>\n");
    });

    it("renders text nodes in one line, on a new line if there are multiple textnode children", function(){
      expect(_.fooBar("x", "y")).
     toEqual("<fooBar>xy</fooBar>\n");
    });

    it("renders mixed text and element nodes - elements have newlines and text nodes do not", function(){
      expect(_.fooBar("x", _.J(), "y", _.K())).
     toEqual("<fooBar>x<J/>\ny<K/>\n</fooBar>\n");
    });
  });
});

<|endoftext|>


<commit_before>var http = require('http')
  , querystring = require('querystring')
  , util = require('../lib/util')
  , that, httpServer;

that = {
  startWebServer: function (logger, callback) {
    httpServer = http.createServer(function (req, res) {
      var reqBody = '';
      logger.trace('incoming request : %s', req.url);
      if (req.url === '/form.html' && req.method === 'POST') {
        req.on('data', function (data) {
          reqBody += data;
        });
        req.on('end', function () {
          var params = querystring.parse(reqBody);
          res.write('<title>Form submitted</title>' +
            '
Done
' +
            util.format('
%s
', params.field) +
            '');
          res.end();
        });
      } else if (req.url === '/hello.html') {
        res.write('<title>Hey</title>' +
          '
Hello

this is a test
' +
          '<form action="/form.html" method="post">' +
          '<input type="text" name="field">' +
          '<input type="submit" value="ok">' +
          '</form>' +
          '');
        res.end();
      }
    });
    httpServer.listen(3000, function () {
      logger.info('web server started.');
      callback();
    });
  },
  stopWebServer: function (logger, callback) {
    httpServer.close(function () {
      logger.info('web server stopped.');
      callback();
    });
  }
};

module.exports = that;<commit_msg>assertTimeLe<commit_after>var http = require('http')
  , querystring = require('querystring')
  , util = require('../lib/util')
  , that, httpServer;

that = {
  startWebServer: function (logger, callback) {
    httpServer = http.createServer(function (req, res) {
      var reqBody = '';
      logger.trace('incoming request : %s', req.url);
      if (req.url === '/form.html' && req.method === 'POST') {
        req.on('data', function (data) {
          reqBody += data;
        });
        req.on('end', function () {
          var params = querystring.parse(reqBody);
          res.write('<title>Form submitted</title>' +
            '
Done
' +
            util.format('
%s
', params.field) +
            '');
          res.end();
        });
      } else if (req.url === '/hello.html') {
        res.write('<title>Hey</title>' +
          '
Hello

this is a test
' +
          '<form action="/form.html" method="post">' +
          '<input type="text" name="field">' +
          '<input type="submit" value="ok">' +
          '</form>' +
          '');
        res.end();
      }
    });
    httpServer.listen(3000, function (err) {
      if (err) {
        return callback(err);
      }
      logger.info('web server started.');
      callback();
    });
  },
  stopWebServer: function (logger, callback) {
    httpServer.close(function () {
      logger.info('web server stopped.');
      callback();
    });
  }
};

module.exports = that;<|endoftext|>


<commit_before>import React, { Component } from 'react';
import addons from '@kadira/storybook-addons';
import CommentsPanel from '../../components/CommentsPanel/';
import deepEquals from 'deep-equal';
import DataStore from './dataStore';

export default class Container extends Component {
  constructor(props, ...args) {
    super(props, ...args);
    this.state = {
      user: null,
      users: [],
      comments: [],
      loading: true,
    };
  }

  componentDidMount() {
    const db = addons.getDatabase();
    this.store = new DataStore(db);
    this.stopListeningToComments = this.store.onComments((comments) => {
      this.setState({ comments });
    });

    // Clear the current notes on every story change.
    this.stopListeningOnStory = this.props.api.onStory((kind, story) => {
      // set the current selection
      this.store.setCurrentStory(kind, story);
    });

    this.getCurrentUser();
  }

  componentWillUnmount() {
    this.stopListeningToComments();
    this.stopListeningOnStory();
  }

  getCurrentUser() {
    const db = addons.getDatabase();

    if (typeof db.persister.getUser !== 'function') {
      throw new Error('unable to get user info');
    }

    this.setState({ loading: true });
    db.persister.getUser()
      .then(user => {
        this.store.setCurrentUser(user);
        this.setState({ user, loading: false });
      });
  }

  addComment(text) {
    const time = Date.now();
    const { user } = this.state;

    const comment = {
      text,
      time,
      userId: user.id
    };

    this.store.addComment(comment);
  }

  render() {
    const props = {
      user: this.state.user,
      comments: this.state.comments,
      loading: this.state.loading,
      addComment: text => this.addComment(text),
    };

    return <CommentsPanel {...props} />;
  }
}
<commit_msg>Rename getUsers to _getUsers<commit_after>import React, { Component } from 'react';
import addons from '@kadira/storybook-addons';
import CommentsPanel from '../../components/CommentsPanel/';
import deepEquals from 'deep-equal';
import DataStore from './dataStore';

export default class Container extends Component {
  constructor(props, ...args) {
    super(props, ...args);
    this.state = {
      user: null,
      users: [],
      comments: [],
      loading: true,
    };
  }

  componentDidMount() {
    const db = addons.getDatabase();
    this.store = new DataStore(db);
    this.stopListeningToComments = this.store.onComments((comments) => {
      this.setState({ comments });
    });

    // Clear the current notes on every story change.
    this.stopListeningOnStory = this.props.api.onStory((kind, story) => {
      // set the current selection
      this.store.setCurrentStory(kind, story);
    });

    this.getCurrentUser();
  }

  componentWillUnmount() {
    this.stopListeningToComments();
    this.stopListeningOnStory();
  }

  getCurrentUser() {
    const db = addons.getDatabase();

    if (typeof db.persister._getUser !== 'function') {
      throw new Error('unable to get user info');
    }

    this.setState({ loading: true });
    db.persister._getUser()
      .then(user => {
        this.store.setCurrentUser(user);
        this.setState({ user, loading: false });
      });
  }

  addComment(text) {
    const time = Date.now();
    const { user } = this.state;

    const comment = {
      text,
      time,
      userId: user.id
    };

    this.store.addComment(comment);
  }

  render() {
    const props = {
      user: this.state.user,
      comments: this.state.comments,
      loading: this.state.loading,
      addComment: text => this.addComment(text),
    };

    return <CommentsPanel {...props} />;
  }
}
<|endoftext|>


<commit_before>// @flow

import { describe, it } from 'kocha'
import assert from 'power-assert'
import { filter } from '../src/index.js'
import type {QueryCondition} from '../../mongolike-operations/decls/query-condition';

describe('filter', () => {
  it('$regex operation can be passed string', () => {
    const objs = ['John', 'Mark', 'Mary'].map(name => ({ name }))
    const filtered = filter(objs, { name: { $regex: '[jy]', $options: 'i' } })
    assert.deepEqual(filtered, [
      { name: 'John' },
      { name: 'Mary' },
    ])
  })

  describe('items', () => {
    const data = [
      { item: 'journal', tag: 'red', dim_cm: 14 },
      { item: 'notebook', tag: 'red', dim_cm: 21 },
      { item: 'paper', tag: 'plain', dim_cm:  14 },
      { item: 'planner', tag: 'red', dim_cm: 30 },
      { item: 'postcard', tag: 'blue', dim_cm: 10 }
    ]

    describe('$eq', () => {
      it('find equal items', () => {
        const query: QueryCondition = {tag: {$eq: 'red'}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'planner'])
      })
    })

    describe('$ne', () => {
      it('find not equal items', () => {
        const query: QueryCondition = {tag: {$ne: 'red'}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['paper', 'postcard'])
      })
    })

    describe('$gt', () => {
      it('find items greater than condition', () => {
        const query: QueryCondition = {dim_cm: {$gt: 21}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner'])
      })
    })

    describe('$gte', () => {
      it('find items greater than or equal condition', () => {
        const query: QueryCondition = {dim_cm: {$gte: 21}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'planner'])
      })
    })

    describe('$lt', () => {
      it('find items less than condition', () => {
        const query: QueryCondition = {dim_cm: {$lt: 14}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$lte', () => {
      it('find items less than or equal condition', () => {
        const query: QueryCondition = {dim_cm: {$lte: 14}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'paper', 'postcard'])
      })
    })

    describe('$in', () => {
      it('find items in condition', () => {
        const query: QueryCondition = {dim_cm: {$in: [10, 30]}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner', 'postcard'])
      })
    })

    describe('$nin', () => {
      it('find items not in condition', () => {
        const query: QueryCondition = {dim_cm: {$nin: [10, 30]}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper'])
      })
    })
  })

  describe('an Array for an Element', () => {

    const data = [
      { item: 'journal', tags: ['blank', 'red'], dim_cm: [ 14, 21 ] },
      { item: 'notebook', tags: ['red', 'blank'], dim_cm: [ 14, 21 ] },
      { item: 'paper', tags: ['red', 'blank', 'plain'], dim_cm: [ 14, 21 ] },
      { item: 'planner', tags: ['blank', 'red'], dim_cm: [ 22.85, 30 ] },
      { item: 'postcard', tags: ['blue'], dim_cm: [ 10, 15.25 ] }
    ]

    describe('$eq', () => {
      it('find same array', () => {
        const query: QueryCondition = { tags: { $eq: ['red', 'blank'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook'])
      })

      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { tags: { $eq: 'red' }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })
    })

    describe('$ne', () => {
      it('find if the array field does not contain any element', () => {
        const query: QueryCondition = { tags: { $ne: 'red' }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$gt', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $gt: 21 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner'])
      })
    })

    describe('$gte', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $gte: 21 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })
    })

    describe('$lt', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $lt: 14 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$lte', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $lte: 14 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'postcard'])
      })
    })

    describe('$in', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { tags: { $in: ['red'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })

      it('find array by arry in array', () => {
        const query: QueryCondition = { tags: { $in: [['blank', 'red']] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'planner'])
      })

      it('find array by arry and element', () => {
        const query: QueryCondition = { tags: { $in: [['blank', 'red'], 'blue'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'planner', 'postcard'])
      })

      it('find nothing if $in is []', () => {
        const query: QueryCondition = { tags: { $in: [] }}
        const filtered = filter(data, query)
        assert(filtered.length === 0)
      })
    })

    describe('$nin', () => {
      it('find if the array field does not contain any element', () => {
        const query: QueryCondition = { tags: { $nin: ['red'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })

      it('find array by arry in array', () => {
        const query: QueryCondition = { tags: { $nin: [['blank', 'red']] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'paper', 'postcard'])
      })

      it('find array by arry and element', () => {
        const query: QueryCondition = { tags: { $nin: [['blank', 'red'], 'blue'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'paper'])
      })

      it('find all if $nin is []', () => {
        const query: QueryCondition = { tags: { $nin: [] } }
        const filtered = filter(data, query)
        assert(filtered.length === data.length)
      })
    })
  })
})
<commit_msg>:rotating_light: fix lint<commit_after>// @flow

import { describe, it } from 'kocha'
import assert from 'power-assert'
import { filter } from '../src/index.js'
import type { QueryCondition } from 'mongolike-operations'

describe('filter', () => {
  it('$regex operation can be passed string', () => {
    const objs = ['John', 'Mark', 'Mary'].map(name => ({ name }))
    const filtered = filter(objs, { name: { $regex: '[jy]', $options: 'i' } })
    assert.deepEqual(filtered, [
      { name: 'John' },
      { name: 'Mary' },
    ])
  })

  describe('items', () => {
    const data = [
      { item: 'journal', tag: 'red', dim_cm: 14 },
      { item: 'notebook', tag: 'red', dim_cm: 21 },
      { item: 'paper', tag: 'plain', dim_cm:  14 },
      { item: 'planner', tag: 'red', dim_cm: 30 },
      { item: 'postcard', tag: 'blue', dim_cm: 10 }
    ]

    describe('$eq', () => {
      it('find equal items', () => {
        const query: QueryCondition = {tag: {$eq: 'red'}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'planner'])
      })
    })

    describe('$ne', () => {
      it('find not equal items', () => {
        const query: QueryCondition = {tag: {$ne: 'red'}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['paper', 'postcard'])
      })
    })

    describe('$gt', () => {
      it('find items greater than condition', () => {
        const query: QueryCondition = {dim_cm: {$gt: 21}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner'])
      })
    })

    describe('$gte', () => {
      it('find items greater than or equal condition', () => {
        const query: QueryCondition = {dim_cm: {$gte: 21}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'planner'])
      })
    })

    describe('$lt', () => {
      it('find items less than condition', () => {
        const query: QueryCondition = {dim_cm: {$lt: 14}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$lte', () => {
      it('find items less than or equal condition', () => {
        const query: QueryCondition = {dim_cm: {$lte: 14}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'paper', 'postcard'])
      })
    })

    describe('$in', () => {
      it('find items in condition', () => {
        const query: QueryCondition = {dim_cm: {$in: [10, 30]}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner', 'postcard'])
      })
    })

    describe('$nin', () => {
      it('find items not in condition', () => {
        const query: QueryCondition = {dim_cm: {$nin: [10, 30]}}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper'])
      })
    })
  })

  describe('an Array for an Element', () => {

    const data = [
      { item: 'journal', tags: ['blank', 'red'], dim_cm: [ 14, 21 ] },
      { item: 'notebook', tags: ['red', 'blank'], dim_cm: [ 14, 21 ] },
      { item: 'paper', tags: ['red', 'blank', 'plain'], dim_cm: [ 14, 21 ] },
      { item: 'planner', tags: ['blank', 'red'], dim_cm: [ 22.85, 30 ] },
      { item: 'postcard', tags: ['blue'], dim_cm: [ 10, 15.25 ] }
    ]

    describe('$eq', () => {
      it('find same array', () => {
        const query: QueryCondition = { tags: { $eq: ['red', 'blank'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook'])
      })

      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { tags: { $eq: 'red' }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })
    })

    describe('$ne', () => {
      it('find if the array field does not contain any element', () => {
        const query: QueryCondition = { tags: { $ne: 'red' }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$gt', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $gt: 21 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['planner'])
      })
    })

    describe('$gte', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $gte: 21 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })
    })

    describe('$lt', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $lt: 14 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })
    })

    describe('$lte', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { dim_cm: { $lte: 14 }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'postcard'])
      })
    })

    describe('$in', () => {
      it('find if the array field contains at least one element', () => {
        const query: QueryCondition = { tags: { $in: ['red'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'notebook', 'paper', 'planner'])
      })

      it('find array by arry in array', () => {
        const query: QueryCondition = { tags: { $in: [['blank', 'red']] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'planner'])
      })

      it('find array by arry and element', () => {
        const query: QueryCondition = { tags: { $in: [['blank', 'red'], 'blue'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['journal', 'planner', 'postcard'])
      })

      it('find nothing if $in is []', () => {
        const query: QueryCondition = { tags: { $in: [] }}
        const filtered = filter(data, query)
        assert(filtered.length === 0)
      })
    })

    describe('$nin', () => {
      it('find if the array field does not contain any element', () => {
        const query: QueryCondition = { tags: { $nin: ['red'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['postcard'])
      })

      it('find array by arry in array', () => {
        const query: QueryCondition = { tags: { $nin: [['blank', 'red']] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'paper', 'postcard'])
      })

      it('find array by arry and element', () => {
        const query: QueryCondition = { tags: { $nin: [['blank', 'red'], 'blue'] }}
        const filtered = filter(data, query)
        const items = filtered.map(f => f.item)
        assert.deepEqual(items, ['notebook', 'paper'])
      })

      it('find all if $nin is []', () => {
        const query: QueryCondition = { tags: { $nin: [] } }
        const filtered = filter(data, query)
        assert(filtered.length === data.length)
      })
    })
  })
})
<|endoftext|>


<commit_before>const ee = require('ee')
const {EMPTY, interval, of, throwError} = require('rxjs')
const {distinctUntilChanged, exhaustMap, first, finalize, map, switchMap, mapTo, takeWhile} = require('rxjs/operators')
const log = require('sepal/log').getLogger('task')
const progress = require('root/progress')

const MONITORING_FREQUENCY = 1000
const {UNSUBMITTED, READY, RUNNING, FAILED} = ee.data.ExportState

const executeTask$ = task =>
    start$(task).pipe(
        switchMap(() => monitor$(task)),
        finalize(() => task.id && cleanup(task))
    )

const start$ = task =>
    ee.$('start task', (resolve, reject) =>
        ee.data.startProcessing(null, task.config_, ({taskId}, error) => {
            if (error) {
                reject(error)
            } else {
                task.id = taskId // [TODO] Solve this without mutation
                resolve()
            }
        })
    )

const monitor$ = task =>
    interval(MONITORING_FREQUENCY).pipe(
        exhaustMap(() => status$(task)),
        switchMap(({state, error_message: error}) => error
            ? throwError(new Error(error))
            : of(state)
        ),
        distinctUntilChanged(),
        takeWhile(status => isRunning(status)),
        switchMap(status => progress$(status))
    )

const status$ = task =>
    ee.$('task status', (resolve, reject) =>
        ee.data.getTaskStatus(task.id,
            (status, error) =>
                error ? reject(error) : resolve(status)
        )
    ).pipe(
        map(([status]) => status)
    )

const cleanup = task => {
    status$(task).pipe(
        map(({state}) => isRunning(state)),
        switchMap(running => running
            ? cancel$(task).pipe(
                mapTo(true)
            )
            : of(false)
        ),
        first()
    ).subscribe({
        next: wasRunning => log.debug(`Task ${task.id}: ${wasRunning ? 'cancelled' : 'complete'}`),
        error: error => log.error('Failed to cancel task', error)
    })
}

const cancel$ = task =>
    ee.$('cancel task', (resolve, reject) => {
        ee.data.cancelTask(task.id,
            (canceled_, error) => error ? reject(error) : resolve())
    })

const isRunning = state => [UNSUBMITTED, READY, RUNNING].includes(state)

const progress$ = state => {
    switch (state) {
    case UNSUBMITTED:
        return of(progress({
            defaultMessage: 'Submitting export task to Google Earth Engine...',
            messageKey: 'tasks.ee.export.pending'
        }))
    case READY:
        return of(progress({
            defaultMessage: 'Waiting for Google Earth Engine to start export...',
            messageKey: 'tasks.ee.export.ready'
        }))
    case RUNNING:
        return of(progress({
            defaultMessage: 'Google Earth Engine is exporting...',
            messageKey: 'tasks.ee.export.running'
        }))
    case FAILED:
        return throwError(new Error(''))
    default:
        return EMPTY
    }
}

module.exports = {executeTask$}
<commit_msg>Slowed down EE task polling.<commit_after>const ee = require('ee')
const {EMPTY, interval, of, throwError} = require('rxjs')
const {distinctUntilChanged, exhaustMap, first, finalize, map, switchMap, mapTo, takeWhile} = require('rxjs/operators')
const log = require('sepal/log').getLogger('task')
const progress = require('root/progress')

const MONITORING_FREQUENCY = 10000
const {UNSUBMITTED, READY, RUNNING, FAILED} = ee.data.ExportState

const executeTask$ = task =>
    start$(task).pipe(
        switchMap(() => monitor$(task)),
        finalize(() => task.id && cleanup(task))
    )

const start$ = task =>
    ee.$('start task', (resolve, reject) =>
        ee.data.startProcessing(null, task.config_, ({taskId}, error) => {
            if (error) {
                reject(error)
            } else {
                task.id = taskId // [TODO] Solve this without mutation
                resolve()
            }
        })
    )

const monitor$ = task =>
    interval(MONITORING_FREQUENCY).pipe(
        exhaustMap(() => status$(task)),
        switchMap(({state, error_message: error}) => error
            ? throwError(new Error(error))
            : of(state)
        ),
        distinctUntilChanged(),
        takeWhile(status => isRunning(status)),
        switchMap(status => progress$(status))
    )

const status$ = task =>
    ee.$('task status', (resolve, reject) =>
        ee.data.getTaskStatus(task.id,
            (status, error) =>
                error ? reject(error) : resolve(status)
        )
    ).pipe(
        map(([status]) => status)
    )

const cleanup = task => {
    status$(task).pipe(
        map(({state}) => isRunning(state)),
        switchMap(running => running
            ? cancel$(task).pipe(
                mapTo(true)
            )
            : of(false)
        ),
        first()
    ).subscribe({
        next: wasRunning => log.debug(`Task ${task.id}: ${wasRunning ? 'cancelled' : 'complete'}`),
        error: error => log.error('Failed to cancel task', error)
    })
}

const cancel$ = task =>
    ee.$('cancel task', (resolve, reject) => {
        ee.data.cancelTask(task.id,
            (canceled_, error) => error ? reject(error) : resolve())
    })

const isRunning = state => [UNSUBMITTED, READY, RUNNING].includes(state)

const progress$ = state => {
    switch (state) {
    case UNSUBMITTED:
        return of(progress({
            defaultMessage: 'Submitting export task to Google Earth Engine...',
            messageKey: 'tasks.ee.export.pending'
        }))
    case READY:
        return of(progress({
            defaultMessage: 'Waiting for Google Earth Engine to start export...',
            messageKey: 'tasks.ee.export.ready'
        }))
    case RUNNING:
        return of(progress({
            defaultMessage: 'Google Earth Engine is exporting...',
            messageKey: 'tasks.ee.export.running'
        }))
    case FAILED:
        return throwError(new Error(''))
    default:
        return EMPTY
    }
}

module.exports = {executeTask$}
<|endoftext|>


<commit_before>const webpack = require('webpack')
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  entry: {
    adhocracy4: [
      'shariff/dist/shariff.min.css',
      'select2/dist/css/select2.min.css',
      'slick-carousel/slick/slick.css',
      './meinberlin/assets/extra_css/_slick-theme.css',
      './meinberlin/assets/scss/style.scss',
      './meinberlin/assets/js/app.js'
    ],
    vendor: [
      'classnames',
      '@fortawesome/fontawesome-free/scss/fontawesome.scss',
      '@fortawesome/fontawesome-free/scss/brands.scss',
      '@fortawesome/fontawesome-free/scss/regular.scss',
      '@fortawesome/fontawesome-free/scss/solid.scss',
      'js-cookie',
      'react',
      'immutability-helper',
      'react-dom',
      'react-flip-move',
      'react-sticky-box'
    ],
    captcheck: [
      './meinberlin/apps/captcha/assets/captcheck.js'
    ],
    datepicker: [
      './meinberlin/assets/js/init-picker.js',
      'datepicker/css/datepicker.min.css'
    ],
    embed: [
      'bootstrap/js/dist/modal.js',
      './meinberlin/apps/embed/assets/embed.js'
    ],
    unload_warning: [
      './meinberlin/assets/js/unload_warning.js'
    ],
    dsgvo_video_embed: [
      'dsgvo-video-embed/dist/dsgvo-video-embed.min.css',
      'dsgvo-video-embed/dist/dsgvo-video-embed.min.js'
    ],
    // A4 dependencies - we want all of them to go through webpack
    mb_plans_map: [
      'leaflet/dist/leaflet.css',
      'mapbox-gl/dist/mapbox-gl.css',
      'leaflet.markercluster/dist/MarkerCluster.css',
      'react-bootstrap-typeahead/css/Typeahead.css',
      './meinberlin/apps/plans/assets/plans_map.jsx'
    ],
    a4maps_display_point: [
      'leaflet/dist/leaflet.css',
      'mapbox-gl/dist/mapbox-gl.css',
      'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_display_point.js'
    ],
    a4maps_display_points: [
      'leaflet/dist/leaflet.css',
      'mapbox-gl/dist/mapbox-gl.css',
      'leaflet.markercluster/dist/MarkerCluster.css',
      'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_display_points.js'
    ],
    a4maps_choose_point: [
      'leaflet/dist/leaflet.css',
      'mapbox-gl/dist/mapbox-gl.css',
      'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_choose_point.js'
    ],
    a4maps_choose_polygon: [
      'leaflet/dist/leaflet.css',
      'mapbox-gl/dist/mapbox-gl.css',
      'leaflet-draw/dist/leaflet.draw.css',
      './meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js'
    ],
    category_formset: [
      'adhocracy4/adhocracy4/categories/assets/category_formset.js'
    ],
    image_uploader: [
      'adhocracy4/adhocracy4/images/assets/image_uploader.js'
    ],
    select_dropdown_init: [
      'adhocracy4/adhocracy4/categories/assets/select_dropdown_init.js'
    ]
  },
  output: {
    libraryTarget: 'this',
    library: '[name]',
    path: path.resolve('./meinberlin/static/'),
    publicPath: '/static/'
  },
  externals: {
    django: 'django'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules\/(?!(adhocracy4)\/).*/, // exclude most dependencies
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env', '@babel/preset-react'].map(require.resolve),
          plugins: ['@babel/plugin-transform-runtime', '@babel/plugin-transform-modules-commonjs']
        }
      },
      {
        test: /\.s?css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader
          },
          {
            loader: 'css-loader',
            options: {
              url: url => !url.startsWith('/')
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')
                ]
              }
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      },
      {
        test: /fonts\/.*\.(svg|woff2?|ttf|eot)(\?.*)?$/,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[ext]'
        }
      },
      {
        test: /\.svg$|\.png$/,
        loader: 'file-loader',
        options: {
          name: 'images/[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    fallback: { path: require.resolve('path-browserify') },
    extensions: ['*', '.js', '.jsx', '.scss', '.css'],
    alias: {
      bootstrap$: 'bootstrap/dist/js/bootstrap.bundle.min.js',
      jquery$: 'jquery/dist/jquery.min.js',
      select2$: 'select2/dist/js/select2.min.js',
      shariff$: 'shariff/dist/shariff.min.js',
      shpjs$: 'shpjs/dist/shp.min.js',
      tether$: 'tether/dist/js/tether.min.js',
      'slick-carousel$': 'slick-carousel/slick/slick.min.js'
    },
    // when using `npm link`, dependencies are resolved against the linked
    // folder by default. This may result in dependencies being included twice.
    // Setting `resolve.root` forces webpack to resolve all dependencies
    // against the local directory.
    modules: [path.resolve('./node_modules')]
  },
  plugins: [
    new webpack.ProvidePlugin({
      timeago: 'timeago.js',
      $: 'jquery',
      jQuery: 'jquery',
      Promise: ['es6-promise', 'Promise'],
      fetch: ['whatwg-fetch', 'fetch']
    }),
    new webpack.optimize.SplitChunksPlugin({
      name: 'vendor',
      filename: 'vendor.js'
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].css'
    }),
    new CopyWebpackPlugin({
      patterns: [{
        from: './meinberlin/assets/images/**/*',
        to: 'images/[name].[ext]'
      }]
    })
  ]
}
<commit_msg>webpack: Explicitly declare chunk dependencies<commit_after>const webpack = require('webpack')
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  entry: {
    vendor: [
      '@fortawesome/fontawesome-free/scss/fontawesome.scss',
      '@fortawesome/fontawesome-free/scss/brands.scss',
      '@fortawesome/fontawesome-free/scss/regular.scss',
      '@fortawesome/fontawesome-free/scss/solid.scss',
      'adhocracy4',
      'classnames',
      'immutability-helper',
      'js-cookie',
      'react',
      'react-dom',
      'react-flip-move',
      'react-sticky-box'
    ],
    adhocracy4: {
      import: [
        'shariff/dist/shariff.min.css',
        'select2/dist/css/select2.min.css',
        'slick-carousel/slick/slick.css',
        './meinberlin/assets/extra_css/_slick-theme.css',
        './meinberlin/assets/scss/style.scss',
        './meinberlin/assets/js/app.js'
      ],
      dependOn: 'vendor'
    },
    captcheck: {
      import: [
        './meinberlin/apps/captcha/assets/captcheck.js'
      ],
      dependOn: 'adhocracy4'
    },
    datepicker: {
      import: [
        './meinberlin/assets/js/init-picker.js',
        'datepicker/css/datepicker.min.css'
      ],
      dependOn: 'adhocracy4'
    },
    embed: {
      import: [
        'bootstrap/js/dist/modal.js',
        './meinberlin/apps/embed/assets/embed.js'
      ],
      dependOn: 'adhocracy4'
    },
    unload_warning: {
      import: [
        './meinberlin/assets/js/unload_warning.js'
      ],
      dependOn: 'adhocracy4'
    },
    dsgvo_video_embed: {
      import: [
        'dsgvo-video-embed/dist/dsgvo-video-embed.min.css',
        'dsgvo-video-embed/dist/dsgvo-video-embed.min.js'
      ],
      dependOn: 'adhocracy4'
    },
    // A4 dependencies - we want all of them to go through webpack
    mb_plans_map: {
      import: [
        'leaflet/dist/leaflet.css',
        'mapbox-gl/dist/mapbox-gl.css',
        'leaflet.markercluster/dist/MarkerCluster.css',
        'react-bootstrap-typeahead/css/Typeahead.css',
        './meinberlin/apps/plans/assets/plans_map.jsx'
      ],
      dependOn: 'adhocracy4'
    },
    a4maps_display_point: {
      import: [
        'leaflet/dist/leaflet.css',
        'mapbox-gl/dist/mapbox-gl.css',
        'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_display_point.js'
      ],
      dependOn: 'adhocracy4'
    },
    a4maps_display_points: {
      import: [
        'leaflet/dist/leaflet.css',
        'mapbox-gl/dist/mapbox-gl.css',
        'leaflet.markercluster/dist/MarkerCluster.css',
        'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_display_points.js'
      ],
      dependOn: 'adhocracy4'
    },
    a4maps_choose_point: {
      import: [
        'leaflet/dist/leaflet.css',
        'mapbox-gl/dist/mapbox-gl.css',
        'adhocracy4/adhocracy4/maps/static/a4maps/a4maps_choose_point.js'
      ],
      dependOn: 'adhocracy4'
    },
    a4maps_choose_polygon: {
      import: [
        'leaflet/dist/leaflet.css',
        'mapbox-gl/dist/mapbox-gl.css',
        'leaflet-draw/dist/leaflet.draw.css',
        './meinberlin/apps/maps/assets/map_choose_polygon_with_preset.js'
      ],
      dependOn: 'adhocracy4'
    },
    category_formset: {
      import: [
        'adhocracy4/adhocracy4/categories/assets/category_formset.js'
      ],
      dependOn: 'adhocracy4'
    },
    image_uploader: {
      import: [
        'adhocracy4/adhocracy4/images/assets/image_uploader.js'
      ],
      dependOn: 'adhocracy4'
    },
    select_dropdown_init: {
      import: [
        'adhocracy4/adhocracy4/categories/assets/select_dropdown_init.js'
      ],
      dependOn: 'adhocracy4'
    }
  },
  output: {
    libraryTarget: 'this',
    library: '[name]',
    path: path.resolve('./meinberlin/static/'),
    publicPath: '/static/'
  },
  externals: {
    django: 'django'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /node_modules\/(?!(adhocracy4)\/).*/, // exclude most dependencies
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env', '@babel/preset-react'].map(require.resolve),
          plugins: ['@babel/plugin-transform-runtime', '@babel/plugin-transform-modules-commonjs']
        }
      },
      {
        test: /\.s?css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader
          },
          {
            loader: 'css-loader',
            options: {
              url: url => !url.startsWith('/')
            }
          },
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer')
                ]
              }
            }
          },
          {
            loader: 'sass-loader'
          }
        ]
      },
      {
        test: /fonts\/.*\.(svg|woff2?|ttf|eot)(\?.*)?$/,
        loader: 'file-loader',
        options: {
          name: 'fonts/[name].[ext]'
        }
      },
      {
        test: /\.svg$|\.png$/,
        loader: 'file-loader',
        options: {
          name: 'images/[name].[ext]'
        }
      }
    ]
  },
  resolve: {
    fallback: { path: require.resolve('path-browserify') },
    extensions: ['*', '.js', '.jsx', '.scss', '.css'],
    alias: {
      bootstrap$: 'bootstrap/dist/js/bootstrap.bundle.min.js',
      jquery$: 'jquery/dist/jquery.min.js',
      select2$: 'select2/dist/js/select2.min.js',
      shariff$: 'shariff/dist/shariff.min.js',
      shpjs$: 'shpjs/dist/shp.min.js',
      tether$: 'tether/dist/js/tether.min.js',
      'slick-carousel$': 'slick-carousel/slick/slick.min.js'
    },
    // when using `npm link`, dependencies are resolved against the linked
    // folder by default. This may result in dependencies being included twice.
    // Setting `resolve.root` forces webpack to resolve all dependencies
    // against the local directory.
    modules: [path.resolve('./node_modules')]
  },
  plugins: [
    new webpack.ProvidePlugin({
      timeago: 'timeago.js',
      $: 'jquery',
      jQuery: 'jquery',
      Promise: ['es6-promise', 'Promise'],
      fetch: ['whatwg-fetch', 'fetch']
    }),
    new webpack.optimize.SplitChunksPlugin({
      name: 'vendor',
      filename: 'vendor.js'
    }),
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[name].css'
    }),
    new CopyWebpackPlugin({
      patterns: [{
        from: './meinberlin/assets/images/**/*',
        to: 'images/[name].[ext]'
      }]
    })
  ]
}
<|endoftext|>


<commit_before>const webpack = require('webpack'),
      path = require('path');

module.exports = {
  entry: [
    'webpack/hot/dev-server',
    path.join(__dirname, './app/components/App')
  ],
  output: {
    path: path.join(__dirname, 'assets'),
    filename: './dist/bundle.js',
    publicPath: '/'
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/i,
        exclude: /node_modules/,
        loaders: ['react-hot', 'babel']
      }, {
        test: /\.scss$/,
        loaders: [
          'style',
          'css',
          'autoprefixer?browsers=last 3 versions',
          'sass?outputStyle=expanded'
        ]
      }, {
        test: /\.json$/i,
        loader: 'json'
      }, {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loader: 'file-loader?name=/images/[name].[ext]'
      }, {
        test: /\.pdf/i,
        loader: 'file-loader?name=/docs/[name].[ext]'
      }
    ]
  },
  watch: true,
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    colors: true,
    contentBase: __dirname,
    noInfo: false,
    hot: true,
    inline: true,
    port: 3000,
    host: 'localhost'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false },
      output: { comments: false },
      sourceMap: false
    })
  ],
  stats: {
    colors: true,
    modules: true,
    reasons: true,
    errorDetails: true
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
};

// module.exports = require('webpack.config.js');
<commit_msg>config: Tweak Webpack devServer config object<commit_after>const webpack = require('webpack'),
      path = require('path');

module.exports = {
  entry: [
    'webpack/hot/dev-server',
    path.join(__dirname, './app/App')
  ],
  output: {
    path: path.join(__dirname, 'assets'),
    filename: './dist/bundle.js',
    publicPath: '/'
  },
  module: {
    loaders: [
      {
        test: /\.jsx?$/i,
        exclude: /node_modules/,
        loaders: ['babel']
      }, {
        test: /\.scss$/,
        loaders: [
          'style',
          'css',
          'autoprefixer?browsers=last 3 versions',
          'sass?outputStyle=expanded'
        ]
      }, {
        test: /\.json$/i,
        loader: 'json'
      }, {
        test: /\.(jpe?g|png|gif|svg)$/i,
        loader: 'file-loader?name=/images/[name].[ext]'
      }, {
        test: /\.pdf/i,
        loader: 'file-loader?name=/docs/[name].[ext]'
      }
    ]
  },
  watch: true,
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    colors: true,
    contentBase: __dirname,
    noInfo: false,
    hot: true,
    inline: true,
    port: 3000,
    host: 'localhost'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': {
        'NODE_ENV': JSON.stringify('production')
      }
    }),
    new webpack.HotModuleReplacementPlugin(),
    new webpack.optimize.DedupePlugin(),
    new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false },
      output: { comments: false },
      sourceMap: false
    })
  ],
  stats: {
    colors: true,
    modules: true,
    reasons: true,
    errorDetails: true
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
};

// module.exports = require('webpack.config.js');
<|endoftext|>


<commit_before>const path = require('path');
const webpack = require('webpack');
const ROOT_PATH = path.resolve(__dirname)

module.exports = function(env) {
  let plugins = [
    new webpack.HotModuleReplacementPlugin(),
  ];

  if (env && env.production) {
    plugins.append(new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false },
      minimize: true,
      comments: false,
      sourceMap: true,
    }))
  }

  return {
    entry: ['babel-polyfill', './src/index.jsx'],
    output: {
      path: path.resolve(ROOT_PATH, './'),
      publicPath: '/',
      filename: 'bundle.js',
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        },
        {
          test: /\.(gif|png|svg)$/,
          use: ['url-loader', 'img-loader']
        },
        {
          test: /\.jsx?$/,
          use: ['react-hot-loader', 'babel-loader'],
          exclude: /node_modules/
        },
        {
          test: /\.less$/,
          use: ['style-loader', 'css-loader', 'less-loader'],
          exclude: /\landing.less$/
        },
      ]
    },
    resolve: {
      extensions: ['.js', '.jsx']
    },
    devServer: {
      contentBase: path.resolve(ROOT_PATH, './'),
      historyApiFallback: true,
      hot: true,
      inline: true,
      port: 8000,
    },
    plugins: plugins,
  };
};
<commit_msg>Fix array push<commit_after>const path = require('path');
const webpack = require('webpack');
const ROOT_PATH = path.resolve(__dirname)

module.exports = function(env) {
  let plugins = [
    new webpack.HotModuleReplacementPlugin(),
  ];

  if (env && env.production) {
    plugins.push(new webpack.optimize.UglifyJsPlugin({
      compress: { warnings: false },
      minimize: true,
      comments: false,
      sourceMap: true,
    }))
  }

  return {
    entry: ['babel-polyfill', './src/index.jsx'],
    output: {
      path: path.resolve(ROOT_PATH, './'),
      publicPath: '/',
      filename: 'bundle.js',
    },
    module: {
      rules: [
        {
          test: /\.css$/,
          use: ['style-loader', 'css-loader']
        },
        {
          test: /\.(gif|png|svg)$/,
          use: ['url-loader', 'img-loader']
        },
        {
          test: /\.jsx?$/,
          use: ['react-hot-loader', 'babel-loader'],
          exclude: /node_modules/
        },
        {
          test: /\.less$/,
          use: ['style-loader', 'css-loader', 'less-loader'],
          exclude: /\landing.less$/
        },
      ]
    },
    resolve: {
      extensions: ['.js', '.jsx']
    },
    devServer: {
      contentBase: path.resolve(ROOT_PATH, './'),
      historyApiFallback: true,
      hot: true,
      inline: true,
      port: 8000,
    },
    plugins: plugins,
  };
};
<|endoftext|>


<commit_before>const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'build.js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [{
          loader: 'vue-loader',
          options: {
            loaders: {
              'scss': 'vue-style-loader!css-loader!sass-loader',
              'sass': 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
            }
          }
        }]
      },
      {
        test: /\.js$/,
        use: ['babel-loader'],
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader']
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|eot|ttf|woff|woff2)$/,
        use: [{
          loader: 'url-loader',
          options: {
            name: '[name].[ext]?[hash]',
            limit: 10000
          }
        }]
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.vue'],
    modules: [
      'node_modules'
    ],
    alias: {
      'vue$': 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    noInfo: true,
    historyApiFallback: {
      index: '/dist/'
    },
  },
  devtool: '#eval-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}<commit_msg>build(webpack): Added friendly errors and dashboard support<commit_after>const path = require('path')
const webpack = require('webpack')
const notifier = require('node-notifier')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const WebpackDashboardPlugin = require('webpack-dashboard/plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')

module.exports = {
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'build.js'
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        use: [
          {
            loader: 'vue-loader',
            options: {
              loaders: {
                scss: 'vue-style-loader!css-loader!sass-loader',
                sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax'
              }
            }
          }
        ]
      },
      {
        test: /\.js$/,
        use: ['babel-loader'],
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader', 'postcss-loader']
      },
      {
        test: /\.(png|jpg|jpeg|gif|svg|eot|ttf|woff|woff2)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              name: '[name].[ext]?[hash]',
              limit: 10000
            }
          }
        ]
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.vue'],
    modules: ['node_modules'],
    alias: {
      vue$: 'vue/dist/vue.esm.js'
    }
  },
  devServer: {
    port: 3000,
    noInfo: true,
    historyApiFallback: {
      index: '/dist/'
    },
    quiet: true
  },
  devtool: '#eval-source-map',
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
}

if (process.env.NODE_ENV === 'development') {
  module.exports.plugins = (module.exports.plugins || []).concat([
    new WebpackDashboardPlugin(),
    new FriendlyErrorsPlugin({
      compilationSuccessInfo: {
        messages: [
          'Hooray! The build was successful.',
          'The local development url is http://localhost:3000'
        ],
        notes: [
          'This build is not optimized for production.',
          'To create a production build, run `npm run build`'
        ]
      },
      onErrors: (severity, errors) => {
        if (severity === 'error') {
          const error = errors[0]
          const message = error.message.match(/(^.*)/)
          notifier.notify({
            title: `Error in ${error.file}`,
            message: message[0]
          })
        }
      }
    })
  ])
}

if (process.env.NODE_ENV === 'production') {
  module.exports.devtool = '#source-map'
  module.exports.plugins = (module.exports.plugins || []).concat([
    new webpack.DefinePlugin({
      'process.env': {
        NODE_ENV: '"production"'
      }
    }),
    new webpack.optimize.UglifyJsPlugin({
      sourceMap: true,
      compress: {
        warnings: false
      }
    }),
    new webpack.LoaderOptionsPlugin({
      minimize: true
    })
  ])
}
<|endoftext|>