<commit_before>'use strict';

var url = require('url'),
    Q = require('q');

var lib = require('manifoldjs-lib');

var utils = lib.utils;

function convertFromBase(manifestInfo, callback) {

  if (!manifestInfo || !manifestInfo.content) {
    return Q.reject(new Error('Manifest content is empty or not initialized.')).nodeify(callback);
  }

  var originalManifest = manifestInfo.content;

  if (!originalManifest.start_url) {
    return Q.reject(new Error('Start URL is required.')).nodeify(callback);
  }

  var manifest = {
    'manifest_version': 2,
    'name': originalManifest.name || originalManifest.short_name || utils.sanitizeName(url.parse(originalManifest.start_url).hostname),
    'version': '0.0.1',
    'app': {
      'launch': {
        'web_url': originalManifest.start_url
      }
    }
  };

  // map icons
  if (originalManifest.icons && originalManifest.icons.length) {
    var icons = {};

    for (var i = 0; i < originalManifest.icons.length; i++) {
      var icon = originalManifest.icons[i];
      var iconDimensions = icon.sizes.split('x');
      if (iconDimensions[0] === iconDimensions[1]) {
        icons[iconDimensions[0]] = url.parse(icon.src).path;;
      }
    }

    manifest.icons = icons;
  }

  // map permissions
  // NOTE: Chrome manifest does not support assigning different permissions based on URL.
  // Mapping calculates union of all permissions found for the 'Chrome' platform and grants
  // the same set of permissions for all URLs.
  if (originalManifest.mjs_api_access && originalManifest.mjs_api_access.length) {
    var permissionSet = {};
    originalManifest.mjs_api_access.forEach(function(rule) {
      if (rule.platform && rule.platform.split(';')
        .map(function (item) { return item.trim(); })
        .indexOf('chrome') >= 0) {
          if (!manifest.app.urls) {
            manifest.app.urls = [];
          }

          // add app URL unless it matches start_url
          var url = rule.match.trim().replace(/\*$/, '');
          if (url !== originalManifest.start_url) {
            manifest.app.urls.push(url);
          }

          // use permissionSet object to calculate union of the permissions in all rules
          rule.access.split(';').forEach(function (p) {
            permissionSet[p.trim()] = undefined;
          });
        }
    });

    // create permissions array
    var permissions = Object.keys(permissionSet);
    if (permissions.length) {
      manifest.permissions = permissions;
    }
  }

  var convertedManifestInfo = {
    'content': manifest,
    'format': lib.constants.CHROME_MANIFEST_FORMAT
  };

  return Q.resolve(convertedManifestInfo).nodeify(callback);
}

module.exports = {
  convertFromBase: convertFromBase
};
<commit_msg>FIxed jshint warning<commit_after>'use strict';

var url = require('url'),
    Q = require('q');

var lib = require('manifoldjs-lib');

var utils = lib.utils;

function convertFromBase(manifestInfo, callback) {

  if (!manifestInfo || !manifestInfo.content) {
    return Q.reject(new Error('Manifest content is empty or not initialized.')).nodeify(callback);
  }

  var originalManifest = manifestInfo.content;

  if (!originalManifest.start_url) {
    return Q.reject(new Error('Start URL is required.')).nodeify(callback);
  }

  var manifest = {
    'manifest_version': 2,
    'name': originalManifest.name || originalManifest.short_name || utils.sanitizeName(url.parse(originalManifest.start_url).hostname),
    'version': '0.0.1',
    'app': {
      'launch': {
        'web_url': originalManifest.start_url
      }
    }
  };

  // map icons
  if (originalManifest.icons && originalManifest.icons.length) {
    var icons = {};

    for (var i = 0; i < originalManifest.icons.length; i++) {
      var icon = originalManifest.icons[i];
      var iconDimensions = icon.sizes.split('x');
      if (iconDimensions[0] === iconDimensions[1]) {
        icons[iconDimensions[0]] = url.parse(icon.src).path;
      }
    }

    manifest.icons = icons;
  }

  // map permissions
  // NOTE: Chrome manifest does not support assigning different permissions based on URL.
  // Mapping calculates union of all permissions found for the 'Chrome' platform and grants
  // the same set of permissions for all URLs.
  if (originalManifest.mjs_api_access && originalManifest.mjs_api_access.length) {
    var permissionSet = {};
    originalManifest.mjs_api_access.forEach(function(rule) {
      if (rule.platform && rule.platform.split(';')
        .map(function (item) { return item.trim(); })
        .indexOf('chrome') >= 0) {
          if (!manifest.app.urls) {
            manifest.app.urls = [];
          }

          // add app URL unless it matches start_url
          var url = rule.match.trim().replace(/\*$/, '');
          if (url !== originalManifest.start_url) {
            manifest.app.urls.push(url);
          }

          // use permissionSet object to calculate union of the permissions in all rules
          rule.access.split(';').forEach(function (p) {
            permissionSet[p.trim()] = undefined;
          });
        }
    });

    // create permissions array
    var permissions = Object.keys(permissionSet);
    if (permissions.length) {
      manifest.permissions = permissions;
    }
  }

  var convertedManifestInfo = {
    'content': manifest,
    'format': lib.constants.CHROME_MANIFEST_FORMAT
  };

  return Q.resolve(convertedManifestInfo).nodeify(callback);
}

module.exports = {
  convertFromBase: convertFromBase
};
<|endoftext|>


<commit_before>/**
 *
 * identity verification
 *
 *
 *
 * 20150813
 */
 (function($, undefined){
 	//生产类
 	var Puf = function(el, op){
 		this.$el = $(el);
 		this.nameInput = $("#name");
 		this.idCardInput = $("#id_card");
 		this.nextBtn = $("#a_next");
 		this._addEvents();
 	}

 	Puf.prototype = {
 		//初始化

 		_init : function(){

 		},
 		//所有事件绑定
 		_addEvents : function(){
 			var sthis = this;

 			/*身份证号输入判断*/
			this.idCardInput.on("keyup", function(){
				var idCard_val = this.value;
				this.value = sthis._fnIdentity(idCard_val);
			})

			/*判断信息完整*/
			this.$el.find("input").on("input", function(){
				sthis._fnComplete();
			})

			/*下一步按钮*/
			this.nextBtn.on("click", function(){
				var name = sthis.nameInput.val(),
					cid = sthis.idCardInput.val(),
					nextBtn = this;
				sthis._fnSubmit(nextBtn,name,cid);
			})

 		},

 		//身份证处理
		_fnIdentity : function(idCard_val){
			idCard_val=idCard_val.replace(/[^\d|xX]/g,"");
			return idCard_val;
		},
		//表单处理
		_fnComplete : function(){
			var ipt=this.$el.find("input"),
				ipt_flag=1,
				ipt_len=2;

			for(var i=0; i<ipt_len; i++){
				if(ipt[i].value.length == 0){
					ipt_flag=0;
				}
			}
			if(ipt_flag==1){
				this.nextBtn.removeClass("disabled");
				this.nextBtn.removeAttr("disabled");
			}else{
				this.nextBtn.addClass("disabled");
				this.nextBtn.attr("disabled","disabled");
			}
		},
 		_fnSubmit : function(nextBtn,name,cid){
 			var flag,
 				age = TOOLS.isIdentity(cid);

			RegCellName = /^[\u4e00-\u9fa5\·\•\●]*$/;
			name_falg=name.search(RegCellName);
			if(name.length<2 || name_falg=="-1"){
				falg=-1;
			}else{
				falg=0;
			}

			if(falg==-1){
				MessageBox.alert({type:"common",txt:"请输入真实姓名！"});
				$("#name").focus();
			}else if(cid.length<18){
				MessageBox.alert({type:"common",txt:"请输入18位身份证号！"});
			}else if(!age){
				MessageBox.alert({type:"common",txt:"身份证号不合法！"});
			}else{
				 alert("success!");//写表单提交代码
				/*if(nextBtn.hasClass("disabled")) return false;
					nextBtn.addClass('disabled');
				TOOLS.ajax({
					url:"./index.php?r=loan/auth",
					data:{name:name,cid:cid},
					type:"post",
					dataType:"json",
					fnSuccess:function(data){
						if(data.stat == "1"){
							window.location.href="./index.php?r=loan/password&type="+data.type;
						}else if(data.stat == "2"){
							nextBtn.removeClass('disabled');
	        				MessageBox.alert({type:"common",txt:"身份验证失败！"});
						}
					},
					fnError:function(){}
				});*/
			}
 		}
 	}

 	$.fn.PUF = function(options){
 		return this.each(function(){
 			var $this = $(this) ,
 				data = $this.data('PUF');

 			if(!data){
 				$this.data('PUF', (data = new Puf(this, options)))
 			}

 			if(typeof options == 'string') data[options](setData);
 		});
 	}
 })(window.jQuery||window.Zepto)

<commit_msg>hez/dev<commit_after>/**
 *
 * identity verification
 *
 *
 *
 * 20150813
 */
 (function($, undefined){
 	//生产类
 	var Puf = function(el, op){
 		this.$el = $(el);
 		this.nameInput = $("#name");
 		this.idCardInput = $("#id_card");
 		this.nextBtn = $("#a_next");
 		this._addEvents();
 	}

 	Puf.prototype = {
 		//初始化

 		_init : function(){

 		},
 		//所有事件绑定
 		_addEvents : function(){
 			var sthis = this;

 			/*身份证号输入判断*/
			this.idCardInput.on("keyup", function(){
				var idCard_val = this.value;
				this.value = sthis._fnIdentity(idCard_val);
			})

			/*判断信息完整*/
			this.$el.find("input").on("input", function(){
				sthis._fnComplete();
			})

			/*下一步按钮*/
			this.nextBtn.on("click", function(){
				var name = sthis.nameInput.val(),
					cid = sthis.idCardInput.val(),
					nextBtn = this;
				sthis._fnSubmit(nextBtn,name,cid);
			})

 		},

 		//身份证处理
		_fnIdentity : function(idCard_val){
			idCard_val=idCard_val.replace(/[^\d|xX]/g,"");
			return idCard_val;
		},
		//表单处理
		_fnComplete : function(){
			var ipt=this.$el.find("input"),
				ipt_flag=1,
				ipt_len=2;

			for(var i=0; i<ipt_len; i++){
				if(ipt[i].value.length == 0){
					ipt_flag=0;
				}
			}
			if(ipt_flag==1){
				this.nextBtn.removeClass("disabled");
				this.nextBtn.removeAttr("disabled");
			}else{
				this.nextBtn.addClass("disabled");
				this.nextBtn.attr("disabled","disabled");
			}
		},
 		_fnSubmit : function(nextBtn,name,cid){
 			var flag,
 				age = TOOLS.isIdentity(cid);

			RegCellName = /^[\u4e00-\u9fa5\·\•\●]*$/;
			name_falg=name.search(RegCellName);
			if(name.length<2 || name_falg=="-1"){
				falg=-1;
			}else{
				falg=0;
			}

			if(falg==-1){
				MessageBox.alert({type:"common",txt:"请输入真实姓名！"});
				$("#name").focus();
			}else if(cid.length<18){
				MessageBox.alert({type:"common",txt:"请输入18位身份证号！"});
			}else if(!age){
				MessageBox.alert({type:"common",txt:"身份证号不合法！"});
			}else if(cid.length==18 && age && falg!=-1){
				 alert("success!");//写表单提交代码
				if(nextBtn.hasClass("disabled")) return false;
					nextBtn.addClass('disabled');
				TOOLS.ajax({
					url:"./index.php?r=loan/auth",
					data:{name:name,cid:cid},
					type:"post",
					dataType:"json",
					fnSuccess:function(data){
						if(data.stat == "1"){
							window.location.href="./index.php?r=loan/password&type="+data.type;
						}else if(data.stat == "2"){
							nextBtn.removeClass('disabled');
	        				MessageBox.alert({type:"common",txt:"身份验证失败！"});
						}
					},
					fnError:function(){}
				});
			}
 		}
 	}

 	$.fn.PUF = function(options){
 		return this.each(function(){
 			var $this = $(this) ,
 				data = $this.data('PUF');

 			if(!data){
 				$this.data('PUF', (data = new Puf(this, options)))
 			}

 			if(typeof options == 'string') data[options](setData);
 		});
 	}
 })(window.jQuery||window.Zepto)

<|endoftext|>


<commit_before>"use strict";

/*
    // Map regular expressions to functions
    var url_map = urls([
        ["/api/campaigns", handlers.campaigns],
        ["/api/campaign/{campaign_id}", handlers.get_campaign],
        ["/", handlers.page]
    ]);
*/
function map_urls(url_map) {
    var i;
    var param_pattern = /\{[^\}]+\}/;
    var path_pattern = /\{\{[^\}]+\}\}/;

    for(i=0; i<url_map.length; i++) {
        (function() {
            var pattern = url_map[i][0];
            var handler = url_map[i][1];

            var j = 1;
            var param_names = {};

            var matches;

            pattern = "^" + pattern + "$";
            pattern = pattern.replace(/\//g, "\\/");

            while(path_pattern.test(pattern)) {
                matches = path_pattern.exec(pattern);

                if(matches) {
                    param_names[j] = matches[0].replace(/^\{\{/, "").replace(/\}\}$/, "");
                    pattern = pattern.replace(matches[0], "(.*?)");

                    j++;
                }
            }

            while(param_pattern.test(pattern)) {
                matches = param_pattern.exec(pattern);

                if(matches) {
                    param_names[j] = matches[0].replace(/^\{/, "").replace(/\}$/, "");
                    pattern = pattern.replace(matches[0], "([^\\/]+)");

                    j++;
                }
            }

            pattern = new RegExp(pattern);

            url_map[i] = function(request, response) {
                var param_index;

                var url = request.url.replace(/\?.*$/, "");

                var matches = pattern.exec(url);

                if(matches) {
                    var params = {};
                    for(param_index in param_names) {
                        params[param_names[param_index]] = matches[param_index];
                    }

                    handler(request, response, params);

                    return true;
                }
            };
        }());
    }

    return url_map;
}

module.exports = map_urls;
<commit_msg>URL patterns can no longer contain arbitrary regular expressions<commit_after>"use strict";

/*
    // Map URL patterns to functions
    var url_map = urls([
        ["/api/campaigns", handlers.campaigns],
        ["/api/campaign/{campaign_id}", handlers.get_campaign],
        ["/", handlers.page]
    ]);
*/
function map_urls(url_map) {
    var i;
    var escape_pattern = /([\.\*\+\^\$\(\)\[\]\\\/])/g;
    var param_pattern = /\{[^\}]+\}/;
    var path_pattern = /\{\{[^\}]+\}\}/;

    for(i=0; i<url_map.length; i++) {
        (function() {
            var pattern = url_map[i][0];
            var handler = url_map[i][1];

            var j = 1;
            var param_names = {};

            var matches;

            pattern = "^" + pattern + "$";
            pattern = pattern.replace(/\//g, "\\/");

            while(path_pattern.test(pattern)) {
                matches = path_pattern.exec(pattern);

                if(matches) {
                    param_names[j] = matches[0].replace(/^\{\{/, "").replace(/\}\}$/, "");
                    pattern = pattern.replace(matches[0], "(.*?)");

                    j++;
                }
            }

            while(param_pattern.test(pattern)) {
                matches = param_pattern.exec(pattern);

                if(matches) {
                    param_names[j] = matches[0].replace(/^\{/, "").replace(/\}$/, "");
                    pattern = pattern.replace(matches[0], "([^\\/]+)");

                    j++;
                }
            }

            pattern = new RegExp(pattern);

            url_map[i] = function(request, response) {
                var param_index;

                var url = request.url.replace(/\?.*$/, "");

                var matches = pattern.exec(url);

                if(matches) {
                    var params = {};
                    for(param_index in param_names) {
                        params[param_names[param_index]] = matches[param_index];
                    }

                    handler(request, response, params);

                    return true;
                }
            };
        }());
    }

    return url_map;
}

module.exports = map_urls;
<|endoftext|>


<commit_before>"use strict";

const {strict: assert} = require("assert");

const {mock_cjs, mock_esm, set_global, zrequire} = require("../zjsunit/namespace");
const {make_stub} = require("../zjsunit/stub");
const {run_test} = require("../zjsunit/test");
const blueslip = require("../zjsunit/zblueslip");
const $ = require("../zjsunit/zjquery");
const {page_params} = require("../zjsunit/zpage_params");

// These unit tests for static/js/message_list.js emphasize the model-ish
// aspects of the MessageList class.  We have to stub out a few functions
// related to views and events to get the tests working.

const noop = function () {};

mock_cjs("jquery", $);
mock_esm("../../static/js/filter", {
    Filter: noop,
});
set_global("document", {
    to_$() {
        return {
            trigger() {},
        };
    },
});

const narrow_state = mock_esm("../../static/js/narrow_state");
const stream_data = mock_esm("../../static/js/stream_data");

const muting = zrequire("muting");
const {MessageList} = zrequire("message_list");

function accept_all_filter() {
    const filter = {
        predicate: () => () => true,
    };

    return filter;
}

run_test("basics", (override) => {
    const filter = accept_all_filter();

    const list = new MessageList({
        filter,
    });

    const messages = [
        {
            id: 50,
            content: "fifty",
        },
        {
            id: 60,
        },
        {
            id: 70,
        },
        {
            id: 80,
        },
    ];

    assert.equal(list.empty(), true);

    list.append(messages, true);

    assert.equal(list.num_items(), 4);
    assert.equal(list.empty(), false);
    assert.equal(list.first().id, 50);
    assert.equal(list.last().id, 80);

    assert.equal(list.get(50).content, "fifty");

    assert.equal(list.closest_id(49), 50);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(51), 50);
    assert.equal(list.closest_id(59), 60);
    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(61), 60);

    assert.deepEqual(list.all_messages(), messages);

    override($, "Event", (ev) => {
        assert.equal(ev, "message_selected.zulip");
    });
    list.select_id(50);

    assert.equal(list.selected_id(), 50);
    assert.equal(list.selected_idx(), 0);

    list.advance_past_messages([60, 80]);
    assert.equal(list.selected_id(), 60);
    assert.equal(list.selected_idx(), 1);

    // Make sure not rerendered when reselected
    let num_renders = 0;
    list.rerender = function () {
        num_renders += 1;
    };
    list.reselect_selected_id();
    assert.equal(num_renders, 0);
    assert.equal(list.selected_id(), 60);

    const old_messages = [
        {
            id: 30,
        },
        {
            id: 40,
        },
    ];
    list.add_messages(old_messages);
    assert.equal(list.first().id, 30);
    assert.equal(list.last().id, 80);

    const new_messages = [
        {
            id: 90,
        },
    ];
    list.append(new_messages, true);
    assert.equal(list.last().id, 90);

    list.view.clear_table = function () {};

    list.remove_and_rerender([60]);
    const removed = list.all_messages().filter((msg) => msg.id !== 60);
    assert.deepEqual(list.all_messages(), removed);

    list.clear();
    assert.deepEqual(list.all_messages(), []);
});

run_test("prev_next", () => {
    const list = new MessageList({});

    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    // try to confuse things with bogus selected id
    list.data.set_selected_id(33);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    const messages = [{id: 30}, {id: 40}, {id: 50}, {id: 60}];
    list.append(messages, true);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);

    // The next case is for defensive code.
    list.data.set_selected_id(45);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    list.data.set_selected_id(30);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), 40);

    list.data.set_selected_id(50);
    assert.equal(list.prev(), 40);
    assert.equal(list.next(), 60);
    assert.equal(list.is_at_end(), false);

    list.data.set_selected_id(60);
    assert.equal(list.prev(), 50);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), true);
});

run_test("message_range", () => {
    const list = new MessageList({});

    const messages = [{id: 30}, {id: 40}, {id: 50}, {id: 60}];
    list.append(messages, true);
    assert.deepEqual(list.message_range(2, 30), [{id: 30}]);
    assert.deepEqual(list.message_range(2, 31), [{id: 30}, {id: 40}]);
    assert.deepEqual(list.message_range(30, 40), [{id: 30}, {id: 40}]);
    assert.deepEqual(list.message_range(31, 39), [{id: 40}]);
    assert.deepEqual(list.message_range(31, 1000), [{id: 40}, {id: 50}, {id: 60}]);
    blueslip.expect("error", "message_range given a start of -1");
    assert.deepEqual(list.message_range(-1, 40), [{id: 30}, {id: 40}]);
});

run_test("nth_most_recent_id", () => {
    const list = new MessageList({});
    list.append([{id: 10}, {id: 20}, {id: 30}]);
    assert.equal(list.nth_most_recent_id(1), 30);
    assert.equal(list.nth_most_recent_id(2), 20);
    assert.equal(list.nth_most_recent_id(3), 10);
    assert.equal(list.nth_most_recent_id(4), -1);
});

run_test("change_message_id", () => {
    const list = new MessageList({});
    list.data._add_to_hash([
        {id: 10.5, content: "good job"},
        {id: 20.5, content: "ok!"},
    ]);

    // local to local
    list.change_message_id(10.5, 11.5);
    assert.equal(list.get(11.5).content, "good job");

    list.change_message_id(11.5, 11);
    assert.equal(list.get(11).content, "good job");

    list.change_message_id(20.5, 10);
    assert.equal(list.get(10).content, "ok!");

    // test nonexistent id
    assert.equal(list.change_message_id(13, 15), undefined);
});

run_test("last_sent_by_me", () => {
    const list = new MessageList({});
    const items = [
        {
            id: 1,
            sender_id: 3,
        },
        {
            id: 2,
            sender_id: 3,
        },
        {
            id: 3,
            sender_id: 6,
        },
    ];

    list.append(items);
    page_params.user_id = 3;
    // Look for the last message where user_id == 3 (our ID)
    assert.equal(list.get_last_message_sent_by_me().id, 2);
});

run_test("local_echo", () => {
    let list = new MessageList({});
    list.append([
        {id: 10},
        {id: 20},
        {id: 30},
        {id: 20.02},
        {id: 20.03},
        {id: 40},
        {id: 50},
        {id: 60},
    ]);
    list._local_only = {20.02: {id: 20.02}, 20.03: {id: 20.03}};

    assert.equal(list.closest_id(10), 10);
    assert.equal(list.closest_id(20), 20);
    assert.equal(list.closest_id(30), 30);
    assert.equal(list.closest_id(20.02), 20.02);
    assert.equal(list.closest_id(20.03), 20.03);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(40), 40);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(60), 60);

    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(21), 20);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(31), 30);
    assert.equal(list.closest_id(54), 50);
    assert.equal(list.closest_id(58), 60);

    list = new MessageList({});
    list.append([
        {id: 10},
        {id: 20},
        {id: 30},
        {id: 20.02},
        {id: 20.03},
        {id: 40},
        {id: 50},
        {id: 50.01},
        {id: 50.02},
        {id: 60},
    ]);
    list._local_only = {
        20.02: {id: 20.02},
        20.03: {id: 20.03},
        50.01: {id: 50.01},
        50.02: {id: 50.02},
    };

    assert.equal(list.closest_id(10), 10);
    assert.equal(list.closest_id(20), 20);
    assert.equal(list.closest_id(30), 30);
    assert.equal(list.closest_id(20.02), 20.02);
    assert.equal(list.closest_id(20.03), 20.03);
    assert.equal(list.closest_id(40), 40);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(60), 60);

    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(21), 20);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(31), 30);
    assert.equal(list.closest_id(47), 50);
    assert.equal(list.closest_id(51), 50.02);
    assert.equal(list.closest_id(59), 60);
    assert.equal(list.closest_id(50.01), 50.01);
});

run_test("bookend", (override) => {
    const list = new MessageList({});

    let expected = "translated: You subscribed to stream IceCream";
    list.view.clear_trailing_bookend = noop;
    list.narrowed = true;

    override(narrow_state, "stream", () => "IceCream");

    let is_subscribed = true;
    let invite_only = false;

    override(stream_data, "is_subscribed", () => is_subscribed);
    override(stream_data, "get_sub", () => ({invite_only}));

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, true);
        assert.equal(bookend.show_button, true);
    }

    expected = "translated: You unsubscribed from stream IceCream";
    list.last_message_historical = false;

    is_subscribed = false;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, true);
    }

    // Test when the stream is privates (invite only)
    expected = "translated: You unsubscribed from stream IceCream";

    invite_only = true;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, false);
    }

    expected = "translated: You are not subscribed to stream IceCream";
    list.last_message_historical = true;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, true);
    }
});

run_test("filter_muted_topic_messages", () => {
    const list = new MessageList({
        excludes_muted_topics: true,
    });
    muting.add_muted_topic(1, "muted");

    const unmuted = [
        {
            id: 50,
            type: "stream",
            stream_id: 1,
            mentioned: true, // overrides mute
            topic: "muted",
        },
        {
            id: 60,
            type: "stream",
            stream_id: 1,
            mentioned: false,
            topic: "whatever",
        },
    ];
    const muted = [
        {
            id: 70,
            type: "stream",
            stream_id: 1,
            mentioned: false,
            topic: "muted",
        },
    ];

    // Make sure unmuted_message filters out the "muted" entry,
    // which we mark as having a muted topic, and not mentioned.
    const test_unmuted = list.unmuted_messages(unmuted.concat(muted));
    assert.deepEqual(unmuted, test_unmuted);
});

run_test("add_remove_rerender", () => {
    const filter = accept_all_filter();

    const list = new MessageList({filter});

    const messages = [{id: 1}, {id: 2}, {id: 3}];

    list.add_messages(messages);
    assert.equal(list.num_items(), 3);

    {
        const stub = make_stub();
        list.rerender = stub.f;
        const message_ids = messages.map((msg) => msg.id);
        list.remove_and_rerender(message_ids);
        assert.equal(stub.num_calls, 1);
        assert.equal(list.num_items(), 0);
    }
});
<commit_msg>node tests: Remove redundant muting tests for message lists.<commit_after>"use strict";

const {strict: assert} = require("assert");

const {mock_cjs, mock_esm, set_global, zrequire} = require("../zjsunit/namespace");
const {make_stub} = require("../zjsunit/stub");
const {run_test} = require("../zjsunit/test");
const blueslip = require("../zjsunit/zblueslip");
const $ = require("../zjsunit/zjquery");
const {page_params} = require("../zjsunit/zpage_params");

// These unit tests for static/js/message_list.js emphasize the model-ish
// aspects of the MessageList class.  We have to stub out a few functions
// related to views and events to get the tests working.

const noop = function () {};

mock_cjs("jquery", $);
mock_esm("../../static/js/filter", {
    Filter: noop,
});
set_global("document", {
    to_$() {
        return {
            trigger() {},
        };
    },
});

const narrow_state = mock_esm("../../static/js/narrow_state");
const stream_data = mock_esm("../../static/js/stream_data");

const {MessageList} = zrequire("message_list");

function accept_all_filter() {
    const filter = {
        predicate: () => () => true,
    };

    return filter;
}

run_test("basics", (override) => {
    const filter = accept_all_filter();

    const list = new MessageList({
        filter,
    });

    const messages = [
        {
            id: 50,
            content: "fifty",
        },
        {
            id: 60,
        },
        {
            id: 70,
        },
        {
            id: 80,
        },
    ];

    assert.equal(list.empty(), true);

    list.append(messages, true);

    assert.equal(list.num_items(), 4);
    assert.equal(list.empty(), false);
    assert.equal(list.first().id, 50);
    assert.equal(list.last().id, 80);

    assert.equal(list.get(50).content, "fifty");

    assert.equal(list.closest_id(49), 50);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(51), 50);
    assert.equal(list.closest_id(59), 60);
    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(61), 60);

    assert.deepEqual(list.all_messages(), messages);

    override($, "Event", (ev) => {
        assert.equal(ev, "message_selected.zulip");
    });
    list.select_id(50);

    assert.equal(list.selected_id(), 50);
    assert.equal(list.selected_idx(), 0);

    list.advance_past_messages([60, 80]);
    assert.equal(list.selected_id(), 60);
    assert.equal(list.selected_idx(), 1);

    // Make sure not rerendered when reselected
    let num_renders = 0;
    list.rerender = function () {
        num_renders += 1;
    };
    list.reselect_selected_id();
    assert.equal(num_renders, 0);
    assert.equal(list.selected_id(), 60);

    const old_messages = [
        {
            id: 30,
        },
        {
            id: 40,
        },
    ];
    list.add_messages(old_messages);
    assert.equal(list.first().id, 30);
    assert.equal(list.last().id, 80);

    const new_messages = [
        {
            id: 90,
        },
    ];
    list.append(new_messages, true);
    assert.equal(list.last().id, 90);

    list.view.clear_table = function () {};

    list.remove_and_rerender([60]);
    const removed = list.all_messages().filter((msg) => msg.id !== 60);
    assert.deepEqual(list.all_messages(), removed);

    list.clear();
    assert.deepEqual(list.all_messages(), []);
});

run_test("prev_next", () => {
    const list = new MessageList({});

    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    // try to confuse things with bogus selected id
    list.data.set_selected_id(33);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    const messages = [{id: 30}, {id: 40}, {id: 50}, {id: 60}];
    list.append(messages, true);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);

    // The next case is for defensive code.
    list.data.set_selected_id(45);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), false);

    list.data.set_selected_id(30);
    assert.equal(list.prev(), undefined);
    assert.equal(list.next(), 40);

    list.data.set_selected_id(50);
    assert.equal(list.prev(), 40);
    assert.equal(list.next(), 60);
    assert.equal(list.is_at_end(), false);

    list.data.set_selected_id(60);
    assert.equal(list.prev(), 50);
    assert.equal(list.next(), undefined);
    assert.equal(list.is_at_end(), true);
});

run_test("message_range", () => {
    const list = new MessageList({});

    const messages = [{id: 30}, {id: 40}, {id: 50}, {id: 60}];
    list.append(messages, true);
    assert.deepEqual(list.message_range(2, 30), [{id: 30}]);
    assert.deepEqual(list.message_range(2, 31), [{id: 30}, {id: 40}]);
    assert.deepEqual(list.message_range(30, 40), [{id: 30}, {id: 40}]);
    assert.deepEqual(list.message_range(31, 39), [{id: 40}]);
    assert.deepEqual(list.message_range(31, 1000), [{id: 40}, {id: 50}, {id: 60}]);
    blueslip.expect("error", "message_range given a start of -1");
    assert.deepEqual(list.message_range(-1, 40), [{id: 30}, {id: 40}]);
});

run_test("nth_most_recent_id", () => {
    const list = new MessageList({});
    list.append([{id: 10}, {id: 20}, {id: 30}]);
    assert.equal(list.nth_most_recent_id(1), 30);
    assert.equal(list.nth_most_recent_id(2), 20);
    assert.equal(list.nth_most_recent_id(3), 10);
    assert.equal(list.nth_most_recent_id(4), -1);
});

run_test("change_message_id", () => {
    const list = new MessageList({});
    list.data._add_to_hash([
        {id: 10.5, content: "good job"},
        {id: 20.5, content: "ok!"},
    ]);

    // local to local
    list.change_message_id(10.5, 11.5);
    assert.equal(list.get(11.5).content, "good job");

    list.change_message_id(11.5, 11);
    assert.equal(list.get(11).content, "good job");

    list.change_message_id(20.5, 10);
    assert.equal(list.get(10).content, "ok!");

    // test nonexistent id
    assert.equal(list.change_message_id(13, 15), undefined);
});

run_test("last_sent_by_me", () => {
    const list = new MessageList({});
    const items = [
        {
            id: 1,
            sender_id: 3,
        },
        {
            id: 2,
            sender_id: 3,
        },
        {
            id: 3,
            sender_id: 6,
        },
    ];

    list.append(items);
    page_params.user_id = 3;
    // Look for the last message where user_id == 3 (our ID)
    assert.equal(list.get_last_message_sent_by_me().id, 2);
});

run_test("local_echo", () => {
    let list = new MessageList({});
    list.append([
        {id: 10},
        {id: 20},
        {id: 30},
        {id: 20.02},
        {id: 20.03},
        {id: 40},
        {id: 50},
        {id: 60},
    ]);
    list._local_only = {20.02: {id: 20.02}, 20.03: {id: 20.03}};

    assert.equal(list.closest_id(10), 10);
    assert.equal(list.closest_id(20), 20);
    assert.equal(list.closest_id(30), 30);
    assert.equal(list.closest_id(20.02), 20.02);
    assert.equal(list.closest_id(20.03), 20.03);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(40), 40);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(60), 60);

    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(21), 20);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(31), 30);
    assert.equal(list.closest_id(54), 50);
    assert.equal(list.closest_id(58), 60);

    list = new MessageList({});
    list.append([
        {id: 10},
        {id: 20},
        {id: 30},
        {id: 20.02},
        {id: 20.03},
        {id: 40},
        {id: 50},
        {id: 50.01},
        {id: 50.02},
        {id: 60},
    ]);
    list._local_only = {
        20.02: {id: 20.02},
        20.03: {id: 20.03},
        50.01: {id: 50.01},
        50.02: {id: 50.02},
    };

    assert.equal(list.closest_id(10), 10);
    assert.equal(list.closest_id(20), 20);
    assert.equal(list.closest_id(30), 30);
    assert.equal(list.closest_id(20.02), 20.02);
    assert.equal(list.closest_id(20.03), 20.03);
    assert.equal(list.closest_id(40), 40);
    assert.equal(list.closest_id(50), 50);
    assert.equal(list.closest_id(60), 60);

    assert.equal(list.closest_id(60), 60);
    assert.equal(list.closest_id(21), 20);
    assert.equal(list.closest_id(29), 30);
    assert.equal(list.closest_id(31), 30);
    assert.equal(list.closest_id(47), 50);
    assert.equal(list.closest_id(51), 50.02);
    assert.equal(list.closest_id(59), 60);
    assert.equal(list.closest_id(50.01), 50.01);
});

run_test("bookend", (override) => {
    const list = new MessageList({});

    let expected = "translated: You subscribed to stream IceCream";
    list.view.clear_trailing_bookend = noop;
    list.narrowed = true;

    override(narrow_state, "stream", () => "IceCream");

    let is_subscribed = true;
    let invite_only = false;

    override(stream_data, "is_subscribed", () => is_subscribed);
    override(stream_data, "get_sub", () => ({invite_only}));

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, true);
        assert.equal(bookend.show_button, true);
    }

    expected = "translated: You unsubscribed from stream IceCream";
    list.last_message_historical = false;

    is_subscribed = false;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, true);
    }

    // Test when the stream is privates (invite only)
    expected = "translated: You unsubscribed from stream IceCream";

    invite_only = true;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, false);
    }

    expected = "translated: You are not subscribed to stream IceCream";
    list.last_message_historical = true;

    {
        const stub = make_stub();
        list.view.render_trailing_bookend = stub.f;
        list.update_trailing_bookend();
        assert.equal(stub.num_calls, 1);
        const bookend = stub.get_args("content", "subscribed", "show_button");
        assert.equal(bookend.content, expected);
        assert.equal(bookend.subscribed, false);
        assert.equal(bookend.show_button, true);
    }
});

run_test("add_remove_rerender", () => {
    const filter = accept_all_filter();

    const list = new MessageList({filter});

    const messages = [{id: 1}, {id: 2}, {id: 3}];

    list.add_messages(messages);
    assert.equal(list.num_items(), 3);

    {
        const stub = make_stub();
        list.rerender = stub.f;
        const message_ids = messages.map((msg) => msg.id);
        list.remove_and_rerender(message_ids);
        assert.equal(stub.num_calls, 1);
        assert.equal(list.num_items(), 0);
    }
});
<|endoftext|>


<commit_before>"use strict";

const {strict: assert} = require("assert");

const {set_global, zrequire} = require("../zjsunit/namespace");
const {run_test} = require("../zjsunit/test");

set_global("page_params", {});

const people = zrequire("people");
const {Filter} = zrequire("../js/filter");
const stream_data = zrequire("stream_data");
const narrow_state = zrequire("narrow_state");

function set_filter(operators) {
    operators = operators.map((op) => ({
        operator: op[0],
        operand: op[1],
    }));
    narrow_state.set_current_filter(new Filter(operators));
}

run_test("stream", () => {
    assert.equal(narrow_state.public_operators(), undefined);
    assert(!narrow_state.active());

    const test_stream = {name: "Test", stream_id: 15};
    stream_data.add_sub(test_stream);

    assert(!narrow_state.is_for_stream_id(test_stream.stream_id));

    set_filter([
        ["stream", "Test"],
        ["topic", "Bar"],
        ["search", "yo"],
    ]);
    assert(narrow_state.active());

    assert.equal(narrow_state.stream(), "Test");
    assert.equal(narrow_state.stream_sub().stream_id, test_stream.stream_id);
    assert.equal(narrow_state.topic(), "Bar");
    assert(narrow_state.is_for_stream_id(test_stream.stream_id));

    const expected_operators = [
        {negated: false, operator: "stream", operand: "Test"},
        {negated: false, operator: "topic", operand: "Bar"},
        {negated: false, operator: "search", operand: "yo"},
    ];

    const public_operators = narrow_state.public_operators();
    assert.deepEqual(public_operators, expected_operators);
    assert.equal(narrow_state.search_string(), "stream:Test topic:Bar yo");
});

run_test("narrowed", () => {
    narrow_state.reset_current_filter(); // not narrowed, basically
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert.equal(narrow_state.stream_sub(), undefined);
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["stream", "Foo"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["pm-with", "steve@zulip.com"]]);
    assert(narrow_state.narrowed_to_pms());
    assert(narrow_state.narrowed_by_reply());
    assert(narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([
        ["stream", "Foo"],
        ["topic", "bar"],
    ]);
    assert(!narrow_state.narrowed_to_pms());
    assert(narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["search", "grail"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["is", "starred"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(narrow_state.narrowed_to_starred());
});

run_test("operators", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
        ["search", "Yo"],
    ]);
    let result = narrow_state.operators();
    assert.equal(result.length, 3);
    assert.equal(result[0].operator, "stream");
    assert.equal(result[0].operand, "Foo");

    assert.equal(result[1].operator, "topic");
    assert.equal(result[1].operand, "Bar");

    assert.equal(result[2].operator, "search");
    assert.equal(result[2].operand, "yo");

    narrow_state.reset_current_filter();
    result = narrow_state.operators();
    assert.equal(result.length, 0);
});

run_test("excludes_muted_topics", () => {
    set_filter([["stream", "devel"]]);
    assert(narrow_state.excludes_muted_topics());

    narrow_state.reset_current_filter(); // not narrowed, basically
    assert(narrow_state.excludes_muted_topics());

    set_filter([
        ["stream", "devel"],
        ["topic", "mac"],
    ]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["search", "whatever"]]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["is", "private"]]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["is", "starred"]]);
    assert(!narrow_state.excludes_muted_topics());
});

run_test("set_compose_defaults", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);

    const stream_and_subject = narrow_state.set_compose_defaults();
    assert.equal(stream_and_subject.stream, "Foo");
    assert.equal(stream_and_subject.topic, "Bar");

    set_filter([["pm-with", "foo@bar.com"]]);
    let pm_test = narrow_state.set_compose_defaults();
    assert.equal(pm_test.private_message_recipient, undefined);

    const john = {
        email: "john@doe.com",
        user_id: 57,
        full_name: "John Doe",
    };
    people.add_active_user(john);
    people.add_active_user(john);

    set_filter([["pm-with", "john@doe.com"]]);
    pm_test = narrow_state.set_compose_defaults();
    assert.equal(pm_test.private_message_recipient, "john@doe.com");

    set_filter([
        ["topic", "duplicate"],
        ["topic", "duplicate"],
    ]);
    assert.deepEqual(narrow_state.set_compose_defaults(), {});

    stream_data.add_sub({name: "ROME", stream_id: 99});
    set_filter([["stream", "rome"]]);

    const stream_test = narrow_state.set_compose_defaults();
    assert.equal(stream_test.stream, "ROME");
});

run_test("update_email", () => {
    const steve = {
        email: "steve@foo.com",
        user_id: 43,
        full_name: "Steve",
    };

    people.add_active_user(steve);
    set_filter([
        ["pm-with", "steve@foo.com"],
        ["sender", "steve@foo.com"],
        ["stream", "steve@foo.com"], // try to be tricky
    ]);
    narrow_state.update_email(steve.user_id, "showell@foo.com");
    const filter = narrow_state.filter();
    assert.deepEqual(filter.operands("pm-with"), ["showell@foo.com"]);
    assert.deepEqual(filter.operands("sender"), ["showell@foo.com"]);
    assert.deepEqual(filter.operands("stream"), ["steve@foo.com"]);
});

run_test("topic", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.topic(), "Bar");

    set_filter([
        ["stream", "release"],
        ["topic", "@#$$^test"],
    ]);
    assert.equal(narrow_state.topic(), "@#$$^test");

    set_filter([]);
    assert.equal(narrow_state.topic(), undefined);

    set_filter([
        ["sender", "test@foo.com"],
        ["pm-with", "test@foo.com"],
    ]);
    assert.equal(narrow_state.topic(), undefined);

    narrow_state.set_current_filter(undefined);
    assert.equal(narrow_state.topic(), undefined);
});

run_test("stream", () => {
    set_filter([]);
    assert.equal(narrow_state.stream(), undefined);
    assert.equal(narrow_state.stream_sub(), undefined);

    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.stream(), "Foo");
    assert.equal(narrow_state.stream_sub(), undefined);

    const sub = {name: "Foo", stream_id: 55};
    stream_data.add_sub(sub);
    assert.deepEqual(narrow_state.stream_sub(), sub);

    set_filter([
        ["sender", "someone"],
        ["topic", "random"],
    ]);
    assert.equal(narrow_state.stream(), undefined);
});

run_test("pm_string", () => {
    // This function will return undefined unless we're clearly
    // narrowed to a specific PM (including huddles) with real
    // users.
    narrow_state.set_current_filter(undefined);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([["pm-with", ""]]);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([["pm-with", "bogus@foo.com"]]);
    assert.equal(narrow_state.pm_string(), undefined);

    const alice = {
        email: "alice@foo.com",
        user_id: 444,
        full_name: "Alice",
    };

    const bob = {
        email: "bob@foo.com",
        user_id: 555,
        full_name: "Bob",
    };

    people.add_active_user(alice);
    people.add_active_user(bob);

    set_filter([["pm-with", "bob@foo.com,alice@foo.com"]]);
    assert.equal(narrow_state.pm_string(), "444,555");
});
<commit_msg>node tests: Clear data for narrow_state tests.<commit_after>"use strict";

const {strict: assert} = require("assert");

const {set_global, zrequire} = require("../zjsunit/namespace");
const {run_test} = require("../zjsunit/test");

set_global("page_params", {});

const people = zrequire("people");
const {Filter} = zrequire("../js/filter");
const stream_data = zrequire("stream_data");
const narrow_state = zrequire("narrow_state");

function set_filter(operators) {
    operators = operators.map((op) => ({
        operator: op[0],
        operand: op[1],
    }));
    narrow_state.set_current_filter(new Filter(operators));
}

function test(label, f) {
    run_test(label, (override) => {
        narrow_state.reset_current_filter();
        stream_data.clear_subscriptions();
        f(override);
    });
}

test("stream", () => {
    assert.equal(narrow_state.public_operators(), undefined);
    assert(!narrow_state.active());

    const test_stream = {name: "Test", stream_id: 15};
    stream_data.add_sub(test_stream);

    assert(!narrow_state.is_for_stream_id(test_stream.stream_id));

    set_filter([
        ["stream", "Test"],
        ["topic", "Bar"],
        ["search", "yo"],
    ]);
    assert(narrow_state.active());

    assert.equal(narrow_state.stream(), "Test");
    assert.equal(narrow_state.stream_sub().stream_id, test_stream.stream_id);
    assert.equal(narrow_state.topic(), "Bar");
    assert(narrow_state.is_for_stream_id(test_stream.stream_id));

    const expected_operators = [
        {negated: false, operator: "stream", operand: "Test"},
        {negated: false, operator: "topic", operand: "Bar"},
        {negated: false, operator: "search", operand: "yo"},
    ];

    const public_operators = narrow_state.public_operators();
    assert.deepEqual(public_operators, expected_operators);
    assert.equal(narrow_state.search_string(), "stream:Test topic:Bar yo");
});

test("narrowed", () => {
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert.equal(narrow_state.stream_sub(), undefined);
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["stream", "Foo"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["pm-with", "steve@zulip.com"]]);
    assert(narrow_state.narrowed_to_pms());
    assert(narrow_state.narrowed_by_reply());
    assert(narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([
        ["stream", "Foo"],
        ["topic", "bar"],
    ]);
    assert(!narrow_state.narrowed_to_pms());
    assert(narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["search", "grail"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(!narrow_state.narrowed_to_starred());

    set_filter([["is", "starred"]]);
    assert(!narrow_state.narrowed_to_pms());
    assert(!narrow_state.narrowed_by_reply());
    assert(!narrow_state.narrowed_by_pm_reply());
    assert(!narrow_state.narrowed_by_topic_reply());
    assert(!narrow_state.narrowed_to_search());
    assert(!narrow_state.narrowed_to_topic());
    assert(!narrow_state.narrowed_by_stream_reply());
    assert(narrow_state.narrowed_to_starred());
});

test("operators", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
        ["search", "Yo"],
    ]);
    let result = narrow_state.operators();
    assert.equal(result.length, 3);
    assert.equal(result[0].operator, "stream");
    assert.equal(result[0].operand, "Foo");

    assert.equal(result[1].operator, "topic");
    assert.equal(result[1].operand, "Bar");

    assert.equal(result[2].operator, "search");
    assert.equal(result[2].operand, "yo");

    narrow_state.reset_current_filter();
    result = narrow_state.operators();
    assert.equal(result.length, 0);
});

test("excludes_muted_topics", () => {
    set_filter([["stream", "devel"]]);
    assert(narrow_state.excludes_muted_topics());

    narrow_state.reset_current_filter(); // not narrowed, basically
    assert(narrow_state.excludes_muted_topics());

    set_filter([
        ["stream", "devel"],
        ["topic", "mac"],
    ]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["search", "whatever"]]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["is", "private"]]);
    assert(!narrow_state.excludes_muted_topics());

    set_filter([["is", "starred"]]);
    assert(!narrow_state.excludes_muted_topics());
});

test("set_compose_defaults", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);

    const stream_and_subject = narrow_state.set_compose_defaults();
    assert.equal(stream_and_subject.stream, "Foo");
    assert.equal(stream_and_subject.topic, "Bar");

    set_filter([["pm-with", "foo@bar.com"]]);
    let pm_test = narrow_state.set_compose_defaults();
    assert.equal(pm_test.private_message_recipient, undefined);

    const john = {
        email: "john@doe.com",
        user_id: 57,
        full_name: "John Doe",
    };
    people.add_active_user(john);
    people.add_active_user(john);

    set_filter([["pm-with", "john@doe.com"]]);
    pm_test = narrow_state.set_compose_defaults();
    assert.equal(pm_test.private_message_recipient, "john@doe.com");

    set_filter([
        ["topic", "duplicate"],
        ["topic", "duplicate"],
    ]);
    assert.deepEqual(narrow_state.set_compose_defaults(), {});

    stream_data.add_sub({name: "ROME", stream_id: 99});
    set_filter([["stream", "rome"]]);

    const stream_test = narrow_state.set_compose_defaults();
    assert.equal(stream_test.stream, "ROME");
});

test("update_email", () => {
    const steve = {
        email: "steve@foo.com",
        user_id: 43,
        full_name: "Steve",
    };

    people.add_active_user(steve);
    set_filter([
        ["pm-with", "steve@foo.com"],
        ["sender", "steve@foo.com"],
        ["stream", "steve@foo.com"], // try to be tricky
    ]);
    narrow_state.update_email(steve.user_id, "showell@foo.com");
    const filter = narrow_state.filter();
    assert.deepEqual(filter.operands("pm-with"), ["showell@foo.com"]);
    assert.deepEqual(filter.operands("sender"), ["showell@foo.com"]);
    assert.deepEqual(filter.operands("stream"), ["steve@foo.com"]);
});

test("topic", () => {
    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.topic(), "Bar");

    set_filter([
        ["stream", "release"],
        ["topic", "@#$$^test"],
    ]);
    assert.equal(narrow_state.topic(), "@#$$^test");

    set_filter([]);
    assert.equal(narrow_state.topic(), undefined);

    set_filter([
        ["sender", "test@foo.com"],
        ["pm-with", "test@foo.com"],
    ]);
    assert.equal(narrow_state.topic(), undefined);

    narrow_state.set_current_filter(undefined);
    assert.equal(narrow_state.topic(), undefined);
});

test("stream_sub", () => {
    set_filter([]);
    assert.equal(narrow_state.stream(), undefined);
    assert.equal(narrow_state.stream_sub(), undefined);

    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.stream(), "Foo");
    assert.equal(narrow_state.stream_sub(), undefined);

    const sub = {name: "Foo", stream_id: 55};
    stream_data.add_sub(sub);
    assert.deepEqual(narrow_state.stream_sub(), sub);

    set_filter([
        ["sender", "someone"],
        ["topic", "random"],
    ]);
    assert.equal(narrow_state.stream(), undefined);
});

test("pm_string", () => {
    // This function will return undefined unless we're clearly
    // narrowed to a specific PM (including huddles) with real
    // users.
    narrow_state.set_current_filter(undefined);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([
        ["stream", "Foo"],
        ["topic", "Bar"],
    ]);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([["pm-with", ""]]);
    assert.equal(narrow_state.pm_string(), undefined);

    set_filter([["pm-with", "bogus@foo.com"]]);
    assert.equal(narrow_state.pm_string(), undefined);

    const alice = {
        email: "alice@foo.com",
        user_id: 444,
        full_name: "Alice",
    };

    const bob = {
        email: "bob@foo.com",
        user_id: 555,
        full_name: "Bob",
    };

    people.add_active_user(alice);
    people.add_active_user(bob);

    set_filter([["pm-with", "bob@foo.com,alice@foo.com"]]);
    assert.equal(narrow_state.pm_string(), "444,555");
});
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var PROJECT_ID = ''          // Required - your Firebase project ID
var ALGOLIA_APP_ID = '';     // Required - your Algolia app ID
var ALGOLIA_SEARCH_KEY = ''; // Optional - Only used for unauthenticated search

function unauthenticated_search(query) {

  // [START search_index_unsecure]
  var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY);
  var index = client.initIndex('notes');

  // Perform an Algolia search:
  // https://www.algolia.com/doc/api-reference/api-methods/search/
  index
    .seach({
      query
    })
    .then(function(responses) {
      // Response from Algolia:
      // https://www.algolia.com/doc/api-reference/api-methods/search/#response-format
      console.log(responses.hits);
    });
  // [END search_index_unsecure]
}

function authenticated_search(query) {
  var client;
  var index;
  // [START search_index_secure]
  // Use Firebase Authentication to request the underlying token
  return firebase.auth().currentUser.getIdToken()
    .then(function(token) {
      // The token is then passed to our getSearchKey Cloud Function
      return fetch('https://us-central1-' + PROJECT_ID + '.cloudfunctions.net/getSearchKey/', {
          headers: { Authorization: 'Bearer ' + token }
      });
    })
    .then(function(response) {
      // The Fetch API returns a stream, which we convert into a JSON object.
      return response.json();
    })
    .then(function(data) {
      // Data will contain the restricted key in the `key` field.
      client = algoliasearch(ALGOLIA_APP_ID, data.key);
      index = client.initIndex('notes');

      // Perform the search as usual.
      return index.search({query});
    })
    .then(function(responses) {
      // Finally, use the search 'hits' returned from Algolia.
      return responses.hits;
    });
  // [END search_index_secure]
}

function search(query) {
  if (!PROJECT_ID) {
    console.warn('Please set PROJECT_ID in /index.js!');
  } else if (!ALGOLIA_APP_ID) {
    console.warn('Please set ALGOLIA_APP_ID in /index.js!');
  } else if (ALGOLIA_SEARCH_KEY) {
    console.log('Performing unauthenticated search...');
    return unauthenticated_search(query);
  } else {
    return firebase.auth().signInAnonymously()
      .then(function() {
        return authenticated_search(query).catch(function(err) {
          console.warn(err);
        });
      }).catch(function(err) {
        console.warn(err);
        console.warn('Please enable Anonymous Authentication in your Firebase Project!');
      });
  }
}

// Other code to wire up the buttons and textboxes.

document.querySelector('#do-add-note').addEventListener('click', function() {
  firebase.firestore().collection('notes').add({
    author: [firebase.auth().currentUser.uid],
    text: document.querySelector('#note-text').value
  }).then(function() {
    document.querySelector('#note-text').value = '';
  });
});

document.querySelector('#do-query').addEventListener('click', function() {
  search(document.querySelector('#query-text').value).then(function(hits) {
    document.querySelector('#results').innerHTML = JSON.stringify(hits, null, 2);
  });
});
<commit_msg>Update index.js<commit_after>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var PROJECT_ID = ''          // Required - your Firebase project ID
var ALGOLIA_APP_ID = '';     // Required - your Algolia app ID
var ALGOLIA_SEARCH_KEY = ''; // Optional - Only used for unauthenticated search

function unauthenticated_search(query) {

  // [START search_index_unsecure]
  var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY);
  var index = client.initIndex('notes');

  // Perform an Algolia search:
  // https://www.algolia.com/doc/api-reference/api-methods/search/
  index
    .search({
      query
    })
    .then(function(responses) {
      // Response from Algolia:
      // https://www.algolia.com/doc/api-reference/api-methods/search/#response-format
      console.log(responses.hits);
    });
  // [END search_index_unsecure]
}

function authenticated_search(query) {
  var client;
  var index;
  // [START search_index_secure]
  // Use Firebase Authentication to request the underlying token
  return firebase.auth().currentUser.getIdToken()
    .then(function(token) {
      // The token is then passed to our getSearchKey Cloud Function
      return fetch('https://us-central1-' + PROJECT_ID + '.cloudfunctions.net/getSearchKey/', {
          headers: { Authorization: 'Bearer ' + token }
      });
    })
    .then(function(response) {
      // The Fetch API returns a stream, which we convert into a JSON object.
      return response.json();
    })
    .then(function(data) {
      // Data will contain the restricted key in the `key` field.
      client = algoliasearch(ALGOLIA_APP_ID, data.key);
      index = client.initIndex('notes');

      // Perform the search as usual.
      return index.search({query});
    })
    .then(function(responses) {
      // Finally, use the search 'hits' returned from Algolia.
      return responses.hits;
    });
  // [END search_index_secure]
}

function search(query) {
  if (!PROJECT_ID) {
    console.warn('Please set PROJECT_ID in /index.js!');
  } else if (!ALGOLIA_APP_ID) {
    console.warn('Please set ALGOLIA_APP_ID in /index.js!');
  } else if (ALGOLIA_SEARCH_KEY) {
    console.log('Performing unauthenticated search...');
    return unauthenticated_search(query);
  } else {
    return firebase.auth().signInAnonymously()
      .then(function() {
        return authenticated_search(query).catch(function(err) {
          console.warn(err);
        });
      }).catch(function(err) {
        console.warn(err);
        console.warn('Please enable Anonymous Authentication in your Firebase Project!');
      });
  }
}

// Other code to wire up the buttons and textboxes.

document.querySelector('#do-add-note').addEventListener('click', function() {
  firebase.firestore().collection('notes').add({
    author: [firebase.auth().currentUser.uid],
    text: document.querySelector('#note-text').value
  }).then(function() {
    document.querySelector('#note-text').value = '';
  });
});

document.querySelector('#do-query').addEventListener('click', function() {
  search(document.querySelector('#query-text').value).then(function(hits) {
    document.querySelector('#results').innerHTML = JSON.stringify(hits, null, 2);
  });
});
<|endoftext|>


<commit_before>"use strict";

var prefixed = require("cl-strings").getCompiler("{green:[}BS{green:]}");
var compile  = require("cl-strings").compile;
var path     = require("path");

var _        = require("lodash");
var utils    = require("./utils").utils;

var messages = {
    /**
     * @param {Object} port
     * @param {Object} options
     * @returns {String}
     */
    initSnippet: function (port, options) {

        var template = "{green:Copy the following snippet into your website, just before the closing}  {green:tag}\n{:tags:}";

        return prefixed(template, {
            tags: this.scriptTags(port, options)
        });
    },
    tunnel: function (url) {
        var templates = [
            "{green:Tunnel running...}",
            "{green:You can access it through the following address:}\n",
            ">>> {magenta:{:url:}}"
        ];

        return prefixed(templates, {url: url});
    },
    server: {
        /**
         * Conflicting options
         * @returns {String}
         */
        withProxy: function () {
            var template = "{red:Invalid config. You cannot specify both a server & proxy option.}";
            return prefixed(template);
        }
    },
    /**
     * @param {Object} options
     * @returns {String}
     */
    initServer: function (options) {

        var output = "";
        var baseDir = options.server ? options.server.baseDir : undefined;

        output += this.getUrls(options.urls);

        if (baseDir) {
            if (Array.isArray(baseDir)) {
                baseDir.forEach(addBase);
            } else {
                addBase(baseDir);
            }
        }

        function addBase (value) {
            output += prefixed("{green:Serving files from:} {magenta:{: baseDir :}}\n", {
                baseDir: value
            });
        }

        return output;
    },
    /**
     * @param {Object} options
     * @returns {String}
     */
    initProxy: function (options) {

        var output   = "";
        var target   = options.proxy.target;

        var templates = [
            "{green:Proxying:} {:proxy:}",
            "{green:Now you can access your site through the following addresses:}\n"
        ];

        output += prefixed(templates, {proxy: target});

        output += this.getUrls(options.urls);

        return output;
    },
    plugin: {
        error: function () {
            return "Your plugin must be a function";
        }
    },
    /**
     * Helper for creating a URL
     * @param {String} host
     * @param {String|Number} port
     * @param {String} [protocol]
     * @returns {String}
     */
    _makeUrl: function (host, port, protocol) {

        var string = "//{: host :}:{: port :}";

        if (protocol) {
            string = protocol + string;
        }

        return compile(string, {host: host, port: port});
    },
    /**
     * @param {Object} options
     * @param {Number} port
     * @returns {String}
     */
    scriptTags: function (port, options) {

        var template = [
            "\n"
        ];

        var script = "//HOST:PORT" + this.clientScript(options);

        if (options.tunnel) {
            script = this.clientScript(options);
        }

        var params = {
            custom: script
        };

        return compile(template.join("") + "\n", params);
    },
    host: {
        /**
         * Warn about possible problems with multi hosts
         * @returns {String}
         */
        multiple: function () {

            var template = [];
            template.push("Warning: Multiple External IP addresses found");
            template.push("If you have problems, you may need to manually set the 'host' option");

            return prefixed(template);
        }
    },
    /**
     * @returns {String}
     */
    invalidBaseDir: function () {

        var template = "{cyan:Invalid Base Directory path for server. Should be like this ( baseDir: 'path/to/app' )}";

        return prefixed(template);
    },
    ports: {
        /**
         * @param {Number} minCount
         * @returns {String}
         */
        invalid: function (minCount) {
            var template = "{red:Invalid port range!} - At least {:minCount:} required!";
            var params   = {
                minCount: minCount
            };

            return prefixed(template, params);
        }
    },
    config: {
        /**
         * @param {String} filePath
         * @returns {String}
         */
        confirm: function (filePath) {
            var template1  = "Config file created ({cyan:{:path:}})";
            var template2  = "To use it, in the same directory run: {green:browser-sync start --config bs-config.js}";

            var params = {
                "path": path.basename(filePath)
            };

            return prefixed([template1, template2], params);
        }
    },
    files: {
        /**
         * @param {Array} [patterns]
         * @returns {String}
         */
        watching: function (patterns) {

            var string;
            if (Array.isArray(patterns) && patterns.length) {
                string = prefixed("{green:Watching files...}");
            } else {
                string = prefixed("{red:Not watching any files...}");
            }
            return string;
        },
        /**
         * @param {String} fileName
         * @returns {String}
         */
        changed: function (fileName) {
            var string = "{green:File Changed: }{magenta:{: path :}}";
            return prefixed(string, {
                path: path.basename(fileName)
            });
        }
    },
    browser: {
        /**
         * @returns {String}
         */
        reload: function () {
            var string = "{cyan:Reloading all connected browsers...}";
            return prefixed(string);
        },
        /**
         * @returns {String}
         */
        inject: function () {
            var string = "{cyan:Injecting file into all connected browsers...}";
            return prefixed(string);
        },
        /**
         * @param {Object} browser
         * @param {Boolean} [prefixed]
         * @returns {String}
         */
        connection: function (browser, prefix) {

            var template = "{cyan:Browser Connected! ({:name:}, version: {:version:})}";
            var params = {
                name: browser.name,
                version: browser.version
            };

            if (!prefix) {
                return compile(template, params);
            }

            return prefixed(template, params);
        }
    },
    /**
     * @param {String} url
     * @returns {*}
     */
    location: function (url) {
        var template = "{green:Link clicked! Redirecting all browsers to }{magenta:{: url :}}";
        var params = {
            url: url
        };
        return prefixed(template, params);
    },

    initBrowserSyncGlobal: function() {
        return "window.___browserSync___ = {};";
    },
    /**
     * @param {number} port
     * @param {Object} options
     * @returns {String}
     */
    socketConnector: function (port, options) {
        // Cleanup global io
        var template = "___browserSync___.io = window.io; delete window.io;";
        // Workaround cl-strings compiling JS objects.
        template += "___browserSync___.socketConfig = {}; ___browserSync___.socketConfig.path = '{:path:}';";
        // Add socket using BrowserSync's io & config.
        template += "___browserSync___.socket = ___browserSync___.io('{:namespace:}', ___browserSync___.socketConfig);";

        var socketConfig = options.socket;
        var path = socketConfig && socketConfig.path || "/browser-sync/socket.io";
        var namespace = socketConfig && socketConfig.namespace || "/browser-sync";

        var params = {
            port: port,
            path: path,
            namespace: namespace
        };
        // compiled template depends on ___browserSync___ global.
        return this.initBrowserSyncGlobal() + compile(template, params);
    },
    /**
     * @param {Object} [options]
     * @param {Boolean} [both]
     */
    clientScript: function (options, both) {

        var prefix = options.socket && options.socket.clientPath || "/browser-sync";
        var script = prefix + "/browser-sync-client.js";
        var template = prefix + "/browser-sync-client.{:version:}.js";

        if (!options || !options.version) {
            return script;
        }

        var params = {
            version: options.version
        };

        var compiled = compile(template, params);

        if (both) {
            return {
                path: script,
                versioned: compiled
            };
        }

        return compiled;
    },
    stream: {
        once: function () {
            return prefixed("{cyan:Reloading Browsers}");
        },
        multi: function (changed) {
            return prefixed("{cyan:Reloading {:count:} file{:s:} ({:files:})}", {
                count: changed.length,
                s: changed.length > 1 ? "s" : "",
                files: changed.join(", ")
            });
        }
    },
    proxyError: function () {
        return messages.getPrefixedError("Proxy address not reachable. Is your server running?");
    },
    getUrls: function (urls) {
        var output = "";
        _.each(urls, function (value, key) {
            output += prefixed("{green:{:name:}:} >>> {magenta:{:url:}}\n", {
                name: utils.ucfirst(key),
                url: value
            });
        });
        return output;
    },
    exit: function () {
        return compile("{cyan:Exiting...}");
    },
    tunnelFail: function (err) {
        return prefixed(["The following is a Tunnel error:", this.getErr(err)]);
    },
    configError: function (msg) {
        return prefixed("{red:CONFIG ERROR: %s }".replace("%s", msg));
    },
    getPrefixedError: function (msg) {
        return prefixed("{red:[ERROR]} %s".replace("%s", msg));
    },
    getErr: function (msg) {
        return "{red:[ERROR] %s }".replace("%s", msg);
    },
    getDebug: function (msg) {
        return prefixed("{cyan:[DEBUG]: %s }".replace("%s", msg));
    },
    getPrefix: function (prefix) {

        var string = "";

        if (_.isString(prefix)) {
            string = "{green:[}%s{green:]} ".replace("%s", prefix);
        }

        return compile(string);
    },
    getNotifyPrefix: function (prefix) {

        var string = "";

        if (_.isString(prefix)) {
            string = "%s: ".replace("%s", prefix);
        }

        return string;
    },
    compile: compile
};

module.exports = messages;
<commit_msg>wrap delete of window property in try catch for ie8<commit_after>"use strict";

var prefixed = require("cl-strings").getCompiler("{green:[}BS{green:]}");
var compile  = require("cl-strings").compile;
var path     = require("path");

var _        = require("lodash");
var utils    = require("./utils").utils;

var messages = {
    /**
     * @param {Object} port
     * @param {Object} options
     * @returns {String}
     */
    initSnippet: function (port, options) {

        var template = "{green:Copy the following snippet into your website, just before the closing}  {green:tag}\n{:tags:}";

        return prefixed(template, {
            tags: this.scriptTags(port, options)
        });
    },
    tunnel: function (url) {
        var templates = [
            "{green:Tunnel running...}",
            "{green:You can access it through the following address:}\n",
            ">>> {magenta:{:url:}}"
        ];

        return prefixed(templates, {url: url});
    },
    server: {
        /**
         * Conflicting options
         * @returns {String}
         */
        withProxy: function () {
            var template = "{red:Invalid config. You cannot specify both a server & proxy option.}";
            return prefixed(template);
        }
    },
    /**
     * @param {Object} options
     * @returns {String}
     */
    initServer: function (options) {

        var output = "";
        var baseDir = options.server ? options.server.baseDir : undefined;

        output += this.getUrls(options.urls);

        if (baseDir) {
            if (Array.isArray(baseDir)) {
                baseDir.forEach(addBase);
            } else {
                addBase(baseDir);
            }
        }

        function addBase (value) {
            output += prefixed("{green:Serving files from:} {magenta:{: baseDir :}}\n", {
                baseDir: value
            });
        }

        return output;
    },
    /**
     * @param {Object} options
     * @returns {String}
     */
    initProxy: function (options) {

        var output   = "";
        var target   = options.proxy.target;

        var templates = [
            "{green:Proxying:} {:proxy:}",
            "{green:Now you can access your site through the following addresses:}\n"
        ];

        output += prefixed(templates, {proxy: target});

        output += this.getUrls(options.urls);

        return output;
    },
    plugin: {
        error: function () {
            return "Your plugin must be a function";
        }
    },
    /**
     * Helper for creating a URL
     * @param {String} host
     * @param {String|Number} port
     * @param {String} [protocol]
     * @returns {String}
     */
    _makeUrl: function (host, port, protocol) {

        var string = "//{: host :}:{: port :}";

        if (protocol) {
            string = protocol + string;
        }

        return compile(string, {host: host, port: port});
    },
    /**
     * @param {Object} options
     * @param {Number} port
     * @returns {String}
     */
    scriptTags: function (port, options) {

        var template = [
            "\n"
        ];

        var script = "//HOST:PORT" + this.clientScript(options);

        if (options.tunnel) {
            script = this.clientScript(options);
        }

        var params = {
            custom: script
        };

        return compile(template.join("") + "\n", params);
    },
    host: {
        /**
         * Warn about possible problems with multi hosts
         * @returns {String}
         */
        multiple: function () {

            var template = [];
            template.push("Warning: Multiple External IP addresses found");
            template.push("If you have problems, you may need to manually set the 'host' option");

            return prefixed(template);
        }
    },
    /**
     * @returns {String}
     */
    invalidBaseDir: function () {

        var template = "{cyan:Invalid Base Directory path for server. Should be like this ( baseDir: 'path/to/app' )}";

        return prefixed(template);
    },
    ports: {
        /**
         * @param {Number} minCount
         * @returns {String}
         */
        invalid: function (minCount) {
            var template = "{red:Invalid port range!} - At least {:minCount:} required!";
            var params   = {
                minCount: minCount
            };

            return prefixed(template, params);
        }
    },
    config: {
        /**
         * @param {String} filePath
         * @returns {String}
         */
        confirm: function (filePath) {
            var template1  = "Config file created ({cyan:{:path:}})";
            var template2  = "To use it, in the same directory run: {green:browser-sync start --config bs-config.js}";

            var params = {
                "path": path.basename(filePath)
            };

            return prefixed([template1, template2], params);
        }
    },
    files: {
        /**
         * @param {Array} [patterns]
         * @returns {String}
         */
        watching: function (patterns) {

            var string;
            if (Array.isArray(patterns) && patterns.length) {
                string = prefixed("{green:Watching files...}");
            } else {
                string = prefixed("{red:Not watching any files...}");
            }
            return string;
        },
        /**
         * @param {String} fileName
         * @returns {String}
         */
        changed: function (fileName) {
            var string = "{green:File Changed: }{magenta:{: path :}}";
            return prefixed(string, {
                path: path.basename(fileName)
            });
        }
    },
    browser: {
        /**
         * @returns {String}
         */
        reload: function () {
            var string = "{cyan:Reloading all connected browsers...}";
            return prefixed(string);
        },
        /**
         * @returns {String}
         */
        inject: function () {
            var string = "{cyan:Injecting file into all connected browsers...}";
            return prefixed(string);
        },
        /**
         * @param {Object} browser
         * @param {Boolean} [prefixed]
         * @returns {String}
         */
        connection: function (browser, prefix) {

            var template = "{cyan:Browser Connected! ({:name:}, version: {:version:})}";
            var params = {
                name: browser.name,
                version: browser.version
            };

            if (!prefix) {
                return compile(template, params);
            }

            return prefixed(template, params);
        }
    },
    /**
     * @param {String} url
     * @returns {*}
     */
    location: function (url) {
        var template = "{green:Link clicked! Redirecting all browsers to }{magenta:{: url :}}";
        var params = {
            url: url
        };
        return prefixed(template, params);
    },

    initBrowserSyncGlobal: function() {
        return "window.___browserSync___ = {};";
    },
    /**
     * @param {number} port
     * @param {Object} options
     * @returns {String}
     */
    socketConnector: function (port, options) {
        // Cleanup global io
        var template = "___browserSync___.io = window.io; try{delete window.io;}catch(err){window.io=undefined;}";
        // Workaround cl-strings compiling JS objects.
        template += "___browserSync___.socketConfig = {}; ___browserSync___.socketConfig.path = '{:path:}';";
        // Add socket using BrowserSync's io & config.
        template += "___browserSync___.socket = ___browserSync___.io('{:namespace:}', ___browserSync___.socketConfig);";

        var socketConfig = options.socket;
        var path = socketConfig && socketConfig.path || "/browser-sync/socket.io";
        var namespace = socketConfig && socketConfig.namespace || "/browser-sync";

        var params = {
            port: port,
            path: path,
            namespace: namespace
        };
        // compiled template depends on ___browserSync___ global.
        return this.initBrowserSyncGlobal() + compile(template, params);
    },
    /**
     * @param {Object} [options]
     * @param {Boolean} [both]
     */
    clientScript: function (options, both) {

        var prefix = options.socket && options.socket.clientPath || "/browser-sync";
        var script = prefix + "/browser-sync-client.js";
        var template = prefix + "/browser-sync-client.{:version:}.js";

        if (!options || !options.version) {
            return script;
        }

        var params = {
            version: options.version
        };

        var compiled = compile(template, params);

        if (both) {
            return {
                path: script,
                versioned: compiled
            };
        }

        return compiled;
    },
    stream: {
        once: function () {
            return prefixed("{cyan:Reloading Browsers}");
        },
        multi: function (changed) {
            return prefixed("{cyan:Reloading {:count:} file{:s:} ({:files:})}", {
                count: changed.length,
                s: changed.length > 1 ? "s" : "",
                files: changed.join(", ")
            });
        }
    },
    proxyError: function () {
        return messages.getPrefixedError("Proxy address not reachable. Is your server running?");
    },
    getUrls: function (urls) {
        var output = "";
        _.each(urls, function (value, key) {
            output += prefixed("{green:{:name:}:} >>> {magenta:{:url:}}\n", {
                name: utils.ucfirst(key),
                url: value
            });
        });
        return output;
    },
    exit: function () {
        return compile("{cyan:Exiting...}");
    },
    tunnelFail: function (err) {
        return prefixed(["The following is a Tunnel error:", this.getErr(err)]);
    },
    configError: function (msg) {
        return prefixed("{red:CONFIG ERROR: %s }".replace("%s", msg));
    },
    getPrefixedError: function (msg) {
        return prefixed("{red:[ERROR]} %s".replace("%s", msg));
    },
    getErr: function (msg) {
        return "{red:[ERROR] %s }".replace("%s", msg);
    },
    getDebug: function (msg) {
        return prefixed("{cyan:[DEBUG]: %s }".replace("%s", msg));
    },
    getPrefix: function (prefix) {

        var string = "";

        if (_.isString(prefix)) {
            string = "{green:[}%s{green:]} ".replace("%s", prefix);
        }

        return compile(string);
    },
    getNotifyPrefix: function (prefix) {

        var string = "";

        if (_.isString(prefix)) {
            string = "%s: ".replace("%s", prefix);
        }

        return string;
    },
    compile: compile
};

module.exports = messages;
<|endoftext|>