<commit_before>var idCounter=0;
function generateId() {
	return 'webgl-starter-id-'+(idCounter++);
}

var Options=require('./options.js');
var generateCode=require('./code.js');

function getHtmlDataUri(html) {
	// with base64: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/btoa
	//return "data:text/html;charset=utf-8;base64,"+window.btoa(unescape(encodeURIComponent(str)));
	// without base64: https://en.wikipedia.org/wiki/Data_URI_scheme
	return 'data:text/html;charset=utf-8,'+encodeURIComponent(html);
}

var i18n=function(id){ // fake temporary i18n
	return {
		'message.hljs': "highlight.js (hosted on cdnjs.cloudflare.com) is not loaded. Syntax highlighting is disabled.",

		'options.general': 'General options',
		'options.background': 'Background',
		'options.background.none': 'None (transparent)',
		'options.background.solid': 'Solid color',
		'options.shader': 'Shader',
		'options.shader.single': 'Single color',
		'options.shader.vertex': 'One color per vertex',
		'options.shader.face': 'One color per face',
		'options.shape': 'Shape to draw',
		'options.shape.square': 'Square',
		'options.shape.triangle': 'Triangle',
		'options.shape.gasket': 'Sierpinski gasket', // wp: Sierpinski triangle
		'options.shape.cube': 'Cube',

		'options.input': 'Input options',
		'options.background.solid.color.r': 'Background color red component',
		'options.background.solid.color.g': 'Background color green component',
		'options.background.solid.color.b': 'Background color blue component',
		'options.background.solid.color.a': 'Background color alpha component',
		'options.shader.single.color.r': 'Fragment color red component',
		'options.shader.single.color.g': 'Fragment color green component',
		'options.shader.single.color.b': 'Fragment color blue component',
		'options.shader.single.color.a': 'Fragment color alpha component',
		'options.shape.gasket.depth': 'Sierpinski gasket recursion depth',
		'options.animation.rotation.speed': 'Z axis rotation speed',
		'options.*.input': 'This value is',
		'options.*.input.constant': 'kept constant',
		'options.*.input.slider': 'updated with a slider',
		'options.*.input.mousemovex': 'updated by moving the mouse horizontally',
		'options.*.input.mousemovey': 'updated by moving the mouse vertically',
		'options.*.input.animated': 'animated',

		'options.transform': 'Transforms',
		'options.rotate.x': 'Angle of rotation around x axis',
		'options.rotate.x.speed': 'Speed of rotation around x axis',
		'options.rotate.y': 'Angle of rotation around y axis',
		'options.rotate.y.speed': 'Speed of rotation around y axis',
		'options.rotate.z': 'Angle of rotation around z axis',
		'options.rotate.z.speed': 'Speed of rotation around z axis',

		'options.debug': 'Debug options',
		'options.debugShader': 'Log shader compilation errors',
		'options.debugInputs': 'Log input values',

		'controls.type.mousemovex': 'Move the mouse pointer horizontally over the canvas',
		'controls.type.mousemovey': 'Move the mouse pointer vertically over the canvas',
		'controls.to': 'to update',
		/*
		// TODO
		'controls.value.background.solid.color.r': 'Background color red component',
		'controls.value.background.solid.color.g': 'Background color green component',
		'controls.value.background.solid.color.b': 'Background color blue component',
		'controls.value.background.solid.color.a': 'Background color alpha component',
		'controls.value.shader.single.color.r': 'Fragment color red component',
		'controls.value.shader.single.color.g': 'Fragment color green component',
		'controls.value.shader.single.color.b': 'Fragment color blue component',
		'controls.value.shader.single.color.a': 'Fragment color alpha component',
		'controls.value.shape.gasket.depth': 'Sierpinski gasket recursion depth',
		'controls.value.animation.rotation.speed': 'Z axis rotation speed',
		*/
	}[id];
};

$(function(){
	$('.webgl-starter').each(function(){
		var containerNode=$(this);
		var optionsNode;
		var codeNode;
		var options=new Options();

		function showHideSuboptionInputs(optionName,optionValue) {
			// TODO sub-sub option support?
			optionsNode.find("[data-option^='"+optionName+".']").show()
				.not("[data-option^='"+optionName+"."+optionValue+".']").hide();
		}
		function updateCode() {
			codeNode.text(generateCode(options.cloneWithoutHidden(),i18n));
			if (window.hljs) hljs.highlightBlock(codeNode[0]);
		}
		function writeGeneralOption(option) {
			var id=generateId();
			return $("
")
				.append(""+i18n('options.'+option.name)+":")
				.append(" ")
				.append(
					$("<select id='"+id+"'>").append(
						option.availableValues.map(function(availableValue){
							return $("<option>").val(availableValue).html(i18n('options.'+option.name+'.'+availableValue))
						})
					).val(options[option.name]).change(function(){
						options[option.name]=this.value;
						showHideSuboptionInputs(option.name,this.value);
						updateCode();
					})
				);
		}
		function writeInputOption(option) {
			var id=generateId();
			var inputId=generateId();
			var sliderInput,numberInput;
			return $("
")
				.append(""+i18n('options.'+option.name)+":")
				.append(" "+option.getMinLabel()+" ")
				.append(
					sliderInput=$("<input type='range' id='"+id+"'>")
						.attr('min',option.getMin())
						.attr('max',option.getMax())
						.attr('step',option.getSetupStep())
						.val(options[option.name])
						.on('input change',function(){
							// TODO delay code generation
						//.on('input',function(){
							numberInput.val(this.value);
							options[option.name]=parseFloat(this.value);
							updateCode();
						})
				)
				.append(" "+option.getMaxLabel()+" ")
				.append(
					numberInput=$("<input type='number' required>")
						.attr('min',option.getMin())
						.attr('max',option.getMax())
						.attr('step',option.getSetupStep())
						.val(options[option.name])
						.on('input',function(){
							sliderInput.val(this.value);
							options[option.name]=parseFloat(this.value);
							updateCode();
						})
				)
				.append(" ")
				.append(
					$("<button type='button'>Reset</button>").click(function(){
						sliderInput.val(option.defaultValue).change();
					})
				)
				.append(" ")
				.append(""+i18n('options.*.input')+" ")
				.append(
					$("<select id='"+inputId+"'>").append(
						option.availableInputTypes.map(function(availableInputType){
							return $("<option>").val(availableInputType).html(i18n('options.*.input.'+availableInputType))
						})
					).val(options[option.name+'.input']).change(function(){
						options[option.name+'.input']=this.value;
						updateCode();
					})
				);
		}
		function writeDebugOption(option) {
			var id=generateId();
			return $("
")
				.append(
					$("<input type='checkbox' id='"+id+"'>")
						.prop('checked',options[option.name])
						.change(function(){
							options[option.name]=$(this).prop('checked');
							updateCode();
						})
				)
				.append(" "+i18n('options.'+option.name)+"");
		}
		function writeOptions() {
			return $("
").append(
				$("<fieldset>").append("<legend>"+i18n('options.general')+"</legend>").append(
					options.generalOptions.map(writeGeneralOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.input')+"</legend>").append(
					options.inputOptions.map(writeInputOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.transform')+"</legend>").append(
					options.transformOptions.map(writeInputOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.debug')+"</legend>").append(
					options.debugOptions.map(writeDebugOption)
				)
			);
		}
		function hideSuboptionInputs() {
			options.generalOptions.forEach(function(option){
				showHideSuboptionInputs(option.name,options[option.name]);
			});
		}
		function writeButtons() {
			return $("
").append(
				$("<button type='button'>Save source code</button>").click(function(){
					// yes I want a button, but download attr is only available for links
					$(this).attr('href',getHtmlDataUri(codeNode.text()));
				})
			).append(
				" "
			).append(
				$("<button type='button'>Run in new window</button>").click(function(){
					window.open(getHtmlDataUri(codeNode.text()),"generatedCode");
				})
			).append(
				" these buttons don't work in Internet Explorer, copy-paste the code manually"
			)
		}

		containerNode.empty().append(optionsNode=writeOptions());
		hideSuboptionInputs();
		containerNode.append(writeButtons()).append(
			$("
").append(codeNode=$("").text(generateCode(options.cloneWithoutHidden(),i18n)))
		);
		if (window.hljs) {
			hljs.highlightBlock(codeNode[0]);
		} else {
			containerNode.append("
"+i18n('message.hljs')+"
");
		}
		containerNode.append(writeButtons());
	});
});
<commit_msg>delay code update for slider/number inputs<commit_after>var idCounter=0;
function generateId() {
	return 'webgl-starter-id-'+(idCounter++);
}

var Options=require('./options.js');
var generateCode=require('./code.js');

function getHtmlDataUri(html) {
	// with base64: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/btoa
	//return "data:text/html;charset=utf-8;base64,"+window.btoa(unescape(encodeURIComponent(str)));
	// without base64: https://en.wikipedia.org/wiki/Data_URI_scheme
	return 'data:text/html;charset=utf-8,'+encodeURIComponent(html);
}

var i18n=function(id){ // fake temporary i18n
	return {
		'message.hljs': "highlight.js (hosted on cdnjs.cloudflare.com) is not loaded. Syntax highlighting is disabled.",

		'options.general': 'General options',
		'options.background': 'Background',
		'options.background.none': 'None (transparent)',
		'options.background.solid': 'Solid color',
		'options.shader': 'Shader',
		'options.shader.single': 'Single color',
		'options.shader.vertex': 'One color per vertex',
		'options.shader.face': 'One color per face',
		'options.shape': 'Shape to draw',
		'options.shape.square': 'Square',
		'options.shape.triangle': 'Triangle',
		'options.shape.gasket': 'Sierpinski gasket', // wp: Sierpinski triangle
		'options.shape.cube': 'Cube',

		'options.input': 'Input options',
		'options.background.solid.color.r': 'Background color red component',
		'options.background.solid.color.g': 'Background color green component',
		'options.background.solid.color.b': 'Background color blue component',
		'options.background.solid.color.a': 'Background color alpha component',
		'options.shader.single.color.r': 'Fragment color red component',
		'options.shader.single.color.g': 'Fragment color green component',
		'options.shader.single.color.b': 'Fragment color blue component',
		'options.shader.single.color.a': 'Fragment color alpha component',
		'options.shape.gasket.depth': 'Sierpinski gasket recursion depth',
		'options.animation.rotation.speed': 'Z axis rotation speed',
		'options.*.input': 'This value is',
		'options.*.input.constant': 'kept constant',
		'options.*.input.slider': 'updated with a slider',
		'options.*.input.mousemovex': 'updated by moving the mouse horizontally',
		'options.*.input.mousemovey': 'updated by moving the mouse vertically',
		'options.*.input.animated': 'animated',

		'options.transform': 'Transforms',
		'options.rotate.x': 'Angle of rotation around x axis',
		'options.rotate.x.speed': 'Speed of rotation around x axis',
		'options.rotate.y': 'Angle of rotation around y axis',
		'options.rotate.y.speed': 'Speed of rotation around y axis',
		'options.rotate.z': 'Angle of rotation around z axis',
		'options.rotate.z.speed': 'Speed of rotation around z axis',

		'options.debug': 'Debug options',
		'options.debugShader': 'Log shader compilation errors',
		'options.debugInputs': 'Log input values',

		'controls.type.mousemovex': 'Move the mouse pointer horizontally over the canvas',
		'controls.type.mousemovey': 'Move the mouse pointer vertically over the canvas',
		'controls.to': 'to update',
		/*
		// TODO
		'controls.value.background.solid.color.r': 'Background color red component',
		'controls.value.background.solid.color.g': 'Background color green component',
		'controls.value.background.solid.color.b': 'Background color blue component',
		'controls.value.background.solid.color.a': 'Background color alpha component',
		'controls.value.shader.single.color.r': 'Fragment color red component',
		'controls.value.shader.single.color.g': 'Fragment color green component',
		'controls.value.shader.single.color.b': 'Fragment color blue component',
		'controls.value.shader.single.color.a': 'Fragment color alpha component',
		'controls.value.shape.gasket.depth': 'Sierpinski gasket recursion depth',
		'controls.value.animation.rotation.speed': 'Z axis rotation speed',
		*/
	}[id];
};

$(function(){
	$('.webgl-starter').each(function(){
		var containerNode=$(this);
		var optionsNode;
		var codeNode;
		var options=new Options();
		var codeUpdateTimeoutId=null;
		var codeUpdateDelay=200;

		function showHideSuboptionInputs(optionName,optionValue) {
			// TODO sub-sub option support?
			optionsNode.find("[data-option^='"+optionName+".']").show()
				.not("[data-option^='"+optionName+"."+optionValue+".']").hide();
		}
		function updateCode() {
			codeNode.text(generateCode(options.cloneWithoutHidden(),i18n));
			if (window.hljs) hljs.highlightBlock(codeNode[0]);
		}
		function writeGeneralOption(option) {
			var id=generateId();
			return $("
")
				.append(""+i18n('options.'+option.name)+":")
				.append(" ")
				.append(
					$("<select id='"+id+"'>").append(
						option.availableValues.map(function(availableValue){
							return $("<option>").val(availableValue).html(i18n('options.'+option.name+'.'+availableValue))
						})
					).val(options[option.name]).change(function(){
						options[option.name]=this.value;
						showHideSuboptionInputs(option.name,this.value);
						updateCode();
					})
				);
		}
		function writeInputOption(option) {
			var id=generateId();
			var inputId=generateId();
			var sliderInput,numberInput;
			return $("
")
				.append(""+i18n('options.'+option.name)+":")
				.append(" "+option.getMinLabel()+" ")
				.append(
					sliderInput=$("<input type='range' id='"+id+"'>")
						.attr('min',option.getMin())
						.attr('max',option.getMax())
						.attr('step',option.getSetupStep())
						.val(options[option.name])
						.on('input change',function(){
							clearTimeout(codeUpdateTimeoutId);
							numberInput.val(this.value);
							options[option.name]=parseFloat(this.value);
							codeUpdateTimeoutId=setTimeout(updateCode,codeUpdateDelay);
						})
				)
				.append(" "+option.getMaxLabel()+" ")
				.append(
					numberInput=$("<input type='number' required>")
						.attr('min',option.getMin())
						.attr('max',option.getMax())
						.attr('step',option.getSetupStep())
						.val(options[option.name])
						.on('input',function(){
							clearTimeout(codeUpdateTimeoutId);
							sliderInput.val(this.value);
							options[option.name]=parseFloat(this.value);
							codeUpdateTimeoutId=setTimeout(updateCode,codeUpdateDelay);
						})
				)
				.append(" ")
				.append(
					$("<button type='button'>Reset</button>").click(function(){
						sliderInput.val(option.defaultValue).change();
					})
				)
				.append(" ")
				.append(""+i18n('options.*.input')+" ")
				.append(
					$("<select id='"+inputId+"'>").append(
						option.availableInputTypes.map(function(availableInputType){
							return $("<option>").val(availableInputType).html(i18n('options.*.input.'+availableInputType))
						})
					).val(options[option.name+'.input']).change(function(){
						options[option.name+'.input']=this.value;
						updateCode();
					})
				);
		}
		function writeDebugOption(option) {
			var id=generateId();
			return $("
")
				.append(
					$("<input type='checkbox' id='"+id+"'>")
						.prop('checked',options[option.name])
						.change(function(){
							options[option.name]=$(this).prop('checked');
							updateCode();
						})
				)
				.append(" "+i18n('options.'+option.name)+"");
		}
		function writeOptions() {
			return $("
").append(
				$("<fieldset>").append("<legend>"+i18n('options.general')+"</legend>").append(
					options.generalOptions.map(writeGeneralOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.input')+"</legend>").append(
					options.inputOptions.map(writeInputOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.transform')+"</legend>").append(
					options.transformOptions.map(writeInputOption)
				)
			).append(
				$("<fieldset>").append("<legend>"+i18n('options.debug')+"</legend>").append(
					options.debugOptions.map(writeDebugOption)
				)
			);
		}
		function hideSuboptionInputs() {
			options.generalOptions.forEach(function(option){
				showHideSuboptionInputs(option.name,options[option.name]);
			});
		}
		function writeButtons() {
			return $("
").append(
				$("<button type='button'>Save source code</button>").click(function(){
					// yes I want a button, but download attr is only available for links
					$(this).attr('href',getHtmlDataUri(codeNode.text()));
				})
			).append(
				" "
			).append(
				$("<button type='button'>Run in new window</button>").click(function(){
					window.open(getHtmlDataUri(codeNode.text()),"generatedCode");
				})
			).append(
				" these buttons don't work in Internet Explorer, copy-paste the code manually"
			)
		}

		containerNode.empty().append(optionsNode=writeOptions());
		hideSuboptionInputs();
		containerNode.append(writeButtons()).append(
			$("
").append(codeNode=$("").text(generateCode(options.cloneWithoutHidden(),i18n)))
		);
		if (window.hljs) {
			hljs.highlightBlock(codeNode[0]);
		} else {
			containerNode.append("
"+i18n('message.hljs')+"
");
		}
		containerNode.append(writeButtons());
	});
});
<|endoftext|>


<commit_before>(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
        window.location.search.slice(1).split("&").some(function (searchTerm) {
          if (searchTerm.indexOf("lang=") === 0) {
            lang = searchTerm.split("=").slice(1).join("=");
            return true;
          }
        });

        return lang.toLowerCase();
      })(window.navigator.userLanguage || window.navigator.language),
      language = languageDialect.split("-")[0],
      QueryableObject = (function() {
        var scope = "_scope";

        function QueryableObject(o) {
          this[scope] = o;
        }

        QueryableObject.prototype.find = function(path) {
          return path.split(".").reduce(function (obj, key) {
            return obj ? obj[key] : undefined;
          }, this[scope]);
        };

        return QueryableObject;
      }());

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("data-i18n")) {
      applyTranslation(ele, ele.getAttribute("data-i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
    var translated = obj.find(path);

    if (typeof(translated) === "object" && !Array.isArray(translated)) {
      console.warn("Could not translate %o: path '%s' is of type object", ele, path);
    } else if(typeof(translated) !== "undefined") {
      clean(ele);
      ele.appendChild(toDom(translated));
      ele.lang = language;
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang.toLowerCase();
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function clean(ele) {
    var child;
    while((child = ele.firstChild)) {
      ele.removeChild(child);
    }
  }

  function toDom(content) {
    if(Array.isArray(content)) {
      return content.reduce(function(frag, text) {
        var ele = document.createElement("p");
        ele.textContent = text;
        frag.appendChild(ele);

        return frag;
      }, document.createDocumentFragment());
    }
    return document.createTextNode(content);
  }

  function translate(obj, ancestor) {
    ancestor = ancestor || document.documentElement;

    if(getLang(ancestor) === languageDialect) {
      if(!ancestor.hasAttribute("data-i18n")) {
        [].slice.call(ancestor.querySelectorAll("[lang]:not([lang='" + language + "'])")).forEach(translate.bind(null, obj));
      }
      return;
    }

    if(ancestor.hasAttribute("data-i18n")) {
      applyTranslationToElement(ancestor, obj);
    } else {
      [].slice.call(ancestor.querySelectorAll("[data-i18n]")).forEach(function(ele) {
        if(getLang(ele, ancestor) !== languageDialect) {
          applyTranslationToElement(ele, obj);
        }
      });
    }
  }

  global.i18n = {
    base: (document.documentElement.getAttribute("data-i18n-base") || "locales/"),
    set: (document.documentElement.getAttribute("data-i18n-set") || "translation"),
    loadTranslations: function(lang, set, base) {
      var url = (base || this.base || "") + (lang || language) + "/" + (set || this.set) + ".json";
      console.info("loading translations from: " + url);

      return fetch(url, {
        headers: {
          "Accept": "application/json"
        }
      }).then(function (res) {
        if (res.status >= 200 && res.status < 300) {
          console.info("successfully loaded translations");

          return res.json().then(function(obj) {
            return new QueryableObject(obj);
          });
        }

        // TODO add error object instead of false
        return Promise.reject(false);
      }).catch(function (err) {
        console.error("Error loading translations: %o", err);
      });
    },
    translate: function(ele) {
      if(!this.translations) {
        this.translations = this.loadTranslations();
      }

      return this.translations.then(function(obj) {
        translate(obj, ele);
        return obj;
      });
    },
    translateAll: function() {
      this.translatedAll = true;
      return this.translate(document.documentElement);
    },
    set language(lang) {
      lang = String(lang);

      if(lang !== language) {
        language = lang;

        if(this.translatedAll) {
          this.translateAll(lang);
        } else if(this.translations) {
          this.translations = this.loadTranslations();
        }
      }
    },
    get language() {
      return language;
    }
  };

  if(!document.documentElement.hasAttribute("data-i18n-disable-auto")) {
    global.i18n.translateAll();
  }
}(this));
<commit_msg>Remove redundant internal translate function<commit_after>(function (global) {
  "use strict";

  var languageDialect = (function(lang) {
        window.location.search.slice(1).split("&").some(function (searchTerm) {
          if (searchTerm.indexOf("lang=") === 0) {
            lang = searchTerm.split("=").slice(1).join("=");
            return true;
          }
        });

        return lang.toLowerCase();
      })(window.navigator.userLanguage || window.navigator.language),
      language = languageDialect.split("-")[0],
      QueryableObject = (function() {
        var scope = "_scope";

        function QueryableObject(o) {
          this[scope] = o;
        }

        QueryableObject.prototype.find = function(path) {
          return path.split(".").reduce(function (obj, key) {
            return obj ? obj[key] : undefined;
          }, this[scope]);
        };

        return QueryableObject;
      }());

  function applyTranslationToElement(ele, obj) {
    if(ele.hasAttribute("data-i18n")) {
      applyTranslation(ele, ele.getAttribute("data-i18n"), obj);
    }
  }

  function applyTranslation(ele, path, obj) {
    var translated = obj.find(path);

    if (typeof(translated) === "object" && !Array.isArray(translated)) {
      console.warn("Could not translate %o: path '%s' is of type object", ele, path);
    } else if(typeof(translated) !== "undefined") {
      clean(ele);
      ele.appendChild(toDom(translated));
      ele.lang = language;
    }
  }

  function getLang(ele, threshold) {
    do {
      if(ele.lang) {
        return ele.lang.toLowerCase();
      }
    } while((ele = ele.parentElement) && ele !== threshold);
  }

  function clean(ele) {
    var child;
    while((child = ele.firstChild)) {
      ele.removeChild(child);
    }
  }

  function toDom(content) {
    if(Array.isArray(content)) {
      return content.reduce(function(frag, text) {
        var ele = document.createElement("p");
        ele.textContent = text;
        frag.appendChild(ele);

        return frag;
      }, document.createDocumentFragment());
    }
    return document.createTextNode(content);
  }

  global.i18n = {
    base: (document.documentElement.getAttribute("data-i18n-base") || "locales/"),
    set: (document.documentElement.getAttribute("data-i18n-set") || "translation"),
    loadTranslations: function(lang, set, base) {
      var url = (base || this.base || "") + (lang || language) + "/" + (set || this.set) + ".json";
      console.info("loading translations from: " + url);

      return fetch(url, {
        headers: {
          "Accept": "application/json"
        }
      }).then(function (res) {
        if (res.status >= 200 && res.status < 300) {
          console.info("successfully loaded translations");

          return res.json().then(function(obj) {
            return new QueryableObject(obj);
          });
        }

        // TODO add error object instead of false
        return Promise.reject(false);
      }).catch(function (err) {
        console.error("Error loading translations: %o", err);
      });
    },
    translate: function(ele) {
      if(!this.translations) {
        this.translations = this.loadTranslations();
      }

      return this.translations.then(function(obj) {
        if(getLang(ele) === languageDialect) {
          if(!ele.hasAttribute("data-i18n")) {
            return Promise.all([].slice.call(ele.querySelectorAll("[lang]:not([lang='" + language + "'])")).map(global.i18n.translate.bind(global.i18n))).then(function() {
              return ele;
            });
          }

          return Promise.resolve(ele);
        }

        if(ele.hasAttribute("data-i18n")) {
          applyTranslationToElement(ele, obj);
        } else {
          [].slice.call(ele.querySelectorAll("[data-i18n]")).forEach(function(match) {
            if(getLang(match, ele) !== languageDialect) {
              applyTranslationToElement(match, obj);
            }
          });
        }
        return obj;
      });
    },
    translateAll: function() {
      this.translatedAll = true;
      return this.translate(document.documentElement);
    },
    set language(lang) {
      lang = String(lang);

      if(lang !== language) {
        language = lang;

        if(this.translatedAll) {
          this.translateAll(lang);
        } else if(this.translations) {
          this.translations = this.loadTranslations();
        }
      }
    },
    get language() {
      return language;
    }
  };

  if(!document.documentElement.hasAttribute("data-i18n-disable-auto")) {
    global.i18n.translateAll();
  }
}(this));
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import createStore from './store/createStore';
import AppContainer from './containers/AppContainer';

// ========================================================
// Store Instantiation
// ========================================================
const initialState = window.___INITIAL_STATE__;
const store = createStore(initialState);

// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root');

let render = () => {
  const routes = require('./routes/index').default(store);

  ReactDOM.render(
    <AppContainer store={store} routes={routes} />,
    MOUNT_NODE
  );
};

// ========================================================
// Developer Tools Setup
// ========================================================
if (__DEV__) {
  if (window.devToolsExtension) {
    window.devToolsExtension.open();
  }
}

// This code is excluded from production bundle
if (__DEV__) {
  if (module.hot) {
    // Development render functions
    const renderApp = render;
    const renderError = (error) => {
      const RedBox = require('redbox-react').default;

      ReactDOM.render(<RedBox error={error} />, MOUNT_NODE);
    };

    // Wrap render in try/catch
    render = () => {
      try {
        renderApp();
      } catch (error) {
        renderError(error);
      }
    };

    // Setup hot module replacement
    module.hot.accept('./routes/index', () =>
      setImmediate(() => {
        ReactDOM.unmountComponentAtNode(MOUNT_NODE);
        render();
      })
    );
  }
}

// ========================================================
// Go!
// ========================================================
render();
<commit_msg>Get rid of annoying Redux-devtools popup. Use in-browser instead.<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import createStore from './store/createStore';
import AppContainer from './containers/AppContainer';

// ========================================================
// Store Instantiation
// ========================================================
const initialState = window.___INITIAL_STATE__;
const store = createStore(initialState);

// ========================================================
// Render Setup
// ========================================================
const MOUNT_NODE = document.getElementById('root');

let render = () => {
  const routes = require('./routes/index').default(store);

  ReactDOM.render(
    <AppContainer store={store} routes={routes} />,
    MOUNT_NODE
  );
};

// This code is excluded from production bundle
if (__DEV__) {
  if (module.hot) {
    // Development render functions
    const renderApp = render;
    const renderError = (error) => {
      const RedBox = require('redbox-react').default;

      ReactDOM.render(<RedBox error={error} />, MOUNT_NODE);
    };

    // Wrap render in try/catch
    render = () => {
      try {
        renderApp();
      } catch (error) {
        renderError(error);
      }
    };

    // Setup hot module replacement
    module.hot.accept('./routes/index', () =>
      setImmediate(() => {
        ReactDOM.unmountComponentAtNode(MOUNT_NODE);
        render();
      })
    );
  }
}

// ========================================================
// Go!
// ========================================================
render();
<|endoftext|>


<commit_before>requirejs.config({
    paths: {
        URIjs: "./3rdparty/URIjs/src",
        jquery: "./3rdparty/jquery-1.8.2",
        jquery_anythingslider: "./3rdparty/jquery.anythingslider",
        jquery_autosuggest: "./3rdparty/jquery.autoSuggest",
        jquery_chosen: "./3rdparty/chosen.jquery",
        jquery_ext: "./jquery-ext",
        jquery_form: "./lib/jquery.form/jquery.form",
        jquery_fullcalendar: "./3rdparty/fullcalendar/fullcalendar",
        jquery_jcrop: "./3rdparty/jCrop/js/jquery.Jcrop",
        jquery_placeholder: "./3rdparty/jquery.placeholder",
        jquery_textchange: "./3rdparty/jquery.textchange/jquery.textchange",
        jquery_validate: "./3rdparty/jquery-validation/jquery.validate",
        jquery_validate_additional_methods: "./3rdparty/jquery-validation/additional-methods",
        logging: "./3rdparty/logging/src/logging",
        less: "./3rdparty/less-1.3.1",
        pagedown_converter: "./3rdparty/pagedown/Markdown.Converter",
        pagedown_sanitizer: "./3rdparty/pagedown/Markdown.Sanitizer",
        modernizr: "./3rdparty/modernizr-2.0.6",
        prefixfree: "./3rdparty/prefixfree",
        tinymce: "./3rdparty/tiny_mce/tiny_mce_src"
    },
    shim: {
        jquery_anythingslider: {
            deps: ["jquery"]
        },
        jquery_autosuggest: {
            deps: ["jquery"]
        },
        jquery_chosen: {
            deps: ["jquery"]
        },
        jquery_form: {
            deps: ["jquery"]
        },
        jquery_fullcalendar: {
            deps: ["jquery"]
        },
        jquery_jcrop: {
            deps: ["jquery"]
        },
        jquery_placeholder: {
            deps: ["jquery"]
        },
        jquery_textchange: {
            deps: ["jquery"]
        },
        jquery_validate: {
            deps: ["jquery"]
        },
        jquery_validate_additional_methods: {
            deps: ["jquery_validate"]
        },
        pagedown_sanitizer: {
            deps: ["pagedown_converter"]
        }
    }
});

define([
    "jquery",
    "./registry",
    "./transforms",
    // below here modules that are only loaded
    "modernizr",
    "less",
    "prefixfree",
    "./patterns/autofocus",
    "./patterns/autoscale",
    "./patterns/autosubmit",
    "./patterns/autosubmit2",
    "./patterns/autosuggest",
    "./patterns/breadcrumbs",
    "./patterns/bumper",
    "./patterns/carousel",
    "./patterns/checkedflag",
    "./patterns/checklist",
    "./patterns/chosen",
    "./patterns/collapsible",
    "./patterns/depends",
    "./patterns/edit-tinymce",
    "./patterns/expandable",
    "./patterns/focus",
    "./patterns/form-state",
    "./patterns/fullcalendar",
    "./patterns/image-crop",
    "./patterns/inject",
    "./patterns/markdown",
    "./patterns/menu",
    "./patterns/modal",
    "./patterns/navigation",
    "./patterns/placeholder",
    "./patterns/setclass",
    "./patterns/sortable",
    "./patterns/switch",
    "./patterns/toggle",
    "./patterns/tooltip",
    "./patterns/validate",
    "./patterns/zoom"
], function($, registry, transforms) {
    $(document).on("patterns-registry-before-scan.patterns", function(e) {
      transforms.transformContent($(e.target));
    });
    // wait for the DOM to be ready and initialize
    $(document).ready(function(){
        registry.scan(document.body);
    });
    return registry;
});
<commit_msg>adding space - triggering travis<commit_after>requirejs.config({
    paths: {
        URIjs: "./3rdparty/URIjs/src",
        jquery: "./3rdparty/jquery-1.8.2",
        jquery_anythingslider: "./3rdparty/jquery.anythingslider",
        jquery_autosuggest: "./3rdparty/jquery.autoSuggest",
        jquery_chosen: "./3rdparty/chosen.jquery",
        jquery_ext: "./jquery-ext",
        jquery_form: "./lib/jquery.form/jquery.form",
        jquery_fullcalendar: "./3rdparty/fullcalendar/fullcalendar",
        jquery_jcrop: "./3rdparty/jCrop/js/jquery.Jcrop",
        jquery_placeholder: "./3rdparty/jquery.placeholder",
        jquery_textchange: "./3rdparty/jquery.textchange/jquery.textchange",
        jquery_validate: "./3rdparty/jquery-validation/jquery.validate",
        jquery_validate_additional_methods: "./3rdparty/jquery-validation/additional-methods",
        logging: "./3rdparty/logging/src/logging",
        less: "./3rdparty/less-1.3.1",
        pagedown_converter: "./3rdparty/pagedown/Markdown.Converter",
        pagedown_sanitizer: "./3rdparty/pagedown/Markdown.Sanitizer",
        modernizr: "./3rdparty/modernizr-2.0.6",
        prefixfree: "./3rdparty/prefixfree",
        tinymce: "./3rdparty/tiny_mce/tiny_mce_src"
    },
    shim: {
        jquery_anythingslider: {
            deps: ["jquery"]
        },
        jquery_autosuggest: {
            deps: ["jquery"]
        },
        jquery_chosen: {
            deps: ["jquery"]
        },
        jquery_form: {
            deps: ["jquery"]
        },
        jquery_fullcalendar: {
            deps: ["jquery"]
        },
        jquery_jcrop: {
            deps: ["jquery"]
        },
        jquery_placeholder: {
            deps: ["jquery"]
        },
        jquery_textchange: {
            deps: ["jquery"]
        },
        jquery_validate: {
            deps: ["jquery"]
        },
        jquery_validate_additional_methods: {
            deps: ["jquery_validate"]
        },
        pagedown_sanitizer: {
            deps: ["pagedown_converter"]
        }
    }
});

define([
    "jquery",
    "./registry",
    "./transforms",
    // below here modules that are only loaded
    "modernizr",
    "less",
    "prefixfree",
    "./patterns/autofocus",
    "./patterns/autoscale",
    "./patterns/autosubmit",
    "./patterns/autosubmit2",
    "./patterns/autosuggest",
    "./patterns/breadcrumbs",
    "./patterns/bumper",
    "./patterns/carousel",
    "./patterns/checkedflag",
    "./patterns/checklist",
    "./patterns/chosen",
    "./patterns/collapsible",
    "./patterns/depends",
    "./patterns/edit-tinymce",
    "./patterns/expandable",
    "./patterns/focus",
    "./patterns/form-state",
    "./patterns/fullcalendar",
    "./patterns/image-crop",
    "./patterns/inject",
    "./patterns/markdown",
    "./patterns/menu",
    "./patterns/modal",
    "./patterns/navigation",
    "./patterns/placeholder",
    "./patterns/setclass",
    "./patterns/sortable",
    "./patterns/switch",
    "./patterns/toggle",
    "./patterns/tooltip",
    "./patterns/validate",
    "./patterns/zoom"
], function($, registry, transforms) {
    $(document).on("patterns-registry-before-scan.patterns", function(e) {
      transforms.transformContent($(e.target));
    });
    // wait for the DOM to be ready and initialize
    $(document).ready(function(){
        registry.scan(document.body);
    });
    return registry;
});
<|endoftext|>


<commit_before>'use strict'

/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 2017 SIRAP Group All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * plugin.js Tinymce plugin headersfooters
 * @file plugin.js
 * @module
 * @name tinycmce-plugin-headersfooters
 * @description
 * A plugin for tinymce WYSIWYG HTML editor that allow to insert headers and footers
 * @see https://github.com/sirap-group/tinymce-plugin-headersfooters
 * @author Rémi Becheras
 * @author Groupe SIRAP
 * @license GNU GPL-v2 http://www.tinymce.com/license
 */

/**
 * Tinymce library - injected by the plugin loader.
 * @external tinymce
 * @see {@link https://www.tinymce.com/docs/api/class/tinymce/|Tinymce API Reference}
 */
var tinymce = window.tinymce

var menuItems = require('./components/menu-items')

var units = require('./utils/units')
var events = require('./utils/events')
var uiUtils = require('./utils/ui')

var eventHandlers = require('./event-handlers')
var Format = require('./classes/Format')

// Add the plugin to the tinymce PluginManager
tinymce.PluginManager.add('headersfooters', tinymcePluginHeadersFooters)

/**
 * Tinymce plugin headers/footers
 * @function
 * @global
 * @param {tinymce.Editor} editor - The injected tinymce editor.
 * @returns void
 */
function tinymcePluginHeadersFooters (editor, url) {
  // var thisPlugin = this
  this.type = editor.settings.headersfooters_type
  this.bodyClass = editor.settings.body_class

  // bind plugin methods
  this.enable = enable
  this.disable = disable
  this.parseParamList = parseParamList

  this.isMaster = this.type === 'body'
  this.isSlave = !this.isMaster

  if (this.isMaster && window.env === 'development') {
    window.mceHF = this
  }

  this.headerFooterFactory = null

  this.units = units
  this.editor = editor

  this.documentBody = null
  this.documentBodies = {
    app: null,
    mce: {
      header: null,
      body: null,
      footer: null
    }
  }
  this.stackedLayout = {
    root: null,
    wrapper: null,
    layout: null,
    menubar: null,
    toolbar: null,
    editarea: null,
    statusbar: null
  }

  if (this.isMaster) {
    this.pageLayout = {
      pageWrapper: null,
      pagePanel: null,
      headerWrapper: null,
      headerPanel: null,
      bodyPanel: null,
      footerWrapper: null,
      footerPanel: null
    }
  }

  _setAvailableFormats.call(this)
  // console.log('availableFormats: ', this.availableFormats)
  // console.log('defaultFormat: ', this.defaultFormat)

  this.menuItemsList = menuItems.create(editor)
  uiUtils.autoAddMenuItems.call(this)

  editor.addCommand('insertPageNumberCmd', function () {
    editor.insertContent('{{page}}')
  })
  editor.addCommand('insertNumberOfPagesCmd', function () {
    editor.insertContent('{{pages}}')
  })

  events.autoBindImplementedEventCallbacks.call(this, editor, eventHandlers)
}

function enable () {
  this.stackedLayout.menubar.show()
  this.stackedLayout.toolbar.show()
  this.stackedLayout.statusbar.wrapper.show()
  this.stackedLayout.statusbar.path.show()
  this.stackedLayout.statusbar.wordcount.show()
  this.stackedLayout.statusbar.resizehandle.show()

  this.stackedLayout.statusbar.wrapper.css({left: 0, right: 0, zIndex: 9999})
}

function disable () {
  this.stackedLayout.menubar.hide()
  this.stackedLayout.toolbar.hide()
  this.stackedLayout.statusbar.wrapper.hide()
  this.stackedLayout.statusbar.path.hide()
  this.stackedLayout.statusbar.wordcount.hide()
  this.stackedLayout.statusbar.resizehandle.hide()
}

function _setAvailableFormats () {
  var that = this
  var settings = this.editor.settings

  // set enabled default formats
  var userEnabledDefaultFormats = this.parseParamList(settings.headersfooters_formats)
  .map(function (formatName) {
    return Format.defaults[formatName]
  })
  .filter(function (v) {
    return !!v
  })
  if (userEnabledDefaultFormats.length) {
    this.formats = userEnabledDefaultFormats
  } else {
    this.formats = []
    for (var name in Format.defaults) {
      that.formats.push(Format.defaults[name])
    }
  }

  // set user custom formats
  this.customFormats = (settings.headersfooters_custom_formats || [])
  .map(function (f) {
    return new Format(f.name, f.config)
  })

  // set the formats available for the editor
  this.availableFormats = {}
  // use enabled default formats
  this.formats.map(function (f) {
    that.availableFormats[f.name] = f
  })
  // add or override custom formats
  this.customFormats.map(function (f) {
    that.availableFormats[f.name] = f
  })

  // select a default format for new doc
  this.defaultFormat = this.availableFormats[settings.headersfooters_default_format] || this.formats[0] || this.customFormats[0]
}

function parseParamList (paramValue) {
  if (paramValue === undefined) {
    return []
  }
  if (typeof paramValue !== 'string') {
    throw new TypeError('paramValue must be a String, ' + typeof paramValue + ' given.')
  }
  return paramValue.split(' ')
}
<commit_msg>collapse any editor selection leaving the editor<commit_after>'use strict'

/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 2017 SIRAP Group All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * plugin.js Tinymce plugin headersfooters
 * @file plugin.js
 * @module
 * @name tinycmce-plugin-headersfooters
 * @description
 * A plugin for tinymce WYSIWYG HTML editor that allow to insert headers and footers
 * @see https://github.com/sirap-group/tinymce-plugin-headersfooters
 * @author Rémi Becheras
 * @author Groupe SIRAP
 * @license GNU GPL-v2 http://www.tinymce.com/license
 */

/**
 * Tinymce library - injected by the plugin loader.
 * @external tinymce
 * @see {@link https://www.tinymce.com/docs/api/class/tinymce/|Tinymce API Reference}
 */
var tinymce = window.tinymce

var menuItems = require('./components/menu-items')

var units = require('./utils/units')
var events = require('./utils/events')
var uiUtils = require('./utils/ui')

var eventHandlers = require('./event-handlers')
var Format = require('./classes/Format')

// Add the plugin to the tinymce PluginManager
tinymce.PluginManager.add('headersfooters', tinymcePluginHeadersFooters)

/**
 * Tinymce plugin headers/footers
 * @function
 * @global
 * @param {tinymce.Editor} editor - The injected tinymce editor.
 * @returns void
 */
function tinymcePluginHeadersFooters (editor, url) {
  // var thisPlugin = this
  this.type = editor.settings.headersfooters_type
  this.bodyClass = editor.settings.body_class

  // bind plugin methods
  this.enable = enable
  this.disable = disable
  this.parseParamList = parseParamList

  this.isMaster = this.type === 'body'
  this.isSlave = !this.isMaster

  if (this.isMaster && window.env === 'development') {
    window.mceHF = this
  }

  this.headerFooterFactory = null

  this.units = units
  this.editor = editor

  this.documentBody = null
  this.documentBodies = {
    app: null,
    mce: {
      header: null,
      body: null,
      footer: null
    }
  }
  this.stackedLayout = {
    root: null,
    wrapper: null,
    layout: null,
    menubar: null,
    toolbar: null,
    editarea: null,
    statusbar: null
  }

  if (this.isMaster) {
    this.pageLayout = {
      pageWrapper: null,
      pagePanel: null,
      headerWrapper: null,
      headerPanel: null,
      bodyPanel: null,
      footerWrapper: null,
      footerPanel: null
    }
  }

  _setAvailableFormats.call(this)
  // console.log('availableFormats: ', this.availableFormats)
  // console.log('defaultFormat: ', this.defaultFormat)

  this.menuItemsList = menuItems.create(editor)
  uiUtils.autoAddMenuItems.call(this)

  editor.addCommand('insertPageNumberCmd', function () {
    editor.insertContent('{{page}}')
  })
  editor.addCommand('insertNumberOfPagesCmd', function () {
    editor.insertContent('{{pages}}')
  })

  events.autoBindImplementedEventCallbacks.call(this, editor, eventHandlers)
}

function enable () {
  this.stackedLayout.menubar.show()
  this.stackedLayout.toolbar.show()
  this.stackedLayout.statusbar.wrapper.show()
  this.stackedLayout.statusbar.path.show()
  this.stackedLayout.statusbar.wordcount.show()
  this.stackedLayout.statusbar.resizehandle.show()

  this.stackedLayout.statusbar.wrapper.css({left: 0, right: 0, zIndex: 9999})
}

function disable () {
  this.stackedLayout.menubar.hide()
  this.stackedLayout.toolbar.hide()
  this.stackedLayout.statusbar.wrapper.hide()
  this.stackedLayout.statusbar.path.hide()
  this.stackedLayout.statusbar.wordcount.hide()
  this.stackedLayout.statusbar.resizehandle.hide()

  if (!this.editor.selection.isCollapsed()) {
    this.editor.selection.collapse()
  }
}

function _setAvailableFormats () {
  var that = this
  var settings = this.editor.settings

  // set enabled default formats
  var userEnabledDefaultFormats = this.parseParamList(settings.headersfooters_formats)
  .map(function (formatName) {
    return Format.defaults[formatName]
  })
  .filter(function (v) {
    return !!v
  })
  if (userEnabledDefaultFormats.length) {
    this.formats = userEnabledDefaultFormats
  } else {
    this.formats = []
    for (var name in Format.defaults) {
      that.formats.push(Format.defaults[name])
    }
  }

  // set user custom formats
  this.customFormats = (settings.headersfooters_custom_formats || [])
  .map(function (f) {
    return new Format(f.name, f.config)
  })

  // set the formats available for the editor
  this.availableFormats = {}
  // use enabled default formats
  this.formats.map(function (f) {
    that.availableFormats[f.name] = f
  })
  // add or override custom formats
  this.customFormats.map(function (f) {
    that.availableFormats[f.name] = f
  })

  // select a default format for new doc
  this.defaultFormat = this.availableFormats[settings.headersfooters_default_format] || this.formats[0] || this.customFormats[0]
}

function parseParamList (paramValue) {
  if (paramValue === undefined) {
    return []
  }
  if (typeof paramValue !== 'string') {
    throw new TypeError('paramValue must be a String, ' + typeof paramValue + ' given.')
  }
  return paramValue.split(' ')
}
<|endoftext|>


<commit_before>define([
    'require',
    'patternregistry',
    './core/init',
    './patterns/toggle',
    '../../lib/jquery',
    '../lib/domReady!'
], function(require) {
    var mapal = require('./core/init');
    mapal.registerWidthClass("narrow", 0, 780);
    mapal.registerWidthClass("medium", 0, 1109);
    mapal.registerWidthClass("wide", 1110, null);

    // register our patterns
    // rethink naming once all patterns are migrated to this style
    var mapal.passivePatterns.toggle = require('./patterns/toggle');

    // wait for the DOM to be ready and initialize
    var doc = require('../lib/domReady!');
    mapal.init();
    mapal.initContent(doc.body);
    $(doc).trigger("setupFinished", doc);
});
<commit_msg>remove erroneous var<commit_after>define([
    'require',
    'patternregistry',
    './core/init',
    './patterns/toggle',
    '../../lib/jquery',
    '../lib/domReady!'
], function(require) {
    var mapal = require('./core/init');
    mapal.registerWidthClass("narrow", 0, 780);
    mapal.registerWidthClass("medium", 0, 1109);
    mapal.registerWidthClass("wide", 1110, null);

    // register our patterns
    // rethink naming once all patterns are migrated to this style
    mapal.passivePatterns.toggle = require('./patterns/toggle');

    // wait for the DOM to be ready and initialize
    var doc = require('../lib/domReady!');
    mapal.init();
    mapal.initContent(doc.body);
    $(doc).trigger("setupFinished", doc);
});
<|endoftext|>


<commit_before>define(function (require, exports, module) {

  const Plugin = require('extplug/Plugin')
  const Events = require('plug/core/Events')

  const MARKUP_TYPES = [ 'message', 'emote', 'mention' ]

  function Token(type, text) {
    this.type = type
    this.text = text
  }

  const ChatMarkup = Plugin.extend({
    name: 'Chat Markup',
    description: 'Applies some markdown/slack/reddit-like markup to ' +
                 'chat messages: _italic_, *bold*, ~strike~, `code`.',

    enable() {
      Events.on('chat:beforereceive', this.onMessage, this)
      this.Style({
        '.extplug-strike': { 'text-decoration': 'line-through' }
      })
    },

    disable() {
      Events.off('chat:beforereceive', this.onMessage)
    },

    onMessage(msg) {
      if (MARKUP_TYPES.indexOf(msg.type) !== -1) {
        msg.message = this.transform(msg.message)
      }
    },

    // we use a tokenizer instead of simple regexes, so we can easily
    // avoid applying markup to mid-word things, or mid-URL and
    // mid-username things in particular. E.g. "@_username_" will just
    // work, and "https://youtu.be/Yif_3Ryr_so" will too.
    // however it will still apply markup if it ends in the middle of a
    // word, like in "_italic_things"
    tokenize(text) {
      let chunk
      let i = 0
      let tokens = []
      // adds a token of type `type` if the current chunk starts with
      // a `delim`-delimited string
      const delimited = (delim, type) => {
        if (chunk[0] === delim && chunk[1] !== delim) {
          let end = chunk.indexOf(delim, 1)
          if (end !== -1) {
            tokens.push(new Token(type, chunk.slice(1, end)))
            i += end + 1
            return true
          }
        }
      }
      // eat spaces
      const space = () => {
        // .slice again because `i` changed
        let m = /^\s+/.exec(text.slice(i))
        if (m) {
          tokens.push(new Token('word', m[0]))
          i += m[0].length
        }
      }
      // tokenize text, just loop until it's done!
      while (chunk = text.slice(i)) {
        let found =
          delimited('_', 'em') ||
          delimited('*', 'strong') ||
          delimited('`', 'code') ||
          delimited('~', 'strike')
        if (!found) {
          let end = chunk.indexOf(' ', 1) + /* eat space */ 1
          if (end === 0) // no match, = -1 + 1
            end = chunk.length
          tokens.push(new Token('word', chunk.slice(0, end)))
          i += end
        }
        space()
      }
      return tokens
    },
    transform(text) {
      return this.tokenize(text).reduce((str, tok) => {
        return str + (
          tok.type === 'em'     ? `<em>${this.transform(tok.text)}</em>`
        : tok.type === 'strong' ? `<strong>${this.transform(tok.text)}</strong>`
        : tok.type === 'code'   ? `<code>${tok.text}</code>`
        : tok.type === 'strike' ? `<span class="extplug-strike">${this.transform(tok.text)}</span>`
        : tok.text
        )
      }, '')
    }
  })

  module.exports = ChatMarkup

})
<commit_msg>simplify using new APIs<commit_after>define(function (require, exports, module) {

  const Plugin = require('extplug/Plugin')
  const Events = require('plug/core/Events')

  const MARKUP_TYPES = [ 'message', 'emote', 'mention' ]

  function Token(type, text) {
    this.type = type
    this.text = text
  }

  const ChatMarkup = Plugin.extend({
    name: 'Chat Markup',
    description: 'Applies some markdown/slack/reddit-like markup to ' +
                 'chat messages: _italic_, *bold*, ~strike~, `code`.',

    style: {
      '.extplug-strike': { 'text-decoration': 'line-through' }
    },

    enable() {
      this.listenTo(Events, 'chat:beforereceive', this.onMessage)
    },

    onMessage(msg) {
      if (MARKUP_TYPES.indexOf(msg.type) !== -1) {
        msg.message = this.transform(msg.message)
      }
    },

    // we use a tokenizer instead of simple regexes, so we can easily
    // avoid applying markup to mid-word things, or mid-URL and
    // mid-username things in particular. E.g. "@_username_" will just
    // work, and "https://youtu.be/Yif_3Ryr_so" will too.
    // however it will still apply markup if it ends in the middle of a
    // word, like in "_italic_things"
    tokenize(text) {
      let chunk
      let i = 0
      let tokens = []
      // adds a token of type `type` if the current chunk starts with
      // a `delim`-delimited string
      const delimited = (delim, type) => {
        if (chunk[0] === delim && chunk[1] !== delim) {
          let end = chunk.indexOf(delim, 1)
          if (end !== -1) {
            tokens.push(new Token(type, chunk.slice(1, end)))
            i += end + 1
            return true
          }
        }
      }
      // eat spaces
      const space = () => {
        // .slice again because `i` changed
        let m = /^\s+/.exec(text.slice(i))
        if (m) {
          tokens.push(new Token('word', m[0]))
          i += m[0].length
        }
      }
      // tokenize text, just loop until it's done!
      while (chunk = text.slice(i)) {
        let found =
          delimited('_', 'em') ||
          delimited('*', 'strong') ||
          delimited('`', 'code') ||
          delimited('~', 'strike')
        if (!found) {
          let end = chunk.indexOf(' ', 1) + /* eat space */ 1
          if (end === 0) // no match, = -1 + 1
            end = chunk.length
          tokens.push(new Token('word', chunk.slice(0, end)))
          i += end
        }
        space()
      }
      return tokens
    },
    transform(text) {
      return this.tokenize(text).reduce((str, tok) => {
        return str + (
          tok.type === 'em'     ? `<em>${this.transform(tok.text)}</em>`
        : tok.type === 'strong' ? `<strong>${this.transform(tok.text)}</strong>`
        : tok.type === 'code'   ? `<code>${tok.text}</code>`
        : tok.type === 'strike' ? `<span class="extplug-strike">${this.transform(tok.text)}</span>`
        : tok.text
        )
      }, '')
    }
  })

  module.exports = ChatMarkup

})
<|endoftext|>


<commit_before>'use strict';

const {
  app,
  BrowserWindow,
  Menu,
  Tray,
  ipcMain,
  nativeImage,
  dialog,
  systemPreferences
} = require('electron');

process.on('uncaughtException', (error) => {
  console.error(error);
});

if (process.platform === 'win32') {
  var cmd = process.argv[1];
  if (cmd === '--squirrel-uninstall') {
    var AutoLaunch = require('auto-launch');
    var appLauncher = new AutoLaunch({
      name: 'Mattermost'
    });
    appLauncher.isEnabled().then(function(enabled) {
      if (enabled)
        appLauncher.disable();
    });
  }
}

require('electron-squirrel-startup');

const fs = require('fs');
const path = require('path');

var settings = require('./common/settings');
const osVersion = require('./common/osVersion');
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
var appMenu = require('./main/menus/app');
const allowProtocolDialog = require('./main/allowProtocolDialog');

var argv = require('yargs').argv;

var client = null;
if (argv.livereload) {
  client = require('electron-connect').client.create();
  client.on('reload', function() {
    mainWindow.reload();
  });
}

if (argv['config-file']) {
  global['config-file'] = argv['config-file'];
}
else {
  global['config-file'] = app.getPath('userData') + '/config.json'
}

var config = {};
try {
  var configFile = global['config-file'];
  config = settings.readFileSync(configFile);
  if (config.version != settings.version) {
    config = settings.upgrade(config);
    settings.writeFileSync(configFile, config);
  }
}
catch (e) {
  config = settings.loadDefault();
  console.log('Failed to read or upgrade config.json');
}
ipcMain.on('update-config', () => {
  config = settings.readFileSync(configFile);
});

// Only for OS X
const switchMenuIconImages = function(icons, isDarkMode) {
  if (isDarkMode) {
    icons.normal = icons.clicked.normal;
    icons.unread = icons.clicked.unread;
    icons.mention = icons.clicked.mention;
  }
  else {
    icons.normal = icons.light.normal;
    icons.unread = icons.light.unread;
    icons.mention = icons.light.mention;
  }
};

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;
var trayIcon = null;
const trayImages = function() {
  switch (process.platform) {
    case 'win32':
      return {
        normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray.ico')),
        unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_unread.ico')),
        mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_mention.ico'))
      };
    case 'darwin':
      const icons = {
        light: {
          normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIcon.png')),
          unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIconUnread.png')),
          mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIconMention.png'))
        },
        clicked: {
          normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIcon.png')),
          unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIconUnread.png')),
          mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIconMention.png'))
        }
      };
      switchMenuIconImages(icons, systemPreferences.isDarkMode());
      return icons;
    case 'linux':
      var resourcesDir = 'resources/linux/' + (config.trayIconTheme || 'light') + '/';
      return {
        normal: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconTemplate.png')),
        unread: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconUnreadTemplate.png')),
        mention: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconMentionTemplate.png'))
      };
    default:
      return {};
  }
}();
var willAppQuit = false;

function shouldShowTrayIcon() {
  if (process.platform === 'win32') {
    return true;
  }
  if (['darwin', 'linux'].includes(process.platform) && config.showTrayIcon === true) {
    return true;
  }
  return false;
}

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// For win32, auto-hide menu bar.
app.on('browser-window-created', function(event, window) {
  if (process.platform === 'win32' || process.platform === 'linux') {
    if (config.hideMenuBar) {
      window.setAutoHideMenuBar(true);
      window.setMenuBarVisibility(false);
    }
  }
});

// For OSX, show hidden mainWindow when clicking dock icon.
app.on('activate', function(event) {
  mainWindow.show();
});

app.on('before-quit', function() {
  // Make sure tray icon gets removed if the user exits via CTRL-Q
  if (process.platform === 'win32') {
    trayIcon.destroy();
  }
  willAppQuit = true;
});

app.on('certificate-error', function(event, webContents, url, error, certificate, callback) {
  if (certificateStore.isTrusted(url, certificate)) {
    event.preventDefault();
    callback(true);
  }
  else {
    var detail = `URL: ${url}\nError: ${error}`;
    if (certificateStore.isExisting(url)) {
      detail = `Certificate is different from previous one.\n\n` + detail;
    }

    dialog.showMessageBox(mainWindow, {
      title: 'Certificate error',
      message: `Do you trust certificate from "${certificate.issuerName}"?`,
      detail: detail,
      type: 'warning',
      buttons: [
        'Yes',
        'No'
      ],
      cancelId: 1
    }, function(response) {
      if (response === 0) {
        certificateStore.add(url, certificate);
        certificateStore.save();
        webContents.loadURL(url);
      }
    });
    callback(false);
  }
});

const loginCallbackMap = new Map();

ipcMain.on('login-credentials', function(event, request, user, password) {
  const callback = loginCallbackMap.get(JSON.stringify(request));
  if (callback != null) {
    callback(user, password);
  }
})

app.on('login', function(event, webContents, request, authInfo, callback) {
  event.preventDefault();
  loginCallbackMap.set(JSON.stringify(request), callback);
  mainWindow.webContents.send('login-request', request, authInfo);
});

allowProtocolDialog.init(mainWindow);

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  ipcMain.on('notified', function(event, arg) {
    if (process.platform === 'win32' || process.platform === 'linux') {
      if (config.notifications.flashWindow === 2) {
        mainWindow.flashFrame(true);
      }
    }
  });
  if (shouldShowTrayIcon()) {
    // set up tray icon
    trayIcon = new Tray(trayImages.normal);
    if (process.platform === 'darwin') {
      trayIcon.setPressedImage(trayImages.clicked.normal);
      systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', (event, userInfo) => {
        switchMenuIconImages(trayImages, systemPreferences.isDarkMode());
        trayIcon.setImage(trayImages.normal);
      });
    }

    trayIcon.setToolTip(app.getName());
    trayIcon.on('click', function() {
      if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
        mainWindow.show();
        mainWindow.focus();
        if (process.platform === 'darwin') {
          app.dock.show();
        }
      }
      else if ((process.platform === 'win32') && config.toggleWindowOnTrayIconClick) {
        mainWindow.minimize();
      }
      else {
        mainWindow.focus();
      }
    });

    trayIcon.on('right-click', () => {
      trayIcon.popUpContextMenu();
    });
    trayIcon.on('balloon-click', function() {
      if (process.platform === 'win32' || process.platform === 'darwin') {
        mainWindow.show();
      }

      if (process.platform === 'darwin') {
        app.dock.show();
      }

      mainWindow.focus();
    });
    ipcMain.on('notified', function(event, arg) {
      if (process.platform === 'win32') {
        // On Windows 8.1 and Windows 8, a shortcut with a Application User Model ID must be installed to the Start screen.
        // In current version, use tray balloon for notification
        if (osVersion.isLowerThanOrEqualWindows8_1()) {
          trayIcon.displayBalloon({
            icon: path.resolve(__dirname, 'resources/appicon.png'),
            title: arg.title,
            content: arg.options.body
          });
        }
      }
    });

    // Set overlay icon from dataURL
    // Set trayicon to show "dot"
    ipcMain.on('update-unread', function(event, arg) {
      if (process.platform === 'win32') {
        const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
        mainWindow.setOverlayIcon(overlay, arg.description);
      }

      if (arg.mentionCount > 0) {
        trayIcon.setImage(trayImages.mention);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.mention);
        }
        trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
      }
      else if (arg.unreadCount > 0) {
        trayIcon.setImage(trayImages.unread);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.unread);
        }
        trayIcon.setToolTip(arg.unreadCount + ' unread channels');
      }
      else {
        trayIcon.setImage(trayImages.normal);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.normal);
        }
        trayIcon.setToolTip(app.getName());
      }
    });
  }

  // Create the browser window.
  var bounds_info_path = path.resolve(app.getPath("userData"), "bounds-info.json");
  var window_options;
  try {
    window_options = JSON.parse(fs.readFileSync(bounds_info_path, 'utf-8'));
  }
  catch (e) {
    // follow Electron's defaults
    window_options = {};
  }
  if (process.platform === 'win32' || process.platform === 'linux') {
    // On HiDPI(125%) Windows environment, the taskbar icon is pixelated. So this line is necessary. See #192.
    // As the side effect, #98 reoccurs.
    window_options.icon = path.resolve(__dirname, 'resources/appicon.png');
  }
  window_options.title = app.getName();
  mainWindow = new BrowserWindow(window_options);

  mainWindow.webContents.on('crashed', () => {
    console.log('The application has crashed.');
  });

  mainWindow.on('unresponsive', () => {
    console.log('The application has become unresponsive.')
  });

  mainWindow.setFullScreenable(true); // fullscreenable option has no effect.
  if (window_options.maximized) {
    mainWindow.maximize();
  }
  if (window_options.fullscreen) {
    mainWindow.setFullScreen(true);
  }

  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/browser/index.html');

  // Set application menu
  ipcMain.on('update-menu', (event, config) => {
    var app_menu = appMenu.createMenu(mainWindow, config);
    Menu.setApplicationMenu(app_menu);
    // set up context menu for tray icon
    if (shouldShowTrayIcon()) {
      const tray_menu = require('./main/menus/tray').createMenu(mainWindow, config);
      trayIcon.setContextMenu(tray_menu);
      if (process.platform === 'darwin') {
        // store the information, if the tray was initialized, for checking in the settings, if the application
        // was restarted after setting "Show icon on menu bar"
        if (trayIcon)
          mainWindow.trayWasVisible = true;
        else
          mainWindow.trayWasVisible = false;
      }
    }
  });
  ipcMain.emit('update-menu', true, config);

  // Open the DevTools.
  // mainWindow.openDevTools();

  var saveWindowState = function(file, window) {
    var window_state = window.getBounds();
    window_state.maximized = window.isMaximized();
    window_state.fullscreen = window.isFullScreen();
    try {
      fs.writeFileSync(bounds_info_path, JSON.stringify(window_state));
    }
    catch (e) {
      // [Linux] error happens only when the window state is changed before the config dir is creatied.
    }
  };

  mainWindow.on('close', function(event) {
    if (willAppQuit) { // when [Ctrl|Cmd]+Q
      saveWindowState(bounds_info_path, mainWindow);
    }
    else { // Minimize or hide the window for close button.
      event.preventDefault();
      switch (process.platform) {
        case 'win32':
          mainWindow.hide();
          break;
        case 'linux':
          if (config.minimizeToTray) {
            mainWindow.hide();
          }
          else {
            mainWindow.minimize();
          }
          break;
        case 'darwin':
          mainWindow.hide();
          if (config.minimizeToTray) {
            app.dock.hide();
          }
          break;
        default:
      }
    }
  });

  // App should save bounds when a window is closed.
  // However, 'close' is not fired in some situations(shutdown, ctrl+c)
  // because main process is killed in such situations.
  // 'blur' event was effective in order to avoid this.
  // Ideally, app should detect that OS is shutting down.
  mainWindow.on('blur', function() {
    saveWindowState(bounds_info_path, mainWindow);
  });

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  // Deny drag&drop navigation in mainWindow.
  // Drag&drop is allowed in webview of index.html.
  mainWindow.webContents.on('will-navigate', function(event, url) {
    var dirname = __dirname;
    if (process.platform === 'win32') {
      dirname = '/' + dirname.replace(/\\/g, '/');
    }

    var index = url.indexOf('file://' + dirname);
    if (index !== 0) {
      event.preventDefault();
    }
  });
});
<commit_msg>Force single app instance on windows<commit_after>'use strict';

const {
  app,
  BrowserWindow,
  Menu,
  Tray,
  ipcMain,
  nativeImage,
  dialog,
  systemPreferences
} = require('electron');

process.on('uncaughtException', (error) => {
  console.error(error);
});

if (process.platform === 'win32') {
  var cmd = process.argv[1];
  if (cmd === '--squirrel-uninstall') {
    var AutoLaunch = require('auto-launch');
    var appLauncher = new AutoLaunch({
      name: 'Mattermost'
    });
    appLauncher.isEnabled().then(function(enabled) {
      if (enabled)
        appLauncher.disable();
    });
  }
}

require('electron-squirrel-startup');

const fs = require('fs');
const path = require('path');

var settings = require('./common/settings');
const osVersion = require('./common/osVersion');
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
var appMenu = require('./main/menus/app');
const allowProtocolDialog = require('./main/allowProtocolDialog');

var argv = require('yargs').argv;

var client = null;
if (argv.livereload) {
  client = require('electron-connect').client.create();
  client.on('reload', function() {
    mainWindow.reload();
  });
}

if (argv['config-file']) {
  global['config-file'] = argv['config-file'];
}
else {
  global['config-file'] = app.getPath('userData') + '/config.json'
}

var config = {};
try {
  var configFile = global['config-file'];
  config = settings.readFileSync(configFile);
  if (config.version != settings.version) {
    config = settings.upgrade(config);
    settings.writeFileSync(configFile, config);
  }
}
catch (e) {
  config = settings.loadDefault();
  console.log('Failed to read or upgrade config.json');
}
ipcMain.on('update-config', () => {
  config = settings.readFileSync(configFile);
});

// Only for OS X
const switchMenuIconImages = function(icons, isDarkMode) {
  if (isDarkMode) {
    icons.normal = icons.clicked.normal;
    icons.unread = icons.clicked.unread;
    icons.mention = icons.clicked.mention;
  }
  else {
    icons.normal = icons.light.normal;
    icons.unread = icons.light.unread;
    icons.mention = icons.light.mention;
  }
};

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
var mainWindow = null;
var trayIcon = null;
const trayImages = function() {
  switch (process.platform) {
    case 'win32':
      return {
        normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray.ico')),
        unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_unread.ico')),
        mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/windows/tray_mention.ico'))
      };
    case 'darwin':
      const icons = {
        light: {
          normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIcon.png')),
          unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIconUnread.png')),
          mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/MenuIconMention.png'))
        },
        clicked: {
          normal: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIcon.png')),
          unread: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIconUnread.png')),
          mention: nativeImage.createFromPath(path.resolve(__dirname, 'resources/osx/ClickedMenuIconMention.png'))
        }
      };
      switchMenuIconImages(icons, systemPreferences.isDarkMode());
      return icons;
    case 'linux':
      var resourcesDir = 'resources/linux/' + (config.trayIconTheme || 'light') + '/';
      return {
        normal: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconTemplate.png')),
        unread: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconUnreadTemplate.png')),
        mention: nativeImage.createFromPath(path.resolve(__dirname, resourcesDir + 'MenuIconMentionTemplate.png'))
      };
    default:
      return {};
  }
}();
var willAppQuit = false;

// If there is already an instance, activate the window in the existing instace and quit this one
if(app.makeSingleInstance((commandLine, workingDirectory) => {
  // Someone tried to run a second instance, we should focus our window.
  if (mainWindow) {
    mainWindow.show()
  }
})) {
  app.quit()
}

function shouldShowTrayIcon() {
  if (process.platform === 'win32') {
    return true;
  }
  if (['darwin', 'linux'].includes(process.platform) && config.showTrayIcon === true) {
    return true;
  }
  return false;
}

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// For win32, auto-hide menu bar.
app.on('browser-window-created', function(event, window) {
  if (process.platform === 'win32' || process.platform === 'linux') {
    if (config.hideMenuBar) {
      window.setAutoHideMenuBar(true);
      window.setMenuBarVisibility(false);
    }
  }
});

// For OSX, show hidden mainWindow when clicking dock icon.
app.on('activate', function(event) {
  mainWindow.show();
});

app.on('before-quit', function() {
  // Make sure tray icon gets removed if the user exits via CTRL-Q
  if (process.platform === 'win32') {
    trayIcon.destroy();
  }
  willAppQuit = true;
});

app.on('certificate-error', function(event, webContents, url, error, certificate, callback) {
  if (certificateStore.isTrusted(url, certificate)) {
    event.preventDefault();
    callback(true);
  }
  else {
    var detail = `URL: ${url}\nError: ${error}`;
    if (certificateStore.isExisting(url)) {
      detail = `Certificate is different from previous one.\n\n` + detail;
    }

    dialog.showMessageBox(mainWindow, {
      title: 'Certificate error',
      message: `Do you trust certificate from "${certificate.issuerName}"?`,
      detail: detail,
      type: 'warning',
      buttons: [
        'Yes',
        'No'
      ],
      cancelId: 1
    }, function(response) {
      if (response === 0) {
        certificateStore.add(url, certificate);
        certificateStore.save();
        webContents.loadURL(url);
      }
    });
    callback(false);
  }
});

const loginCallbackMap = new Map();

ipcMain.on('login-credentials', function(event, request, user, password) {
  const callback = loginCallbackMap.get(JSON.stringify(request));
  if (callback != null) {
    callback(user, password);
  }
})

app.on('login', function(event, webContents, request, authInfo, callback) {
  event.preventDefault();
  loginCallbackMap.set(JSON.stringify(request), callback);
  mainWindow.webContents.send('login-request', request, authInfo);
});

allowProtocolDialog.init(mainWindow);

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  ipcMain.on('notified', function(event, arg) {
    if (process.platform === 'win32' || process.platform === 'linux') {
      if (config.notifications.flashWindow === 2) {
        mainWindow.flashFrame(true);
      }
    }
  });
  if (shouldShowTrayIcon()) {
    // set up tray icon
    trayIcon = new Tray(trayImages.normal);
    if (process.platform === 'darwin') {
      trayIcon.setPressedImage(trayImages.clicked.normal);
      systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', (event, userInfo) => {
        switchMenuIconImages(trayImages, systemPreferences.isDarkMode());
        trayIcon.setImage(trayImages.normal);
      });
    }

    trayIcon.setToolTip(app.getName());
    trayIcon.on('click', function() {
      if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
        mainWindow.show();
        mainWindow.focus();
        if (process.platform === 'darwin') {
          app.dock.show();
        }
      }
      else if ((process.platform === 'win32') && config.toggleWindowOnTrayIconClick) {
        mainWindow.minimize();
      }
      else {
        mainWindow.focus();
      }
    });

    trayIcon.on('right-click', () => {
      trayIcon.popUpContextMenu();
    });
    trayIcon.on('balloon-click', function() {
      if (process.platform === 'win32' || process.platform === 'darwin') {
        mainWindow.show();
      }

      if (process.platform === 'darwin') {
        app.dock.show();
      }

      mainWindow.focus();
    });
    ipcMain.on('notified', function(event, arg) {
      if (process.platform === 'win32') {
        // On Windows 8.1 and Windows 8, a shortcut with a Application User Model ID must be installed to the Start screen.
        // In current version, use tray balloon for notification
        if (osVersion.isLowerThanOrEqualWindows8_1()) {
          trayIcon.displayBalloon({
            icon: path.resolve(__dirname, 'resources/appicon.png'),
            title: arg.title,
            content: arg.options.body
          });
        }
      }
    });

    // Set overlay icon from dataURL
    // Set trayicon to show "dot"
    ipcMain.on('update-unread', function(event, arg) {
      if (process.platform === 'win32') {
        const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
        mainWindow.setOverlayIcon(overlay, arg.description);
      }

      if (arg.mentionCount > 0) {
        trayIcon.setImage(trayImages.mention);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.mention);
        }
        trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
      }
      else if (arg.unreadCount > 0) {
        trayIcon.setImage(trayImages.unread);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.unread);
        }
        trayIcon.setToolTip(arg.unreadCount + ' unread channels');
      }
      else {
        trayIcon.setImage(trayImages.normal);
        if (process.platform === 'darwin') {
          trayIcon.setPressedImage(trayImages.clicked.normal);
        }
        trayIcon.setToolTip(app.getName());
      }
    });
  }

  // Create the browser window.
  var bounds_info_path = path.resolve(app.getPath("userData"), "bounds-info.json");
  var window_options;
  try {
    window_options = JSON.parse(fs.readFileSync(bounds_info_path, 'utf-8'));
  }
  catch (e) {
    // follow Electron's defaults
    window_options = {};
  }
  if (process.platform === 'win32' || process.platform === 'linux') {
    // On HiDPI(125%) Windows environment, the taskbar icon is pixelated. So this line is necessary. See #192.
    // As the side effect, #98 reoccurs.
    window_options.icon = path.resolve(__dirname, 'resources/appicon.png');
  }
  window_options.title = app.getName();
  mainWindow = new BrowserWindow(window_options);

  mainWindow.webContents.on('crashed', () => {
    console.log('The application has crashed.');
  });

  mainWindow.on('unresponsive', () => {
    console.log('The application has become unresponsive.')
  });

  mainWindow.setFullScreenable(true); // fullscreenable option has no effect.
  if (window_options.maximized) {
    mainWindow.maximize();
  }
  if (window_options.fullscreen) {
    mainWindow.setFullScreen(true);
  }

  // and load the index.html of the app.
  mainWindow.loadURL('file://' + __dirname + '/browser/index.html');

  // Set application menu
  ipcMain.on('update-menu', (event, config) => {
    var app_menu = appMenu.createMenu(mainWindow, config);
    Menu.setApplicationMenu(app_menu);
    // set up context menu for tray icon
    if (shouldShowTrayIcon()) {
      const tray_menu = require('./main/menus/tray').createMenu(mainWindow, config);
      trayIcon.setContextMenu(tray_menu);
      if (process.platform === 'darwin') {
        // store the information, if the tray was initialized, for checking in the settings, if the application
        // was restarted after setting "Show icon on menu bar"
        if (trayIcon)
          mainWindow.trayWasVisible = true;
        else
          mainWindow.trayWasVisible = false;
      }
    }
  });
  ipcMain.emit('update-menu', true, config);

  // Open the DevTools.
  // mainWindow.openDevTools();

  var saveWindowState = function(file, window) {
    var window_state = window.getBounds();
    window_state.maximized = window.isMaximized();
    window_state.fullscreen = window.isFullScreen();
    try {
      fs.writeFileSync(bounds_info_path, JSON.stringify(window_state));
    }
    catch (e) {
      // [Linux] error happens only when the window state is changed before the config dir is creatied.
    }
  };

  mainWindow.on('close', function(event) {
    if (willAppQuit) { // when [Ctrl|Cmd]+Q
      saveWindowState(bounds_info_path, mainWindow);
    }
    else { // Minimize or hide the window for close button.
      event.preventDefault();
      switch (process.platform) {
        case 'win32':
          mainWindow.hide();
          break;
        case 'linux':
          if (config.minimizeToTray) {
            mainWindow.hide();
          }
          else {
            mainWindow.minimize();
          }
          break;
        case 'darwin':
          mainWindow.hide();
          if (config.minimizeToTray) {
            app.dock.hide();
          }
          break;
        default:
      }
    }
  });

  // App should save bounds when a window is closed.
  // However, 'close' is not fired in some situations(shutdown, ctrl+c)
  // because main process is killed in such situations.
  // 'blur' event was effective in order to avoid this.
  // Ideally, app should detect that OS is shutting down.
  mainWindow.on('blur', function() {
    saveWindowState(bounds_info_path, mainWindow);
  });

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });

  // Deny drag&drop navigation in mainWindow.
  // Drag&drop is allowed in webview of index.html.
  mainWindow.webContents.on('will-navigate', function(event, url) {
    var dirname = __dirname;
    if (process.platform === 'win32') {
      dirname = '/' + dirname.replace(/\\/g, '/');
    }

    var index = url.indexOf('file://' + dirname);
    if (index !== 0) {
      event.preventDefault();
    }
  });
});
<|endoftext|>


<commit_before>import Api from './api';
import auth from './api.auth';
import search from './api.search';

export default function (http) {
  return new Api(http)
    .mixin(auth)
    .mixin(search);
}<commit_msg>Removed main.js<commit_after><|endoftext|>


<commit_before>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-default/index.css';
import 'assets/custom-theme/index.css'; // https://github.com/PanJiaChen/custom-element-theme
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import 'normalize.css/normalize.css';
import 'styles/index.scss';
import 'components/Icon-svg/index';
import 'assets/iconfont/iconfont';
import * as filters from './filters';
import Multiselect from 'vue-multiselect';
import Sticky from 'components/Sticky';
import 'vue-multiselect/dist/vue-multiselect.min.css';
import vueWaves from './directive/waves';
import vueSticky from './directive/sticky';
import errLog from 'store/errLog';
import './mock/index.js';
// import './styles/mixin.scss';

// register globally
Vue.component('multiselect', Multiselect);
Vue.component('Sticky', Sticky);
Vue.use(ElementUI);
Vue.use(vueWaves);
Vue.use(vueSticky);

// register global utility filters.
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
});

function hasPermission(roles, permissionRoles) {
  if (roles.indexOf('admin') >= 0) return true;
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
// register global progress.
const whiteList = ['/login', '/authredirect', '/reset', '/sendpwd'];// 不重定向白名单
router.beforeEach((to, from, next) => {
  NProgress.start();
  if (store.getters.token) {
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
      if (to.meta && to.meta.role) {
        if (hasPermission(store.getters.roles, to.meta.role)) {
          next();
        } else {
          next('/401');
        }
      } else {
        next();
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
});

router.afterEach(() => {
  NProgress.done();
});

// window.onunhandledrejection = e => {
//     console.log('unhandled', e.reason, e.promise);
//     e.preventDefault()
// };

// 生产环境错误日志
if (process.env === 'production') {
  Vue.config.errorHandler = function(err, vm) {
    console.log(err, window.location.href);
    errLog.pushLog({
      err,
      url: window.location.href,
      vm
    })
  };
}

// window.onerror = function (msg, url, lineNo, columnNo, error) {
//     console.log('window')
// };
//
// console.error = (function (origin) {
//     return function (errorlog) {
//         // handler();//基于业务的日志记录及数据报错
//         console.log('console'+errorlog)
//         origin.call(console, errorlog);
//     }
// })(console.error);

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app');

<commit_msg>Update main.js<commit_after>// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue';
import App from './App';
import router from './router';
import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-default/index.css';
import 'assets/custom-theme/index.css'; // https://github.com/PanJiaChen/custom-element-theme
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import 'normalize.css/normalize.css';
import 'styles/index.scss';
import 'components/Icon-svg/index';
import 'assets/iconfont/iconfont';
import * as filters from './filters';
import Multiselect from 'vue-multiselect';
import Sticky from 'components/Sticky';
import 'vue-multiselect/dist/vue-multiselect.min.css';
import vueWaves from './directive/waves';
import vueSticky from './directive/sticky';
import errLog from 'store/errLog';
import './mock/index.js';  //使用api请求时请将此行注释，不然将被mock拦截
// import './styles/mixin.scss';

// register globally
Vue.component('multiselect', Multiselect);
Vue.component('Sticky', Sticky);
Vue.use(ElementUI);
Vue.use(vueWaves);
Vue.use(vueSticky);

// register global utility filters.
Object.keys(filters).forEach(key => {
  Vue.filter(key, filters[key])
});

function hasPermission(roles, permissionRoles) {
  if (roles.indexOf('admin') >= 0) return true;
  return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
// register global progress.
const whiteList = ['/login', '/authredirect', '/reset', '/sendpwd'];// 不重定向白名单
router.beforeEach((to, from, next) => {
  NProgress.start();
  if (store.getters.token) {
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
      if (to.meta && to.meta.role) {
        if (hasPermission(store.getters.roles, to.meta.role)) {
          next();
        } else {
          next('/401');
        }
      } else {
        next();
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
});

router.afterEach(() => {
  NProgress.done();
});

// window.onunhandledrejection = e => {
//     console.log('unhandled', e.reason, e.promise);
//     e.preventDefault()
// };

// 生产环境错误日志
if (process.env === 'production') {
  Vue.config.errorHandler = function(err, vm) {
    console.log(err, window.location.href);
    errLog.pushLog({
      err,
      url: window.location.href,
      vm
    })
  };
}

// window.onerror = function (msg, url, lineNo, columnNo, error) {
//     console.log('window')
// };
//
// console.error = (function (origin) {
//     return function (errorlog) {
//         // handler();//基于业务的日志记录及数据报错
//         console.log('console'+errorlog)
//         origin.call(console, errorlog);
//     }
// })(console.error);

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app');

<|endoftext|>